Compare commits

...

1184 Commits

Author SHA1 Message Date
Miroslav Lichvar
8b50a8298a doc: update NEWS 2020-03-16 13:16:14 +01:00
Bryan Christianson
3eab329042 sys_macosx: don't require clock_gettime()
Earlier versions of macOS do not provide clock_gettime(). This patch
checks for clock_gettime() at run-time and falls back to gettimeofday()
if the symbol is not present.
2020-03-16 11:35:56 +01:00
Miroslav Lichvar
552d3b53b1 main: accept zero timeout
Allow -t to specify zero timeout to exit immediately. It might be
useful for testing.
2020-03-12 14:25:21 +01:00
Miroslav Lichvar
8afd62d954 reference: update synchronization status more frequently
Update the local clock errors with each update of the leap status to
avoid the kernel marking the clock as unsynchronized when a large
number of NTP samples is dropped.
2020-03-12 14:07:12 +01:00
Miroslav Lichvar
4883086fc1 sources: update reference leap status early
When a leap second status is updated by a source, don't wait for the
next source selection and full update of the reference. Count votes from
sources that passed the previous selection and update the reference leap
status directly.

This should allow leap seconds to spread quickly even when the
samples are dropped or delayed by the filters.
2020-03-12 14:07:12 +01:00
Miroslav Lichvar
2582be8754 sources: separate update of leap status
Remove leap status from the NTP sample and set it independently from
the sample accumulation in order to accept a leap second sooner when
samples are filtered.
2020-03-12 14:07:12 +01:00
Miroslav Lichvar
ff9301567e sourcestats: move leap status to sources 2020-03-12 12:09:50 +01:00
Miroslav Lichvar
e7a254265f cmdmon: add reset command
The reset command drops all measurements and switches the reference 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).
2020-03-12 12:09:50 +01:00
Miroslav Lichvar
d5311adafb doc: fix tag in chronyc man page 2020-03-12 12:09:50 +01:00
Miroslav Lichvar
18d7ea62b3 reference: don't report synchronized status after unknown step
The source handler resets SST instances on an unknown step, which
makes the sources unselectable, but SRC_SelectSource() doesn't call
REF_SetUnsynchronised() when no source is selectable.

Handle the step in the reference handler.

Fixes: 049eae661a ("sources: keep synchronized status with unreachable/unselectable sources")
2020-03-12 12:09:50 +01:00
Miroslav Lichvar
fb2849b230 reference: convert to monotonic time
Calculate the update interval and drift file age from increments in the
monotonic time instead of real time.
2020-03-12 12:09:50 +01:00
Miroslav Lichvar
fd59877692 nts: convert to monotonic time
Use the monotonic timestamp provided by the scheduler for NTS-KE rate
limiting and refresh.
2020-03-12 12:09:50 +01:00
Miroslav Lichvar
bb0553e4c4 sched: provide low-precision monotonic time
Measure the interval since the start in order to provide a monotonic
time for periodical tasks not using timers like driftfile updates, key
refresh, etc. Return the interval in the double format, but keep an
integer remainder limiting the precision to 0.01 second to avoid issues
with very small increments in a long-running process.
2020-03-12 12:09:49 +01:00
Miroslav Lichvar
46f954490d configure: improve nettle and gnutls check
Before enabling NTS, check for more gnutls functions (some added in
3.6.3) to avoid build failures with older gnutls versions. Also, make
sure that nettle supports the new AES interface (added in 3.0).
2020-03-10 15:52:28 +01:00
Miroslav Lichvar
31e6a50386 doc: add missing dependencies to installation document 2020-03-10 11:16:23 +01:00
Miroslav Lichvar
9a9c0d7b99 configure: improve pkg-config support 2020-03-10 11:16:23 +01:00
Miroslav Lichvar
0c80f00d0b doc: update description of on/offline commands 2020-03-09 13:05:02 +01:00
Miroslav Lichvar
27b3bf48ea ntp: ignore onoffline command for unresolved sources
The onoffline command switches an unresolved source to the offline
status, even when the network is already up.

Ignore the onoffline command for unresolved sources to prevent sources
unexpectedly staying in the offline status, e.g. when the command is
issued from a network dispatcher script (and no other call is expected
later when the name is resolved).
2020-03-09 13:02:23 +01:00
Miroslav Lichvar
c3e34b8145 doc: update installation document 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
7bf3ec4aeb doc: describe NTS directives and options 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
50204a125b test: add nts unit tests 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
111d170542 test: update compilation tests 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
d6dd6f0bc9 test: add 139-nts test 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
44aac84feb cmdmon: add NTS support
Allow the nts and ntsport options to be specified for sources added from
chronyc. This is an incompatible change in the request, but there was no
release using the new REQ_ADD_SOURCE command yet.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
c41508723f ntp: enable NTS support
Add an option to enable NTS for an NTP source. Check for NTS-specific
extension fields and pass the packets to the NTS-NTP code in order to
enable the NTS client and server.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
6043632f80 nts: add NTS-NTP server and client
Add support for the NTS NTP extension fields.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
a420ed57a1 nts: add NTS-KE server and client
Add a client and server implementing the Network Time Security (NTS) Key
Establishment. Use the GnuTLS library for TLS.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
72f99033fe test: add siv unit test 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
2d798bc4cf siv: add internal implementation based on Nettle
This adds an internal implementation of the AES-SIV-CMAC-256 cipher
based on GNU Nettle and the following patch (which was later reworked
and included in Nettle):

https://gitlab.com/gnutls/gnutls/uploads/1ab02c51e317264f9dba07ddfbc01d9a/0001-Added-support-for-AES_SIV_CMAC_256-and-AES_SIV_CMAC_.patch

This implementation will be dropped when the cipher is widely supported
by gnutls or Nettle.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
881d07fa0a siv: add support for Nettle 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
c5306bed39 siv: introduce API for SIV
Add a header file for Synthetic Initialization Vector (SIV) cipher mode,
which will be used by NTS.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
934b8712a5 sys_linux: allow getuid() in seccomp filter
This will be needed by gnutls when loading certificates.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
1d4690eb64 sys_linux: add syscall filter context for NTS-KE
The NTS-KE helper process will use a more restrictive filter than the
main process.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
e6848b1e3f sys: specify context for syscall filter
Specify a context to enable different processes using different (more
restrictive) syscall filters.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
3e537416a9 sched: remove slew handler in finalization
This allows repeated calls of SCH_Initialise() and SCH_Finalise().
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
26a1ed8bc3 sched: add function to remove all timers and descriptors
This allows a helper process to be started in an *_Initialise() call
and use the scheduler (unlike the privops helper, which has its own
loop).
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
24538fe3e9 nameserv: allow concurrent asynchronous requests
Allow multiple resolving threads to be running at the same time in order
to support multiple callers, but use a mutex to avoid sending multiple
requests to the privops helper. This will be needed for the NTS-KE
server negotiation.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
e43d699973 util: add functions for printing and parsing hexadecimal data 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
1e727c4497 sources: don't reset active status
Avoid resetting the active status when an NTP source changes its
address in NCR_ChangeRemoteAddress().

This will allow an NTP source to update its address with NTS-KE
hostname negotiation and continue in a special reference mode
(e.g. -q/-Q option).
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
83010590af ntp: move definition of invalid stratum to ntp.h 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
fa402a173a ntp: pass server name to ntp_core instances
The server name will be needed for certificate verification in NTS-KE.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
ca83d2a804 test: add ntp_ext unit test 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
725beb360a ntp: add functions for adding extension fields 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
86d29221f3 ntp: add function to change authentication-specific address
When an NTS source will be replaced, the authentication-specific address
of the NTS-KE server will need to be changed too.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
e8062b7ff1 ntp: add function to update source NTP address
This will allow a source to have its address changed due to NTS-KE
server negotiation, which allows the NTS-KE server to have a different
address than the NTP server.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
9468fd4aa6 ntp: allow changing port of source
Modify the replace_source() function to not require a different IP
address when replacing a source with the same address but different
port. This will enable the NTS-KE port negotiation.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
5ed9b888ff ntp: don't accept packets with unexpected authentication
If authentication is not enabled in configuration, responses are not
expected to be authenticated. Handle such responses as having failed
authentication.

A case where this could happen is a misconfigured symmetric association
where only one peer has specified the other with a key. Before this
change synchronization would work in one direction and used packets
with an asymmetric length.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
7661a6e95b ntp: don't allow long MACs in NTPv4 packets with extension fields
MAC longer than 24 octets in NTPv4 packet is supported only for
compatibility with some pre-RFC7822 chrony versions. They didn't use
any extension fields.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
aca1daf7c9 ntp: add support for sending KoD responses
Enable the server to respond with a KoD when authentication fails. This
will be used by NTS to respond with a NAK when a client has expired
cookies.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
46cac4e22f ntp: prefix NTP_AuthMode enums 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
56a102ed4d ntp: move auth parsing to ntp_auth
Move the remaining authentication-specific code to the new file.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
ca28dbd2c3 ntp: refactor authentication
Move most of the authentication-specific code to a new file and
introduce authenticator instances in order to support other
authentication mechanisms (e.g. NTS).
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
588785e160 ntp: rework packet parsing
Rework the code to detect the authentication mode and count extension
fields in the first parsing of the packet and store this information in
the new packet info structure.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
cabcccd6c3 ntp: add functions for parsing extension fields 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
567e66a0bb ntp: count packets with invalid format
Include packets that cannot be parsed in the total RX count.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
b8ee6d6e56 ntp: don't send response longer than request
When sending a response in the server or passive mode, make sure the
response is not longer than the request to prevent amplification
attacks when resposes may contain extension fields (e.g. NTS).
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
9ea1e4e40f ntp: provide access to request in transmit_packet()
This will allow new authentication code (e.g. NTS) to get data from the
request when generating a response.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
2d492eacb5 ntp: rename receive_packet() to process_response() 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
cb8660e79a ntp: add structure with packet info
Add a structure for length and other information about received and
transmitted NTP packets to minimize the number of parameters and avoid
repeated parsing of the packet.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
d29bef93e9 ntp: refactor NTP_Packet structure for extension fields 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
5a09adebfd ntp: don't replace sources with unroutable addresses
When changing an address of a source (both known and unknown), make sure
the new address is connectable. This should avoid useless replacements,
e.g. polling an IPv6 address on IPv4-only systems.
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
8c0ee9c175 doc: list unsupported options in peer directive 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
f20fabdbf4 test: make 132-logchange more reliable 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
57cea56e6e test: extend 001-features test 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
db7d9639b4 test: fix unit tests to build with -NTP and -CMDMON 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
beb40d63ed test: extend 122-xleave test 2020-03-05 16:02:15 +01:00
Miroslav Lichvar
672b98dd3f sources: don't save or load dumpfiles for unknown addresses
Don't open a dumpfile for reading or writing if the NTP source doesn't
have a real address.

Fixes: d7e3ad17ff ("ntp: create sources for unresolved addresses")
2020-03-05 16:02:15 +01:00
Miroslav Lichvar
a24d2713cd client: add option to print all sources
Add -a option to the sources and sourcestats commands to print all
sources, including those that don't have a resolved address yet. By
default, only sources that have a real address are printed for
compatibility. Remove the "210 Number of sources" messages to avoid
confusion. Also, modify the ntpdata command to always print only sources
with a resolved address.
2020-02-19 15:03:20 +01:00
Miroslav Lichvar
a5110d3ed9 client: add support for source identifiers
Allow the new identifiers to be specified as addresses in commands that
modify or remove NTP sources.
2020-02-19 15:03:20 +01:00
Miroslav Lichvar
9d1c1505b9 ntp: repeat resolving until all pool sources are resolved
When resolving of a pool name succeeds, don't remove the remaining
unresolved sources, i.e. try to get all maxsources (default 4) sources,
even if it takes multiple DNS requests.
2020-02-19 15:03:20 +01:00
Miroslav Lichvar
8c25632321 ntp: stop resolving if unresolved source is removed
If an individual unresolved source or all unresolved sources from a pool
are removed, stop resolving their addresses on the next attempt (don't
remove them immediately as the resolver may be running).
2020-02-19 15:03:20 +01:00
Miroslav Lichvar
2507b66640 ntp: update resolving timeout ID
This will allow unresolved sources to be removed before resolving.
2020-02-19 15:03:20 +01:00
Miroslav Lichvar
d7e3ad17ff ntp: create sources for unresolved addresses
Rework the ntp_sources code to create sources for addresses that are not
resolved yet using the new identifiers.
2020-02-19 14:52:03 +01:00
Miroslav Lichvar
84902d0e00 addressing: introduce identifiers for unresolved addresses
Add a new type of address for NTP sources that don't have a resolved
address yet. This will allow the sources to be displayed, modified and
deleted by chronyc.

Update utility functions to support the new addresses.
2020-02-19 11:37:15 +01:00
Miroslav Lichvar
f7f3667bcb addrfilt: explicitly handle unexpected addresses 2020-02-18 16:01:25 +01:00
Miroslav Lichvar
794cbfbbb5 logging: restrict file log permissions
With the new file utility functions permissions can be restricted for
newly created files. For the log file specified by the -l option it
is better to remove the "other" permissions (0640) to make it similar
to the system log.
2020-02-18 16:01:25 +01:00
Miroslav Lichvar
1858104b5c util: don't log unlink() error if file is not accessible
Try stat() before calling unlink() to make sure the file is accessible.

This fixes chronyc running under a non-root/chrony user printing an
error message due to missing permissions on /var/run/chrony before
trying to bind its socket.
2020-02-18 16:01:25 +01:00
Vincent Blut
994409a036 sys_linux: allow renameat2 in seccomp filter
This is needed for architectures that support neither rename() nor
renameat() (e.g. riscv64)
2020-01-20 12:26:54 +01:00
Miroslav Lichvar
2d9eb5b6fa test: fix util unit test for NTP era split
The current default NTP era split passed the Unix epoch (~50 years ago),
which means the epoch converted to an NTP timestamp and back ends up in
the next NTP era (year 2106).

Fix the test to take into account the era split.
2020-01-03 12:01:55 +01:00
Miroslav Lichvar
3477cbe28f stubs: add NSR_GetName()
Fixes: 93f6358916 ("cmdmon: add request to get source name")
2020-01-03 11:47:53 +01:00
Miroslav Lichvar
8634158de0 stubs: update NSR_AddSourceByName()
Fixes: 3763befd62 ("ntp: check name and return status from NSR_AddSourceByName()")
2020-01-03 11:47:53 +01:00
Miroslav Lichvar
3eaf0765b0 client: add missing commands to tab-completion
Reported-by: Lonnie Abelbeck <lonnie@abelbeck.com>
2020-01-03 11:47:53 +01:00
Miroslav Lichvar
dd0679ad45 socket: add function to check supported family
Don't log error when an IPv6 socket cannot be opened and chronyd was
built without IPv6 support.

Reported-by: Lonnie Abelbeck <lonnie@abelbeck.com>
2020-01-03 11:47:41 +01:00
Miroslav Lichvar
bfcd8ecc56 client: add sourcename command
Add a new command to print the original name of a source specified by
address. This could be useful in scripts to avoid having to run the
sources command with and without -N.
2019-12-12 16:17:45 +01:00
Miroslav Lichvar
50e5865c73 client: add option to print original names
Add -N option to chronyc to print the original names by which the
sources were specified instead of using reverse DNS lookup. The option
works in the sources, sourcestats and tracking commands.
2019-12-12 16:17:44 +01:00
Miroslav Lichvar
93f6358916 cmdmon: add request to get source name
Specify a new request to get the name of the NTP source corresponding to
an address, which was originally specified in the configuration file or
chronyc add command.
2019-12-12 16:06:58 +01:00
Miroslav Lichvar
9300854439 cmdmon: add support for adding pool sources
Specify a new type for pool sources and extend the syntax of the chronyc
"add" command to add a pool.
2019-12-12 14:44:03 +01:00
Miroslav Lichvar
02914ac637 cmdmon: specify name instead of address in add request
Modify the request for adding a source to provide the name of the source
instead of its address (resolved in chronyc) in order to enable chronyd
to replace the source, support an "add pool" command, and enable an NTS
client to verify the server's certificate.

The name resolving does not block the response. Success is indicated
even if the name cannot be resolved, or a source with the same address
is already present.

To prevent unresolvable names from getting to chronyd, chronyc does not
send the request if it could not resolve the name itself (assuming they
are both running on the same host using the same resolver).
2019-12-12 14:44:03 +01:00
Miroslav Lichvar
00fff161cf cmdmon: merge add server/peer requests
Instead of having two separate requests in the protocol for adding a
server and peer, specify the type of the new source in the request data.
2019-12-12 14:44:03 +01:00
Miroslav Lichvar
3763befd62 ntp: check name and return status from NSR_AddSourceByName()
Return an error status when the name is not printable or contains a
space (don't bother with full hostname validation). If the name is an
address, return the same status as NSR_AddSource(). Otherwise, return a
"not resolved yet" status.
2019-12-12 14:44:03 +01:00
Miroslav Lichvar
2ae008bcee ntp: print name of replaced source in log message 2019-12-12 14:44:03 +01:00
Miroslav Lichvar
ea41f7ab09 doc: remove unsupported options of add commands
All options from the configuration file are supported in the chronyc add
commands.

This fixes commit 65fd30a547.
2019-12-12 14:43:55 +01:00
Miroslav Lichvar
c673b1e8b7 privops: convert to new socket API 2019-12-12 13:03:31 +01:00
Miroslav Lichvar
2bf1ba22f2 socket: change SCK_Send() declaration to const buffer 2019-12-12 13:03:31 +01:00
Miroslav Lichvar
dfc2f70876 socket: add support for opening socket pairs 2019-12-12 13:03:31 +01:00
Miroslav Lichvar
0dba2b9689 socket: add support for blocking sockets
Add a flag to open a blocking socket. The default stays non-blocking.
2019-12-12 13:03:31 +01:00
Miroslav Lichvar
e7fc2d31cc socket: remove unnecessary MSG_DONTWAIT flag
This is not needed since sockets are non-blocking by default.
2019-12-12 13:03:31 +01:00
Miroslav Lichvar
f231efb811 socket: add support for sending and receiving descriptors
Add support for the SCM_RIGHTS control message used in communication
with the privops helper.
2019-12-12 13:03:31 +01:00
Christian Ehrhardt
c4d6f98bed test: accept test result if RTC can't enable RTC_UIE_ON
The test might run on different platforms. If the platform happens
to have a RTC that does exist but unable to have RTC_UIE_ON set the
test will fail, while the chrony code is actually good.

Examples of bad clocks are:
- ppc64el: rtc-generic
- arm64: rtc-efi

To avoid that extend the log message check on 101-rtc to accept
that condition as a valid test result as well.

Signed-off-by: Christian Ehrhardt <christian.ehrhardt@canonical.com>
2019-12-12 13:03:26 +01:00
Christian Ehrhardt
bff3f51d13 rtc: extend check for RTCs that don't support interrupts
Several RTCs would only expose the broken behavior on enabling
interrupts. The reason for that is that the kernel only returns the
error if the state changes. Therefore the check has to probe
switch_interrupts(1) as well.

On platforms that work it will be switched on and off, while on those it
never works it will just stay off.

Clocks known to expose that behavior include, but are not limited to:
PPC64# dmesg | grep -i rtc   
[    0.241872] rtc-generic rtc-generic: registered as rtc0
[    0.270221] rtc-generic rtc-generic: setting system clock to ...
ARM64# dmesg | grep -i rtc
[    0.876198] rtc-efi rtc-efi: registered as rtc0
[    1.046869] rtc-efi rtc-efi: setting system clock to ...

Signed-off-by: Christian Ehrhardt <christian.ehrhardt@canonical.com>
2019-12-12 12:50:19 +01:00
Miroslav Lichvar
f5eb7daf20 rtc: disable interrupts in finalization
Don't leave interrupts enabled if chronyd is stopped when making an RTC
measurement.
2019-12-10 17:45:28 +01:00
Miroslav Lichvar
d66b2f2b24 rtc: handle RTCs that don't support interrupts
Some RTCs supported by the Linux kernel don't support the RTC_UIE_ON/OFF
ioctls, which causes chronyd started with the -s option to get stuck in
the initial RTC mode.

After opening the RTC device in the initialization, return error if
the ioctls are not supported to prevent the upper layer from calling the
time_init() function and expecting it to finish.
2019-12-10 17:45:28 +01:00
Miroslav Lichvar
a57e1eb542 rtc: don't finalize driver if initialization failed 2019-12-10 17:45:20 +01:00
Miroslav Lichvar
25bdee7a0e rtc: simplify and move switch_interrupts() 2019-12-10 17:03:15 +01:00
Miroslav Lichvar
f6001202ec test: update log checks in system tests
Measurements are no longer accepted and clock updated when polling
itself.

This fixes commit 7a88e0a87b.
2019-12-10 15:56:48 +01:00
Miroslav Lichvar
0cf506c929 sys_linux: allow clock_adjtime in seccomp filter
The adjtimex() function in glibc was switched to the clock_adjtime
system call.
2019-12-02 18:06:25 +01:00
Miroslav Lichvar
d05e9fb2ec logging: enable line buffering on stderr
This should avoid mixed lines on console or in file log when multiple
processes will be logging messages at the same time.
2019-12-02 18:06:15 +01:00
Vincent Blut
54d7e3e94a doc: fix typo in chrony.conf man page 2019-11-28 16:41:45 +01:00
Miroslav Lichvar
c7223f4c8f logging: disable all debug messages in non-debug build
For consistency, don't print debug messages that are compiled in due to
using the LOG macro instead of DEBUG_LOG.
2019-11-19 14:59:21 +01:00
Miroslav Lichvar
07badbede7 client: don't print log messages with lower severity 2019-11-19 14:43:01 +01:00
Miroslav Lichvar
468cfeeb71 privops: keep stdin/out/err open 2019-11-19 14:43:01 +01:00
Miroslav Lichvar
b3fc549622 privops: remove debug message from PRV_Name2IPAddress()
The function may be called from a separate thread, but logging is not
considered thread safe (e.g. due to using functions which read
environment variables).
2019-11-19 14:43:01 +01:00
Miroslav Lichvar
077dbd5692 main: don't try to open unspecified pidfile 2019-11-19 14:34:51 +01:00
Miroslav Lichvar
e18903a6b5 switch to new util file functions
Replace all fopen(), rename(), and unlink() calls with the new util
functions.
2019-10-24 12:48:45 +02:00
Miroslav Lichvar
7dfd4ae556 test: extend util unit test 2019-10-24 12:48:45 +02:00
Miroslav Lichvar
429c4468b0 sys_linux: allow F_GETFL in seccomp filter
This is needed for fdopen().
2019-10-24 12:48:45 +02:00
Miroslav Lichvar
7a4c396bba util: add functions for common file operations
Add a function to open a file for reading, writing, or appending.
In uppercase modes errors are handled as fatal, i.e. the caller doesn't
need to check for NULL. To avoid string manipulations in the callers,
the function accepts an optional directory and suffix. New files are
created with specified permissions, which will be needed for saving
keys. The O_EXCL flag is used in the writing mode to make sure a new
file is created (on filesystems that support it).

Also, add a function to rename a temporary file by changing its suffix,
and a function to remove a file.

All functions log all errors, at least as debug messages.
2019-10-24 12:48:45 +02:00
Miroslav Lichvar
88f846f656 rtc: don't clone file attributes of rtc file
When replacing an existing rtc file with the temporary file, don't
change the ownership or permissions of the temporary file to match the
old rtc file, as if it didn't exist.
2019-10-24 11:03:47 +02:00
Miroslav Lichvar
27c8a64977 reference: don't clone file attributes of drift file
When replacing an existing drift file with the temporary file, don't
change the ownership or permissions of the temporary file to match the
old drift file, as if it didn't exist.
2019-10-24 11:03:47 +02:00
Miroslav Lichvar
2fc8edacb8 use PATH_MAX
Include <limits.h> and use the PATH_MAX macro to define the length of
buffers containing paths to make it constistent. (It's not supposed to
fit all possible paths.)
2019-10-24 11:03:47 +02:00
Miroslav Lichvar
903fa247f8 logging: include <syslog.h>
Move the inclusion of <syslog.h> from sysincl.h to logging.c to avoid
accidentally using the LOG_* constants from the header.
2019-10-24 11:03:47 +02:00
Miroslav Lichvar
96771d6857 logging: make banner printing safer
Don't rely on the buffer filled with '=' characters to be always at
least as long as the log-specific banner string.
2019-10-24 11:03:41 +02:00
Miroslav Lichvar
f4c6a00b2a logging: call exit() in LOG_Message()
Call exit() in LOG_Message() after printing a fatal message to allow the
LOG macro or LOG_Message() to be used directly instead of the LOG_FATAL
macro.
2019-10-10 18:05:00 +02:00
Miroslav Lichvar
990f8cd89b test: extend 110-chronyc test 2019-09-24 16:39:49 +02:00
Miroslav Lichvar
813ea71b50 test: extend 105-ntpauth test 2019-09-24 16:39:49 +02:00
Miroslav Lichvar
e8be384cdf test: extend keys unit test 2019-09-24 16:39:49 +02:00
Miroslav Lichvar
61773a2c07 test: add cmac unit test 2019-09-24 16:39:49 +02:00
Miroslav Lichvar
510aa8b050 client: add CMAC support to keygen command
Allow a CMAC cipher to be specified in the keygen command. Ignore the
specified length as the key length is determined by the cipher.
2019-09-24 16:39:01 +02:00
Miroslav Lichvar
57957ab6cf keys: add support for CMAC keys
Allow a cipher (AES128 or AES256) to be specified as the type of a key
in the key file to authenticate NTP packets with a CMAC instead of the
NTPv4 (RFC 5905) MAC using a hash function. This follows RFC 8573.
2019-09-24 16:38:12 +02:00
Miroslav Lichvar
e8069a0179 cmac: add support for Nettle
Add support for AES128 and AES256 CMAC in Nettle.
2019-09-24 14:04:44 +02:00
Miroslav Lichvar
f3f840551a cmac: add CMAC interface
Add cmac.h and stubs for cipher-based message authentication code
(CMAC).
2019-09-24 11:56:05 +02:00
Miroslav Lichvar
10a42c1e04 keys: don't fudge authentication delay
Remove the magic constant compensating for copying, conversions, etc.
It cannot possibly be accurate on all hardware. The delay is supposed to
be a minimum delay.
2019-09-24 11:35:51 +02:00
Miroslav Lichvar
4a219ecbf1 hash: drop support for RIPEMD hash functions
An analysis by Tim Ruffing [1] shows that a length extension attack
adding valid extension fields to NTPv4 packets is possible with some
specific key lengths and hash functions using little-endian length like
MD5 and RIPEMD160.

chronyd currently doesn't process or generate any extension fields, but
it could be a problem in future when a non-authentication extension
field is supported.

Drop support for all RIPEMD functions as they don't seem to be secure in
the context of the NTPv4 MAC. MD5 is kept only for compatibility.

[1] https://mailarchive.ietf.org/arch/msg/ntp/gvibuB6bTbDRBumfHNdJ84Kq4kA
2019-09-24 11:32:31 +02:00
Miroslav Lichvar
0d298bfc4c makefile: improve coding style 2019-09-19 17:30:28 +02:00
Miroslav Lichvar
792c241e3a makefile: refactor to support extra client-specific objects 2019-09-19 17:30:28 +02:00
Miroslav Lichvar
6336a87855 configure: move duplicated libraries to LIBS 2019-09-19 17:30:28 +02:00
Miroslav Lichvar
f5721b1212 configure: remove unused variables 2019-09-19 17:30:28 +02:00
Miroslav Lichvar
7d3e9180c6 test: disable server on client-only nodes by default 2019-09-12 14:51:12 +02:00
Miroslav Lichvar
03b8ca186a test: add 138-syncloop test 2019-09-12 14:51:12 +02:00
Miroslav Lichvar
435cbef31a test: allow nodes to poll themselves 2019-09-12 14:51:12 +02:00
Miroslav Lichvar
4adcf58368 test: remove subdirectories in tmp directory 2019-09-12 14:50:58 +02:00
Miroslav Lichvar
004986310d ntp: skip loop test if no server socket is open
If there is no socket that could receive a request from a client or
peer, we know that nothing can be synchronized to us and no loop is
possible.
2019-09-12 13:01:18 +02:00
Miroslav Lichvar
7a88e0a87b ntp: prevent synchronization to itself
Improve the client's test D to compare the stratum, reference ID,
reference timestamp, and root delay from the received packet with its
own reference data in order to prevent it from synchronizing to itself,
e.g. due to a misconfiguration.
2019-09-12 13:01:18 +02:00
Miroslav Lichvar
64e21d6281 reference: make local reference timestamp consistent
In the local reference mode, instead of returning the adjusted current
time as the reference time, return the same timestamp updated only once
per about 62.5 seconds.

This will enable chronyd to detect polling of itself even when the local
reference mode is active.
2019-09-12 13:01:06 +02:00
Miroslav Lichvar
9ef7ea2bcb reference: rework adjustment of reference timestamp
Instead of converting the reference timestamp to the NTP format and
back, add a negative double value to the timestamp directly. Move the
code to a separate function. This will allow the timestamp to stay
outside the compiled-in NTP era, which is useful for testing of the
cmdmon protocol.
2019-09-11 17:33:57 +02:00
Miroslav Lichvar
6d1796d6be test: extend 110-chronyc test 2019-09-03 13:17:44 +02:00
Miroslav Lichvar
fcaba98101 test: add 137-pool test 2019-09-03 12:41:01 +02:00
Miroslav Lichvar
9bbda5c964 test: add 013-nameserv test 2019-09-02 16:32:58 +02:00
Miroslav Lichvar
2c81d38861 test: add option to enable name/address resolving 2019-09-02 16:27:05 +02:00
Miroslav Lichvar
78fec3f05a test: add copyright header to util unit test 2019-08-27 17:11:49 +02:00
Miroslav Lichvar
392a1a5ff6 test: extend 105-ntpauth test 2019-08-27 17:11:49 +02:00
Miroslav Lichvar
219a414cb7 test: add debug message to ntp unit test 2019-08-27 17:11:49 +02:00
Miroslav Lichvar
58fc81441b ntp: update setting of socket option 2019-08-27 17:11:49 +02:00
Miroslav Lichvar
02ada36838 socket: add support for TCP sockets
TCP sockets will be needed for NTS key establishment.
2019-08-27 17:10:13 +02:00
Miroslav Lichvar
81978f0ba0 socket: fix typo in union declaration 2019-08-08 17:32:48 +02:00
Miroslav Lichvar
622769cdfd util: add debug messages to UTI_FdSetCloexec() 2019-08-08 17:32:48 +02:00
Miroslav Lichvar
3038047f9b makefile: clean unit tests in clean target 2019-08-06 16:11:07 +02:00
Miroslav Lichvar
3e3f045ab7 doc: improve ntpdate answer in FAQ 2019-08-06 16:11:07 +02:00
Miroslav Lichvar
a6d9f41eda sourcestats: report offset even with single sample 2019-08-06 16:11:07 +02:00
Miroslav Lichvar
bf6a4e1a81 sourcestats: simplify SST_DoSourcestatsReport() 2019-08-06 16:11:07 +02:00
Miroslav Lichvar
5982d96b75 test: extend 130-quit test 2019-08-06 16:11:07 +02:00
Miroslav Lichvar
28e3e4cdca sourcestats: enable selection with maxsamples < 3
Setting maxsamples to 1 or 2 prevented the source from being selected as
the regression would always fail. Handle this as a special case with
disabled frequency tracking in order to enable a fast reference update
with the -q/-Q option.
2019-08-06 16:11:07 +02:00
Miroslav Lichvar
24134c78e8 sourcestats: update offset estimate when regression fails
If there are too few samples to make a regression, at least update the
offset estimate from the last sample and keep the previous frequency
offset unchanged. Also, reset the error estimates.
2019-08-06 13:04:59 +02:00
Miroslav Lichvar
5e8ed72b89 socket: fix compiler warning
Don't define check_socket_flag() if no supported socket flag is defined.
2019-07-25 09:52:33 +02:00
Miroslav Lichvar
45e41b7ac1 socket: avoid unnecessary bind() call
Don't call bind() if the specified local address of a socket has port 0
and the "any" address. It will be bound automatically on connect() or
sendmsg().
2019-07-24 16:27:07 +02:00
Miroslav Lichvar
27fd751915 socket: add support for socket() flags
On start, check if the SOCK_CLOEXEC and SOCK_NONBLOCK flags are
supported in the socket() call and use them instead of fcntl() in order
to reduce the number of system calls required to send a client request.
2019-07-24 15:35:00 +02:00
Miroslav Lichvar
4d26cfc92b socket: make all sockets non-blocking
All networking code in chronyd (NTP server/client, signd client, cmdmon
server) assumes sending a message will not block, but only the signd
client actually checks for a write event and only the NTP server
requests a non-blocking socket. The cmdmon server and NTP client
(if using one socket for all servers) might be blocked.

chronyc doesn't need a non-blocking socket, but it is not expected to
block as it sends only one message at a time.

Prefer dropped messages over blocking in all cases. Remove the
SCK_FLAG_NONBLOCK flag and make all sockets non-blocking.
2019-07-24 10:21:14 +02:00
Miroslav Lichvar
d78680912e ntp: improve debug messages with port number 2019-07-18 17:29:44 +02:00
Miroslav Lichvar
47e4cb31b2 util: move and improve sockaddr-specific functions
Move the functions to socket.c and improve them to require and check the
sockaddr length.
2019-07-18 17:29:44 +02:00
Miroslav Lichvar
91da65a782 util: remove UTI_SockaddrToString()
It is no longer used after the conversions.
2019-07-18 17:29:44 +02:00
Miroslav Lichvar
bb1c02e9f5 client: convert to new socket API 2019-07-18 17:29:44 +02:00
Miroslav Lichvar
c651ea9b6b refclock: remove SOCK socket on exit 2019-07-18 17:29:44 +02:00
Miroslav Lichvar
207f9fb128 refclock: convert SOCK to new socket API 2019-07-18 17:29:44 +02:00
Miroslav Lichvar
f06c1cfa97 cmdmon: respond from same address
Enable the destination address of received messages in order to respond
from the same address on multihomed hosts.
2019-07-18 17:29:44 +02:00
Miroslav Lichvar
6cd47bff8f cmdmon: convert to new socket API 2019-07-18 17:29:44 +02:00
Miroslav Lichvar
2de24cfd82 ntp: convert to new socket API
Rework the NTP I/O code to use the new socket support. There are
differences in debug messages and handling of some errors.
2019-07-18 17:29:35 +02:00
Miroslav Lichvar
86a3ef9ed1 socket: add new socket support
Add a new file implementing support for opening sockets, sending and
receiving messages with control messages (e.g. addresses, timestamps),
and related operations, which should be simpler to use than the system
functions and allow their features to be reused between different parts
of the chrony code.

It is based on the ntp_io.c and ntp_io_linux.c files. It will be used by
the NTP client/server, cmdmon server, client, and others.
2019-07-18 16:54:48 +02:00
Miroslav Lichvar
3f8c57c8f2 util: add UTI_IPSockAddrToString()
This function prints an IPSockAddr. IPv6 addresses are printed in
brackets to separate the address from the port.
2019-07-18 13:37:52 +02:00
Miroslav Lichvar
ca96946416 addressing: introduce IPSockAddr
Rename NTP_Remote_Address to IPSockAddr to make it usable in non-NTP
context and provide NTP_Remote_Address for compatibility. Also, change
the type of port to uint16_t.
2019-07-18 13:37:52 +02:00
Miroslav Lichvar
e5b9b6d701 cmdmon: limit rate of all responses
Include responses to invalid requests in the rate limiting enabled by
the cmdratelimit directive.
2019-07-18 13:37:52 +02:00
Miroslav Lichvar
8cb689a5e6 cmdmon: don't require bound UDP socket
Don't abort on start when no UDP socket could be opened/bound for
cmdmon. The Unix socket is more important and with the IP_FREEBIND
option this case was not caught anyway.
2019-07-18 13:35:54 +02:00
Miroslav Lichvar
2270234115 privops: add assertion for bind address length 2019-07-16 13:46:37 +02:00
Miroslav Lichvar
a073f383e6 test: fix building of unit tests
This fixes commit 1227873b88.
2019-07-16 13:46:37 +02:00
Miroslav Lichvar
8e74655b03 doc: improve chronyd man page 2019-07-04 17:38:13 +02:00
Miroslav Lichvar
70fa3a6905 main: add option to specify minimum log severity level
The -L option can be used to disable logging of less severe messages,
e.g informational or warnings.
2019-07-04 17:38:13 +02:00
Miroslav Lichvar
1227873b88 logging: refactor enabling of debug messages
Reorder the LOGS_Severity enum in order of severity and change the code
to not log/print messages with severity below the specified minimum
instead of having a separate debug level.
2019-07-04 17:38:13 +02:00
Miroslav Lichvar
d30e73d0d9 nameserv: request SOCK_DGRAM socktype
Specify SOCK_DGRAM socktype instead of SOCK_STREAM in hints for
getaddrinfo() as chronyd is (and will mainly be) using the returned
addresses to open UDP sockets. This shouldn't make a difference in
practice, but it might avoid some confusion.
2019-06-26 17:21:47 +02:00
Miroslav Lichvar
9e7a7008de configure: fix warnings in tests
Fix some warnings in configure tests reported by clang and coverity
static analyzer.
2019-06-18 16:24:01 +02:00
Miroslav Lichvar
62d6aed6a6 test: update processing of packet log
Two new fields have been added to the packet log, which broke some
of the simulation tests.
2019-06-18 15:42:11 +02:00
Miroslav Lichvar
ffb9887cce doc: update NEWS 2019-05-10 12:22:57 +02:00
Miroslav Lichvar
9220c9b8a2 update copyright years 2019-05-10 11:01:27 +02:00
Miroslav Lichvar
2e28b19112 doc: add note about minsamples to FAQ 2019-05-10 11:01:27 +02:00
Miroslav Lichvar
636a4e2794 refclock: remove unnecessary strlen() call 2019-05-10 11:01:27 +02:00
Miroslav Lichvar
5c9e1e0b69 test: extend 133-hwtimestamp test 2019-05-10 11:01:27 +02:00
Miroslav Lichvar
64fd1b8ba5 ntp: check value returned by CMSG_FIRSTHDR
In NIO_Linux_RequestTxTimestamp(), check the returned pointer and the
length of the buffer before adding the control message. This fixes an
issue reported by the Clang static analyzer.
2019-05-10 10:58:37 +02:00
Miroslav Lichvar
69d3913f3e ntp: check timestamping configuration when SIOCSHWTSTAMP fails
With future kernels it may be possible to get, but not set, the HW
timestamping configuration on some specific interfaces like macvlan in
containers. This would require the admin to configure the timestamping
before starting chronyd.

If SIOCSHWTSTAMP failed on an interface, try SIOCGHWTSTAMP to check if
the current configuration matches the expected configuration and allow
the interface to be used for HW timestamping.
2019-05-09 14:44:58 +02:00
Miroslav Lichvar
08fd011b6a examples: remove /var from PIDFile in chronyd.service
Recent systemd versions complain when loading a unit using a PIDFile
that relies on the /var/run -> /run symlink.
2019-05-06 15:44:24 +02:00
Miroslav Lichvar
c172268cfe doc: update NEWS 2019-05-02 11:50:41 +02:00
Miroslav Lichvar
94b014865c doc: add more recommendations for best stability to FAQ 2019-04-30 14:46:19 +02:00
Miroslav Lichvar
099aaf2cb1 doc: update list of contributors
Include all authors from the git repository.
2019-04-30 12:56:25 +02:00
Miroslav Lichvar
4481a8b24f doc: simplify acknowledgements in README
Stop trying to maintain a list of individual contributions. Just list
the contributors. For tracking individual changes in the source code
there is git.
2019-04-30 12:56:25 +02:00
Miroslav Lichvar
b626fe661e doc: list build requirements in installation 2019-04-30 12:56:25 +02:00
Miroslav Lichvar
ba8fcd145d doc: improve combinelimit description 2019-04-26 11:19:20 +02:00
Miroslav Lichvar
981d09de40 doc: improve rtconutc description 2019-04-26 11:12:41 +02:00
Miroslav Lichvar
86a99bb257 test: use env in shebang of system tests
This should allow the tests to run on systems where bash is not in /bin.
2019-04-26 10:54:02 +02:00
Miroslav Lichvar
3093a11cd0 test: add 104-systemdirs system test 2019-04-25 18:58:01 +02:00
Miroslav Lichvar
058b788d38 test: fix owner of driftfile and keys in system tests 2019-04-25 18:57:39 +02:00
Miroslav Lichvar
66a42fa493 test: allow separate lib/log/run directories in system tests 2019-04-25 18:55:56 +02:00
Miroslav Lichvar
a85f63cc15 test: check if non-root user can access test directory 2019-04-25 18:24:50 +02:00
Miroslav Lichvar
bbe1e69dcc test: redirect error messages in system tests 2019-04-25 18:24:41 +02:00
Miroslav Lichvar
1b52bba7b9 test: allow TEST_DIR and CHRONYC_WRAPPER to be set for system tests 2019-04-25 17:29:34 +02:00
Stefan R. Filipek
c5c80ef400 sys_posix: support SCHED_FIFO and mlockall on more OSs
Real-time scheduling and memory locking is available on posix compliant
OSs. This patch centralizes this functionality and brings support to
FreeBSD, NetBSD, and Solaris.

[ML: updated coding style]
2019-04-24 12:18:07 +02:00
Miroslav Lichvar
a78031ce0d refclock: check all driver options
In each driver provide a list of supported options and abort when an
unknown option is specified in the refclock directive.
2019-04-18 16:27:47 +02:00
Miroslav Lichvar
34e9dd13ce doc: fix syntax of refclock directive
When multiple driver options are specified, they need to be separated by
colon, not comma.
2019-04-18 16:27:19 +02:00
Miroslav Lichvar
6e52a9be7a test: add system tests
Add a new set of tests for testing basic functionality, starting chronyd
with root privileges on the actual system instead of the simulator.

Tests numbered in the 100-199 range are considered destructive and
intended to be used only on machines dedicated for development or
testing. They are started by the run script only with the -d option.
They may adjust/step the system clock and other clocks, block the RTC,
enable HW timestamping, create SHM segments, etc.

Other tests should not interfere with the system and should work even
when another NTP server/client is running.
2019-04-18 16:11:45 +02:00
Stefan R. Filipek
69c6dffd63 sys_linux: use pthread_setschedparam instead of sched_setscheduler
Fix an issue with Linux and musl libc where sched_setscheduler is not
implemented. It seems that pthread_setschedparam is more widely
supported across different C libraries and OSs. For our use case, it
should make no difference which call is used.
2019-04-08 16:38:38 +02:00
Vincent Blut
2ddd0ae231 sys_linux: allow further syscalls in seccomp filter
These are needed on arm64.
2019-03-18 14:24:54 +01:00
Leigh Brown
79db0b7eca sys_linux: allow recv and send in seccomp filter 2019-03-14 09:07:15 +01:00
Vincent Blut
2ebba7fbaa sys_linux: allow waitpid in seccomp filter 2019-02-28 17:42:02 +01:00
Vincent Blut
e392d1fde9 sys_linux: allow _llseek in seccomp filter
This is needed on various 32-bit platforms to reposition read/write file
offset on {raw}measurements and statistics log files.
2019-02-28 16:19:05 +01:00
Miroslav Lichvar
d7c93ec950 test: fix distribution of settings in ntp_core unit test 2019-02-20 10:11:58 +01:00
Miroslav Lichvar
6af39d63aa ntp: don't use IP_SENDSRCADDR on bound socket
On FreeBSD, sendmsg() fails when IP_SENDSRCADDR specifies a source
address on a socket that is bound to the address. This prevents a server
configured with the bindaddress directive from responding to clients.

Add a new variable to check whether the server IPv4 socket is not bound
before setting the source address.
2018-12-03 16:08:08 +01:00
Miroslav Lichvar
cc8414b1b3 sys_linux: add support for PTP_SYS_OFFSET_EXTENDED ioctl
A new ioctl will probably be added in Linux 4.21. It should enable a
significantly more accurate measurement of the offset between PHC and
system clock.
2018-11-27 14:56:17 +01:00
Miroslav Lichvar
6b44055e3d sys_linux: split reading and processing of PHC samples 2018-11-27 14:51:25 +01:00
Miroslav Lichvar
9f9c6cc6ab ntp: fix transposition with timestamping packet info
Don't forget to include the length of the frame check sequence (FCS) in
the RX timestamp transposition when the L2 length of the received packet
is from SCM_TIMESTAMPING_PKTINFO.

This fixes commit 934d4047f1.
2018-10-08 15:54:07 +02:00
Bryan Christianson
f176193d35 sys_macosx: remove adjtime() check
Remove the runtime checking of adjtime(). adjtime() was broken in beta
releases of macOS 10.13 but is ok now.
2018-10-02 10:50:04 +02:00
Miroslav Lichvar
e8bc41e862 test: fix tests to skip when missing required feature 2018-09-27 11:42:38 +02:00
Miroslav Lichvar
91dbe3c6c2 test: allow unit tests to be skipped 2018-09-27 11:42:38 +02:00
Miroslav Lichvar
3e876d4218 test: add function for checking config.h in tests 2018-09-27 11:42:38 +02:00
Miroslav Lichvar
31b1f2e8a1 test: include util.h for MIN macro 2018-09-27 11:42:38 +02:00
Miroslav Lichvar
4169e94b1d nameserv: adopt some include directives from sysincl.h
Move headers specific to name resolving to nameserv.c. This should hide
the system MIN/MAX macros from the rest of the code.
2018-09-20 15:34:24 +02:00
Miroslav Lichvar
948ecf8431 hash: include util.h for MIN macro
The hash_intmd5.c file inadvertently relied on the system headers to
provide the MIN macro, but it is missing with some libc implementations.
2018-09-20 15:34:24 +02:00
Miroslav Lichvar
91f3f97ea7 test: fix Makefile to not create .deps in project root 2018-09-19 16:38:15 +02:00
Miroslav Lichvar
65bb65b440 doc: add new question to FAQ 2018-09-17 18:38:46 +02:00
Miroslav Lichvar
ea6e8d85a3 doc: improve description of minsamples directive 2018-09-17 18:38:24 +02:00
Miroslav Lichvar
add932501f test: add 136-broadcast test 2018-09-17 18:36:22 +02:00
Miroslav Lichvar
89390a738f test: add 012-daemonts test 2018-09-17 18:36:22 +02:00
Miroslav Lichvar
ac4f6ab93b test: improve Makefile
The -s option of make apparently doesn't work when called from make -C.
Add another filter to ignore the Entering/Leaving messages.

Also, fix a typo.
2018-09-13 16:29:49 +02:00
Miroslav Lichvar
dbcb1b9b0b test: include all objects in prerequisites of unit tests 2018-09-13 11:23:16 +02:00
Miroslav Lichvar
6375307798 test: get list of objects from main Makefile
Instead of linking unit tests with *.o in the root directory, which may
include conflicting objects from a different configuration (e.g. hash),
add a print target to the main Makefile and use it in the unit test
Makefile to link only with objects that are relevant in the current
configuration.
2018-09-13 11:23:16 +02:00
Miroslav Lichvar
fb78e60d26 test: add 135-ratelimit test 2018-09-12 19:27:48 +02:00
Miroslav Lichvar
b822c7164f test: add 134-log test 2018-09-12 19:04:11 +02:00
Miroslav Lichvar
aa295730a0 test: extend 110-chronyc test 2018-09-12 18:17:35 +02:00
Miroslav Lichvar
69d70703b2 git: update .gitignore 2018-09-12 17:16:33 +02:00
Miroslav Lichvar
b2b6ef00dc makefile: remove gcov files and core dumps 2018-09-12 17:15:30 +02:00
Miroslav Lichvar
5dc86c236b update copyright years 2018-09-12 11:38:10 +02:00
Miroslav Lichvar
2563dd9d29 examples: drop chrony.spec
The example spec file was too limited to be recommended for use in any
rpm-based distribution, e.g. it didn't configure chronyd to drop the
root privileges.

Users that want to build a package from the latest source code should
start with the official package of their distribution.
2018-09-12 11:38:10 +02:00
Miroslav Lichvar
a899e3df33 examples: improve description in chrony.keys example 2018-09-12 11:38:08 +02:00
Miroslav Lichvar
692cea49f8 doc: warn about permissions in keyfile description 2018-09-12 10:43:01 +02:00
Miroslav Lichvar
bcedacaa3d test: fix samplefilt unit test to work with low-precision clock 2018-09-10 14:56:03 +02:00
Miroslav Lichvar
be3c1b5243 samplefilt: use SQUARE macro in SPF_CreateInstance() 2018-09-10 13:20:14 +02:00
Miroslav Lichvar
e626ec6c37 examples: update chrony.conf example for new default pidfile 2018-09-10 10:37:37 +02:00
Miroslav Lichvar
49d52b547f test: make 129-reload more reliable 2018-08-31 12:48:36 +02:00
Miroslav Lichvar
74a546a9e7 configure: fix compiler warning in pthread test code 2018-08-31 11:04:36 +02:00
Miroslav Lichvar
d1777087c1 doc: update NEWS 2018-08-31 10:11:17 +02:00
Miroslav Lichvar
cf7b5363cd test: extend 110-chronyc test 2018-08-31 09:55:43 +02:00
Miroslav Lichvar
7f3183cc72 test: extend 106-refclock test 2018-08-31 09:55:43 +02:00
Miroslav Lichvar
f1b8da085b doc: update FAQ 2018-08-30 11:56:13 +02:00
Miroslav Lichvar
09dfca49ec configure: fix detection of timepps.h on NetBSD
The header requires <time.h> for struct timespec.
2018-08-30 11:56:13 +02:00
Miroslav Lichvar
88e0ec07aa refclock: fix compiler warning on FreeBSD 2018-08-30 11:56:13 +02:00
Miroslav Lichvar
0adc8e8f92 ntp: add support for IP_RECVDSTADDR and IP_SENDSRCADDR
FreeBSD doesn't support IP_PKTINFO. Instead it provides IP_RECVDSTADDR
and IP_SENDSRCADDR, which can be used to get/set the destination/source
address.

In future IP_RECVIF and IP_SENDIF may be supported to get and set also
the interface.
2018-08-30 11:56:13 +02:00
Miroslav Lichvar
5fc7674e36 ntp: set interface index in IP*_PKTINFO when responding
When a server with multiple interfaces in the same network is sending a
response, setting the ipi_spec_dst/ipi6_addr field of the IP*_PKTINFO
control message selects the source address, but not necessarily the
interface. The packet has the expected source address, but it may be
sent by an interface that doesn't have the address.

Set the ipi_ifindex/ipi6_ifindex field to respond on the same interface
as the request was received from to avoid asymmetries in delay and
timestamping.
2018-08-30 11:56:13 +02:00
Miroslav Lichvar
018977044a test: add 133-hwtimestamp test 2018-08-30 08:08:19 +02:00
Miroslav Lichvar
cc49d8e6e6 test: add 132-logchange test 2018-08-28 18:27:43 +02:00
Miroslav Lichvar
933bd017b4 test: add 131-maxchange test 2018-08-28 18:27:43 +02:00
Miroslav Lichvar
d558b33d85 test: extend 108-peer test 2018-08-28 18:27:43 +02:00
Miroslav Lichvar
9268bf2cff test: don't override user settings with default values
This fixes commit 671daf06b8.
2018-08-28 18:27:43 +02:00
Miroslav Lichvar
dbf2c22467 test: extend ntp_core unit test 2018-08-28 18:27:03 +02:00
Miroslav Lichvar
af4fe92095 test: update hash unit test 2018-08-28 18:18:07 +02:00
Miroslav Lichvar
e034a07be8 test: enable unit tests to suspend logging 2018-08-28 18:18:07 +02:00
Miroslav Lichvar
eb8c9ad601 logging: allow reopening stderr
LOG_OpenFileLog(NULL) can be now used to reopen stderr.
2018-08-28 18:18:07 +02:00
Miroslav Lichvar
6847536669 logging: close previous file log after opening new one
Currently, the log is always opened only once, but that will change with
tests temporarily suspending logging.
2018-08-28 15:42:38 +02:00
Miroslav Lichvar
f5206db9b0 ntp: optimize MAC truncation
When generating a MAC for an NTP packet, request only the bytes that
will be sent.
2018-08-27 19:00:08 +02:00
Miroslav Lichvar
6ab2ed0da6 hash: allow truncated output
Tomcrypt, some NSS hash functions, and the internal MD5 require the
output buffer to be at least as long as the digest. To provide the same
hashing API with all four options, use an extra buffer for the digest
when necessary and copy only the requested bytes to the caller.
2018-08-27 19:00:08 +02:00
Miroslav Lichvar
7352e470e1 ntp: remove unnecessary constant 2018-08-27 17:30:47 +02:00
Miroslav Lichvar
5bc9c0d07a sources: check maximum reach size before postponing update
Don't wait for other sources to be selectable when the maximum
selectable and non-selectable reachability registers happen to match
and a register is already full (e.g. after heavy packet loss).
2018-08-27 13:33:47 +02:00
Miroslav Lichvar
a2146e82ef doc: improve description of LastRx column in chronyc sources 2018-08-27 11:26:51 +02:00
Miroslav Lichvar
6e10e6740c test: add 130-quit test 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
bfaa10f2b0 test: add 129-reload test 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
9f167a7997 test: add 128-nocontrol test 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
6908163464 test: separate client/server chronyd options 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
671daf06b8 test: avoid using eval in shell scripts 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
b189a5386b test: extend util unit test 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
7889d108c2 test: add samplefilt unit test 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
3cfa8ce9d3 test: add sanitizers test 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
570573fe28 test: detect configure errors in compilation test 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
62b1a11736 test: add -Werror to CFLAGS in compilation test 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
c00d517e12 doc: update description of -r option 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
001f3d5e27 sourcestats: improve debug message in SST_GetTrackingData() 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
6045023a49 sources: use SQUARE macro in combine_sources() 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
bba29a0ee7 samplefilt: check for non-increasing sample times
Adopt the check from the refclock code to check also samples from NTP.
2018-08-24 18:09:29 +02:00
Miroslav Lichvar
cffc856b50 test: update hwclock unit test 2018-08-21 16:54:54 +02:00
Miroslav Lichvar
419077e04b sys_linux: extend debug message 2018-08-21 15:52:35 +02:00
Miroslav Lichvar
7db9d4acea sys_linux: improve support for upcoming kernel versions
Starting with Linux 4.19, the frequency of the system clock should be
updated immediately in the system call itself, which will significantly
reduce the maximum delay of the update.

Increase the assumed tick rate in order to reduce the dispersion
accumulated by the driver when it sets the frequency.
2018-08-21 15:52:35 +02:00
Miroslav Lichvar
8d5b86efe7 test: make 121-orphan more reliable 2018-08-21 15:52:35 +02:00
Miroslav Lichvar
6cf16aea7b reference: refactor estimation of clock frequency
Reorder code in REF_SetReference(), clean it up a bit, and split off the
parts specific to the weighting and estimation of the new frequency.
2018-08-21 15:52:33 +02:00
Miroslav Lichvar
870545d3cb reference: include skew in local sync status setting 2018-08-21 12:06:57 +02:00
Miroslav Lichvar
2a030c0d0c sourcestats: include offset SD in tracking root dispersion 2018-08-21 12:06:57 +02:00
Miroslav Lichvar
0b709ab1bc util: introduce SQUARE macro 2018-08-21 12:06:57 +02:00
Miroslav Lichvar
a1f2f17385 reference: fix offset SD to include elapsed time
This should slow down corrections based on old measurements with large
estimated error in frequency.
2018-08-21 12:06:57 +02:00
Miroslav Lichvar
2240eefbd0 sources: fix combined offset SD to include elapsed time 2018-08-21 12:06:44 +02:00
Miroslav Lichvar
706d0c281a sources: combine frequencies by variance instead of skew
This seems to slightly improve the stability.
2018-08-21 10:15:19 +02:00
Miroslav Lichvar
ca73e34f30 sources: provide frequency SD to reference update 2018-08-17 17:40:06 +02:00
Miroslav Lichvar
cca2ef4649 sourcestats: provide frequency SD in tracking data 2018-08-17 17:40:06 +02:00
Miroslav Lichvar
05d9edbf8f sourcestats: replace constant with macro 2018-08-17 17:40:06 +02:00
Miroslav Lichvar
c5bdc52a59 test: don't require exit message to be on last line
This is useful with enabled debug output.
2018-08-17 17:39:50 +02:00
Miroslav Lichvar
74f0c0924a ntp: change maxdelay* info messages to use option names 2018-08-13 12:17:57 +02:00
Miroslav Lichvar
05492d1d23 test: improve 101-poll test 2018-08-10 16:46:38 +02:00
Miroslav Lichvar
eea343b93f refclock: improve error messages 2018-08-09 14:52:08 +02:00
Miroslav Lichvar
afff06c88c ntp: add options to set minsamples/maxsamples of hwclock 2018-08-09 14:52:08 +02:00
Miroslav Lichvar
c0717a27f6 hwclock: add parameters for minimum/maximum number of samples
Allocate the arrays which hold the samples dynamically and limit the
number of dropped samples to not fall below the minimum.
2018-08-09 14:52:08 +02:00
Miroslav Lichvar
159bd73f76 test: add 127-filter test 2018-08-09 14:52:08 +02:00
Miroslav Lichvar
9931a9166b cmdmon: include filter length in ADD_SERVER/ADD_PEER request 2018-08-09 14:33:48 +02:00
Miroslav Lichvar
8aa4ae027b ntp: add assertion to get_seperation() 2018-08-09 14:33:48 +02:00
Miroslav Lichvar
dcce79fdbe ntp: shorten minimum allowed polling interval
With the filter option it is useful to collect NTP measurements at
a higher rate.
2018-08-09 14:33:48 +02:00
Miroslav Lichvar
189aafde9d ntp: add filter option
Add an option to use the median filter to reduce noise in measurements
before they are accumulated to sourcestats, similarly to reference
clocks. The option specifies how many samples are reduced to a single
sample.

The filter is intended to be used with very short polling intervals in
local networks where it is acceptable to generate a lot of NTP traffic.
2018-08-09 14:33:47 +02:00
Miroslav Lichvar
99e3045df4 ntp: enable auto burst with very short polling intervals
This fixes commit 5b75d4afef.
2018-08-08 11:36:06 +02:00
Miroslav Lichvar
c498c21fad refclock: split off median filter
Move the implementation of the median filter to a separate file to make
it useful for NTP. Replace some constants with parameters and generalize
the code to work with full NTP samples (including root dispersion/delay,
stratum, and leap).

For refclocks it should give the same results as before.
2018-08-03 17:21:02 +02:00
Miroslav Lichvar
6bef8aa0e9 use common structure for NTP samples
Define a structure for NTP samples and use it to pass samples from
the ntp_core and refclock code to sources and sourcestats.
2018-08-03 17:21:02 +02:00
Miroslav Lichvar
108d112272 sourcestats: don't save stratum for all samples
Save stratum only from the last accumulated sample as only that is
currently needed.
2018-08-03 17:21:02 +02:00
Miroslav Lichvar
05078e4252 sourcestats: track leap status
This moves the leap status of the last sample from the source instance
to the sourcestats instance in order to make them both accumulate the
same data.
2018-08-03 17:21:02 +02:00
Miroslav Lichvar
4ceb9e4cd0 sys_linux: allow fcntl(F_SETFL) in seccomp filter
This fixes commit 76bed76289.
2018-08-03 17:21:02 +02:00
Miroslav Lichvar
a9f237a395 configure: fix detection of timepps.h on FreeBSD
The header requires inttypes.h to be useful.
2018-08-03 17:21:02 +02:00
Miroslav Lichvar
e7ca560c3d configure: drop detection of stdint.h and inttypes.h
The current code uses macros from inttypes.h. There is no point in
detecting and selecting between stdint.h and inttypes.h as the latter is
always needed.
2018-08-03 17:21:02 +02:00
Miroslav Lichvar
d9f86f6f70 memory: add missing include 2018-08-03 17:21:02 +02:00
Miroslav Lichvar
879d936277 util: handle or ignore SIGPIPE signal
In chronyc handle SIGPIPE similarly to SIGTERM. In chronyd ignore the
signal to avoid crashing when a TCP socket will be needed (e.g. for
NTS-KE) and will be unexpectedly closed from the other side.
2018-08-03 17:21:02 +02:00
Miroslav Lichvar
5bb2bf9361 util: handle errors in setting of signal handler as fatal 2018-08-03 17:21:02 +02:00
Miroslav Lichvar
a8167b7959 sched: allow file handler with multiple events to remove itself
Before dispatching a handler, check if it is still valid. This allows a
handler to remove itself when a descriptor has two different events at
the same time.
2018-08-03 17:21:02 +02:00
Miroslav Lichvar
b33b682356 doc: update chrony.conf man page for recent changes 2018-06-22 12:12:11 +02:00
Miroslav Lichvar
2c47602c33 ntp: allow sub-second maxpoll
Remove the maxpoll-specific limit and allow both minpoll and maxpoll to
be set to a negative value.
2018-06-22 12:12:11 +02:00
Miroslav Lichvar
59d1b41716 ntp: restrict use of sub-second polling intervals
When the local polling interval is adjusted between minpoll and maxpoll
to a sub-second value, check if the source is reachable and the minimum
measured delay is 10 milliseconds or less. If it's not, ignore the
maxpoll value and set the interval to 1 second.

This should prevent clients (mis)configured with an extremely short
minpoll/maxpoll from flooding servers on the Internet.
2018-06-22 12:11:36 +02:00
Miroslav Lichvar
5b75d4afef ntp: make burst interval proportional to polling interval
If the polling interval is shorter than 8 seconds, set the burst
interval to the 1/4th of the polling interval instead of the 2-second
constant. This should make the burst option and command useful with
very short polling intervals.
2018-06-22 10:57:03 +02:00
Miroslav Lichvar
e15c7cd236 refclock_sock: downgrade error messages to debug messages
Turn error messages which are not expected to happen with a correctly
working SOCK client into debug messages.
2018-06-20 16:57:24 +02:00
Miroslav Lichvar
9bc774d6af fix printf()/scanf() format signedness
Fix mismatches between the format and sign of variables passed to
printf() or scanf(), which were found in a Frama-C analysis and gcc
using the -Wformat-signedness option.
2018-06-20 16:57:04 +02:00
Miroslav Lichvar
9b34556952 keys: initialize data used for measuring authentication delay
This issue was found in a Frama-C analysis.
2018-06-20 16:45:26 +02:00
Miroslav Lichvar
9a6369d8f1 reference: specify recipient in message when sending email
Instead of adding the recipient to the sendmail command line (which is
interpretted by the shell) add a "To" line to the message and run
sendmail with the -t option to read the recipient from the message.
2018-06-20 16:45:26 +02:00
Miroslav Lichvar
49cdd6bf09 reference: terminate string returned by gethostname()
POSIX doesn't require the string to be terminated if it didn't fit in
the buffer.

This issue was found in a Frama-C analysis.
2018-06-20 16:45:21 +02:00
Miroslav Lichvar
63fe34e890 check values returned by gmtime() and localtime()
While it is not expected to happen with any time that can be represented
by the system clock, the functions are allowed to return NULL. Check the
pointer before dereferencing.

This issue was found in a Frama-C analysis.
2018-06-20 16:45:14 +02:00
Lonnie Abelbeck
85465afb62 client: re-work tab-completion to work with libedit 20180525-3.1
Remove spaces from tab-completion results and now break on a space.
Tested with both readline and editline (libedit)
Incorporated Miroslav's suggestions.
2018-06-18 12:30:45 +02:00
Miroslav Lichvar
339cb06a49 doc: fix description of pidfile directive to mention -Q option
This fixes commit 778fce4039.
2018-06-08 16:44:53 +02:00
Miroslav Lichvar
10150bfcab examples: update pidfile in chronyd.service 2018-06-08 16:44:53 +02:00
Miroslav Lichvar
e50dc739d8 configure: move default pidfile to /var/run/chrony
This allows chronyd to remove its pidfile on exit after dropping the
root privileges in order to prevent another chronyd instance from
failing to start, e.g. due to a wrong SELinux label from chronyd -q.
2018-06-08 16:44:53 +02:00
Miroslav Lichvar
26e08abe71 main: create directories before writing pidfile
This makes it possible to save pidfile in /var/run/chrony.
2018-06-08 16:42:49 +02:00
Miroslav Lichvar
7637faa0d0 ntp: change auto_offline to trigger on failed transmissions
Instead of counting missing responses, switch to the offline state
immediately when sendmsg() fails.

This makes the option usable with servers and networks that may drop
packets, and the effect will be consistent with the onoffline command.
2018-05-25 10:53:21 +02:00
Miroslav Lichvar
8a57a28177 examples: update NetworkManager dispatcher script
Replace most of the code with the new onoffline command.
2018-05-25 10:53:21 +02:00
Miroslav Lichvar
34db671b57 cmdmon: add onoffline command
The onoffline command tells chronyd to switch all sources to the online
or offline status according to the current network configuration. A
source is considered online if it is possible to send requests to it,
i.e. a route to the network is present.
2018-05-25 10:53:21 +02:00
Miroslav Lichvar
8b9021bf34 ntp: allow online/offline state to be selected by connectability
Allow SRC_MAYBE_ONLINE to be specified for new NTP sources and
connectivity setting to select between SRC_ONLINE and SRC_OFFLINE
according to the result of the connect() system call, i.e. check whether
the client has a route to send its requests.
2018-05-25 10:53:21 +02:00
Miroslav Lichvar
ce6b896948 ntp: refactor switching between online and offline state
Use an enum to describe connectivity of a source and merge
the NCR and NSR TakeSourceOnline/Offline() functions into
SetConnectivity() functions.
2018-05-25 10:53:21 +02:00
Miroslav Lichvar
2962fc6286 ntp: check PHC index before opening device
Apparently, it is possible for an interface to report all necessary
flags for HW timestamping without having a PHC. Check the PHC index to
avoid an error message in the system log saying that /dev/ptp-1 cannot
be opened.
2018-05-25 10:53:21 +02:00
Miroslav Lichvar
76bed76289 ntp: enable non-blocking mode on server sockets
Avoid blocking in sendmsg() due to a full send buffer.
2018-05-25 10:53:21 +02:00
Miroslav Lichvar
113f2ebec0 doc: add new questions to FAQ 2018-05-25 10:53:21 +02:00
Miroslav Lichvar
7c5bd948bb util: fall back to reading /dev/urandom when getrandom() blocks
With recent changes in the Linux kernel, the getrandom() system call may
block for a long time after boot on machines that don't have enough
entropy. It blocks the chronyd's initialization before it can detach
from the terminal and may cause a chronyd service to fail to start due
to a timeout.

At least for now, enable the GRND_NONBLOCK flag to make the system call
non-blocking and let the code fall back to reading /dev/urandom (which
never blocks) if the system call failed with EAGAIN or any other error.

This makes the start of chronyd non-deterministic with respect to files
that it needs to open and possibly also makes it slightly easier to
guess the transmit/receive timestamp in client requests until the
urandom source is fully initialized.
2018-05-25 10:53:21 +02:00
Christian Ehrhardt
8cbc68f28f examples: make nm-dispatcher script usable for networkd-dispatcher
Historically there were plenty of callback based implementations around
ifupdown via /etc/network/if-up and similar. NetworkManager added the
dispatcher [1] feature for such a kind of functionality.

But so far a systemd-networkd (only) systemd had no means to handle those
cases. This is solved by networkd-dispatcher which is currently available
at least in ArchLinux and Ubuntu.
It takes away the responsibility to listen on netlink events in each
application and provides a more classic script-drop-in interface to respond
to networkd events [3].

This commit makes the NM example compatible to be used by NetworkManager
dispatcher as well as by networkd-dispatcher. That way we avoid too much
code duplication and can from now on handle special cases in the
beginning so that the tail can stay commonly used.

After discussion on IRC the current check differs by checking the
argument count (only in NetworkManager), if ever needed we could extend
that to check for known custom environment vars (NetworkManager =>
CONNECTION_UUID; networkd-dispatcher => OperationalState).

[1]: https://developer.gnome.org/NetworkManager/stable/NetworkManager.html
[2]: https://github.com/craftyguy/networkd-dispatcher
[3]: https://github.com/systemd/systemd/blob/master/src/systemd/sd-network.h#L86

Signed-off-by: Christian Ehrhardt <christian.ehrhardt@canonical.com>
2018-04-18 15:55:07 +02:00
Miroslav Lichvar
bf7aa52394 sys_linux: fix building with old libcap versions
The cap_get_bound() function and CAP_IS_SUPPORTED macro were added in
libcap-2.21. Check if the macro is defined before use.

The sys/capability.h header from libcap-2.16 and earlier disables the
linux/types.h header, which breaks the linux/ptp_clock.h header. Change
the order to include sys/capability.h as the last system header.
2018-04-05 16:18:23 +02:00
Miroslav Lichvar
366345790d doc: update NEWS 2018-04-04 09:18:44 +02:00
Miroslav Lichvar
f881c153bf client: update copyright years 2018-04-04 09:18:44 +02:00
Miroslav Lichvar
19f3ab2225 ntp: fix handling of socket errors with error queue
In the next Linux version the recvmmsg() system call will be probably
fixed to not return socket errors (e.g. due to ICMP) when reading from
the error queue.

The NTP I/O code assumed this was the correct behavior. When the system
call is fixed, a socket error on a client socket will cause chronyd to
enter a busy loop consuming the CPU until the receive timeout is reached
(8 seconds by default).

Use getsockopt(SO_ERROR) to clear the socket error when reading from the
error queue failed.
2018-04-04 09:18:44 +02:00
Miroslav Lichvar
fd1e80802f privops: allow binding to acquisition port
Fix the privileged helper process to allow binding of client sockets to
a non-zero acquisition port which is not equal to the server port.
2018-04-04 09:18:34 +02:00
Miroslav Lichvar
4b7cb161a8 doc: improve FAQ 2018-03-28 16:57:48 +02:00
Miroslav Lichvar
7848794222 doc: improve description of fallbackdrift directive 2018-03-28 16:57:48 +02:00
Miroslav Lichvar
94822d5156 doc: improve description of key option in chrony.conf man page 2018-03-28 16:57:48 +02:00
Miroslav Lichvar
e3f840aae9 doc: update installation document 2018-03-28 16:57:48 +02:00
Miroslav Lichvar
5aae563277 update copyright years 2018-03-28 16:57:48 +02:00
Miroslav Lichvar
02de782fa3 ntp: fix compiler warnings
Warnings about using uninitialized variables were seen with gcc-7.3.1
and -O3 in CFLAGS.
2018-03-28 16:57:48 +02:00
Miroslav Lichvar
3f6df33feb configure: include SECHASH in chronyc features
chronyc is linked with the crypto library and the keygen command checks
if the specified hash function is known.
2018-03-28 16:57:48 +02:00
Miroslav Lichvar
a94f5fe007 test: extend 126-burst 2018-03-28 16:57:48 +02:00
Miroslav Lichvar
63f0234748 test: fix hash unit test for NSS
Some hash functions in the freebl3 library ignore the length of the
output buffer and always return the length of the digest.
2018-03-28 16:57:48 +02:00
Miroslav Lichvar
47921c7c0c hash: initialize return value before calling NSS hash functions
Some hash functions in the freebl3 library don't support truncated
digests and either return immediately with no update of the output
length, or ignore the length of the output buffer and always write whole
digest.

Initialize the return value to zero to get correct result with the
former.

This is triggered only in the hash unit test. chronyd always provides a
sufficient buffer for the digest.
2018-03-28 16:57:48 +02:00
Miroslav Lichvar
42a85f685e doc: update NEWS 2018-03-15 09:00:47 +01:00
Miroslav Lichvar
feca2399e4 hash: add support for older nettle versions
Use nettle_hashes[] instead of nettle_get_hashes(), which is available
only in nettle >= 3.4. nettle_hashes[] is a symbol available in older
versions and may be renamed in future. In nettle >= 3.4 it is a macro
using nettle_get_hashes() for compatibility.
2018-03-15 09:00:09 +01:00
Miroslav Lichvar
d34e611ec8 doc: update README 2018-03-14 16:14:56 +01:00
Christian Ehrhardt
02098ed830 sys_linux: report if CAP_SYS_TIME is not present
Instead of having adjtimex just fail with a permission issue
improve the error messaging by warning for the lack of
CAP_SYS_TIME on SYS_Linux_Initialise.

Message will look like (instead of only the latter message):
 CAP_SYS_TIME not present
 adjtimex(0x8001) failed : Operation not permitted

Signed-off-by: Christian Ehrhardt <christian.ehrhardt@canonical.com>
2018-03-14 16:14:56 +01:00
Miroslav Lichvar
aa4228bf1b doc: improve description of -x in chronyd man page 2018-03-14 14:59:41 +01:00
Miroslav Lichvar
b296441708 ntp: fix adjustment of init_local_rx timestamp
This fixes commit f0f18a02a7.
2018-03-14 14:59:41 +01:00
Miroslav Lichvar
b827475378 ntp: add debug message to update of link speed 2018-03-14 14:59:41 +01:00
Miroslav Lichvar
78a6698ae1 test: update compilation test to disable nettle 2018-03-13 10:46:57 +01:00
Miroslav Lichvar
e7b6feb34b doc: update supported hash functions in chrony.conf man apge 2018-03-13 10:46:57 +01:00
Miroslav Lichvar
84be834385 hash: add support for nettle 2018-03-13 10:46:57 +01:00
Miroslav Lichvar
e83d808dfd hash: add support for SHA-3 with libtomcrypt 2018-03-13 10:44:24 +01:00
Miroslav Lichvar
35a68d5b59 test: add hash unit test 2018-03-13 10:44:24 +01:00
Miroslav Lichvar
3c593137b0 doc: fix typo in chrony.conf man page 2018-03-12 12:42:05 +01:00
Miroslav Lichvar
deaf0ffed3 ntp: add missing breaks in switch statement
Fortunately, they didn't change the behavior of the code.
2018-03-12 12:42:05 +01:00
Miroslav Lichvar
af145e871e test: use random version in ntp_core unit test 2018-03-07 13:17:55 +01:00
Miroslav Lichvar
fbca570d0b ntp: respond to NTPv1 client requests with zero mode
NTPv1 packets have a reserved field instead of the mode field and the
actual mode is determined from the port numbers. It seems there is still
a large number of clients sending NTPv1 requests with a zero value in
the field (per RFC 1059).

Follow ntpd and respond to the requests with server mode packets.
2018-03-07 11:36:11 +01:00
Miroslav Lichvar
448ef779c2 client: improve handling of unknown responses
Rework the code to not ignore valid packets with unknown or obsolete
responses and return immediately with "bad reply from daemon" instead of
timing out with "cannot talk to daemon".
2018-03-06 13:47:25 +01:00
Miroslav Lichvar
499a69e611 cmdmon: initialize all bytes of reply buffer
Instead of zeroing individual fields, zero all bytes of the buffer
before the reply is formed.

This may have a small impact on performance, but it simplifies the code
and minimizes the risk of leaking uninitialized memory.
2018-03-06 13:47:25 +01:00
Miroslav Lichvar
58c2915878 cmdmon: update protocol changelog 2018-03-06 13:47:25 +01:00
Miroslav Lichvar
eda4b111d3 cmdmon: make length of manual list constant
Make the length of responses containing manual samples constant to
simplify the protocol. It was the only type of response that had a
variable length.

This reverts commit 2343e7a89c.
2018-03-06 13:47:25 +01:00
Miroslav Lichvar
c6dd749687 ntp: check RX and TX timestamp in interleaved client requests
Clients sending packets in the interleaved mode are supposed to use
a different receive and transmit timestamp in order to reliably detect
the mode of the response. If an interleaved request with the receive
timestamp equal to the transmit timestamp is detected, respond in the
basic mode.
2018-03-06 13:47:25 +01:00
Miroslav Lichvar
d2a96f5fbc doc: update README 2018-03-02 14:46:08 +01:00
Miroslav Lichvar
499f513d40 cmdmon: add shutdown command
The command is functionally equivalent to sending the process the
SIGTERM signal.
2018-03-02 13:04:14 +01:00
Miroslav Lichvar
8b1f68b1b4 ntp: delay enabling permanent kernel RX timestamping on Linux
Wait until a kernel RX timestamp is actually missing before opening the
dummy socket in order to avoid a small performance impact in case the
servers are so slow/distant that the kernel can constantly win the race.
2018-03-02 13:03:26 +01:00
Miroslav Lichvar
8e4c776900 test: add 126-burst test 2018-02-28 10:09:47 +01:00
Miroslav Lichvar
d0eb9427c2 ntp: add burst option
When the burst option is specified in the server/pool directive and the
current poll is longer than the minimum poll, initiate on each poll a
burst with 1 good sample and 2 or 4 total samples according to the
difference between the current and minimum poll.
2018-02-28 10:09:47 +01:00
Miroslav Lichvar
7d100b89fc doc: improve description of server options 2018-02-28 10:08:58 +01:00
Miroslav Lichvar
a4bd7f1800 test: make 119-smoothtime more reliable 2018-02-26 17:24:08 +01:00
Miroslav Lichvar
5308e0a25f sources: include maxclockerror in source selection
In the source selection algorithm, include extra dispersion due to
maxclockerror in the root distance of sources that don't have new
samples (the last sample is older than span of all samples) to not
prefer unreachable sources with a short distance and small skew over
reachable sources for too long, and also to decrease their chances of
becoming falsetickers.
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
da862158bf main: open /dev/null as stdin/out/err in daemonization
chronyd doesn't normally write anything to stdout or stderr when running
as a daemon, but it is a good practice to replace them with descriptors
of /dev/null to prevent accidental writes to other files or sockets that
would otherwise take their place.
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
7b98443a13 logging: don't write fatal messages to invalid descriptor
If opening the log file specified with the -l option failed (after
closing all descriptors), the error message is written to an invalid
descriptor as no log file or syslog is opened yet. Fix the code to track
when the output is usable.
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
4da9f74d24 util: replace assert for missing MD5 with fatal log message
Apparently, on some systems the MD5 function is missing with the NSS
support (freebl3). Instead of failing an assertion, exit with a log
message.
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
e41042e258 test: update util unit test 2018-02-26 13:42:04 +01:00
Miroslav Lichvar
5581466c63 test: improve and extend ntp_core unit test 2018-02-26 13:42:04 +01:00
Miroslav Lichvar
e79a6c2116 sourcestats: limit minimum value of std_dev 2018-02-26 13:42:04 +01:00
Miroslav Lichvar
666ece122e ntp: compare receive timestamp when checking for duplicate
Compare both receive and transmit timestamps in the NTP test number 1.

This prevents a client from dropping a valid response in the interleaved
mode if it follows a response in the basic mode and the server did not
have a kernel/hardware transmit timestamp, and the random bits of the
two timestamps happen to be the same (chance of 1 in 2^(32-precision)).
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
2c7ab98370 ntp: don't send packets with RX/TX timestamp equal to another timestamp
Before sending a new packet, check if the receive/transmit timestamp
is not equal to the origin timestamp or the previous receive/transmit
timestamp in order to prevent the packet from being its own valid
response (in the symmetric mode) and invalidate responses to the
previous packet.

This improves protection against replay attacks in the symmetric mode.
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
f0f18a02a7 ntp: separate timestamps for restarting symmetric protocol
Save the local receive and remote transmit timestamp needed for
(re)starting the symmetric protocol when no valid reply was received
separately from the timestamps that are used for synchronization of the
local clock.

This extends the interval in which the local NTP state is (partially)
protected against replay attacks in order to complete a measurement
in the interleaved symmetric mode from [last valid RX, next TX] to
[last TX, next TX], i.e. it should be the same as in the basic mode.
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
c5d8af0285 main: create directories before refclock initialization
This allows the SOCK refclock to open sockets in the /var/run/chrony
directory.
2018-02-16 11:09:54 +01:00
Miroslav Lichvar
0ce15a8472 main: improve error message for failed getpwnam() 2018-02-16 11:09:54 +01:00
Miroslav Lichvar
da60629201 configure: improve check for timestamping options
The socket.h header provided by musl doesn't seem to include the kernel
headers and is missing SCM_TIMESTAMPING_PKTINFO, which causes the
Linux-specific code in chrony to fail to build.
2018-02-16 11:09:54 +01:00
Miroslav Lichvar
2343e7a89c pktlength: handle truncated MANUAL_LIST reply
Before reading the n_samples field of the MANUAL_LIST reply, check if it
is actually contained in the received message. This does not change the
outcome of the client's length check as the returned length was always
larger than the length of the truncated reply and it was dropped anyway,
but it prevents the client from reading uninitialized memory.
2018-02-16 11:09:54 +01:00
Miroslav Lichvar
45f27f4f5e sourcestats: reset instance before loading dump file
Don't rely on the caller to reset the instance and always reset it
before loading data to make sure it can't get to an unexpected state.
2018-02-16 11:09:54 +01:00
Miroslav Lichvar
0bc112f8b4 doc: improve description of refclock tai option
Emphasize that tzdata must be kept up to date in order for the
correction to work as expected.
2018-02-16 11:09:54 +01:00
Miroslav Lichvar
bfc2fa645c test: add 125-packetloss test 2018-02-16 11:09:54 +01:00
Miroslav Lichvar
11111804fd test: extend 106-refclock 2018-02-16 11:09:54 +01:00
Miroslav Lichvar
87ec67247e test: extend 110-chronyc 2018-02-16 11:09:54 +01:00
Miroslav Lichvar
0df8328ceb ntp: keep kernel RX timestamping permanently enabled on Linux
The Linux kernel has a counter for sockets using kernel RX timestamping
and timestamps (all) received packets only when it is not zero. However,
this counter is updated asynchronously from setsockopt(). If there are
currently no other sockets using the timestamping, it is possible that a
fast server response is received before the kernel timestamping is
actually enabled after setting the socket option and sending a request.

Open a dummy socket on start to make sure there is always at least one
timestamping socket to avoid the race condition.
2018-02-16 11:09:54 +01:00
Miroslav Lichvar
b563048ee2 examples: ignore non-up/down events in nm-dispatcher script 2018-02-16 11:09:54 +01:00
Miroslav Lichvar
e8096330be sys_linux: don't keep CAP_SYS_TIME with -x option
When dropping the root privileges, don't try to keep the CAP_SYS_TIME
capability if the -x option was enabled. This allows chronyd to be
started without the capability (e.g. in containers) and also drop the
root privileges.
2018-02-05 14:05:19 +01:00
Miroslav Lichvar
b1647dbcb7 ntp: wait for late HW TX timestamps
When sending client requests to a close and fast server, it is possible
that a response will be received before the HW transmit timestamp of
the request itself. To avoid processing of the response without the HW
timestamp, monitor events returned by select() and suspend reading of
packets from the receive queue for up to 200 microseconds. As the
requests are normally separated by at least 200 milliseconds, it is
sufficient to monitor and suspend one socket at a time.
2018-02-02 11:36:38 +01:00
Miroslav Lichvar
4ddadd5622 ntp: don't request TX timestamp when SW/HW timestamping is disabled 2018-02-01 17:27:45 +01:00
Miroslav Lichvar
3e854006c7 ntp: add missing header guard 2018-01-31 17:23:40 +01:00
Miroslav Lichvar
2c4c235147 sched: allow enabling/disabling individual file handler events 2018-01-30 15:56:51 +01:00
Miroslav Lichvar
6863e43269 client: avoid reading clock after sending request
If chronyc sent a request which caused chronyd to step the clock (e.g.
makestep, settime) and the second reading of the clock before calling
select() to wait for a response happened after the clock was stepped, a
new request could be sent immediately and chronyd would process the same
command twice. If the second request failed (e.g. a settime request too
close to the first request), chronyc would report an error.

Change the submit_request() function to read the clock only once per
select() to wait for the first response even when the clock was stepped.
2017-12-12 11:37:36 +01:00
Miroslav Lichvar
de8708f331 client: remove unused file descriptor sets 2017-12-05 12:13:40 +01:00
Miroslav Lichvar
d0b2486036 client: don't call select() with invalid timeout
If the system clock was stepped forward after chronyc sent a request and
before it read the clock in order to calculate the receive timeout,
select() could be called with a negative timeout, which resulted in an
infinite loop waiting for select() to succeed.

Fix the submit_request() function to not call select() with a negative
timeout. Also, return immediately on any error of select().
2017-12-05 12:13:37 +01:00
Miroslav Lichvar
5384a93645 test: extend util unit test 2017-12-05 10:14:19 +01:00
Miroslav Lichvar
4bbc768652 util: avoid casting to long in UTI_DoubleToTimeval() 2017-12-05 09:44:59 +01:00
Vincent Blut
fead915b45 doc: fix typo in chronyd man page 2017-12-04 11:16:20 +01:00
Miroslav Lichvar
5422e49026 doc: improve leapsectz description 2017-10-12 14:07:12 +02:00
Miroslav Lichvar
77a1f27a1d test: add 124-tai test 2017-10-11 17:49:30 +02:00
Miroslav Lichvar
b45d864f73 test: check for maxchange message in check_chronyd_exit() 2017-10-11 17:49:21 +02:00
Miroslav Lichvar
f35c81c871 refclock: improve TAI-UTC conversion
Instead of using the TAI-UTC offset which corresponds to the current
system time, get the offset for the reference time. This allows the
clock to be accurately stepped from a time with different TAI-UTC
offset.
2017-10-11 17:45:21 +02:00
Miroslav Lichvar
a349b2803c refclock: remove unnecessary return statements 2017-10-11 17:28:34 +02:00
Chris Perl
f5d1b8fb74 refclock: add tai option
This option is for indicating to chronyd that the reference clock is
kept in TAI and that chrony should attempt to convert from TAI to UTC by
using the timezone configured by the "leapsectz" directive.
2017-10-11 17:28:34 +02:00
Chris Perl
a0fe71eef1 reference: add function to get TAI-UTC offset 2017-10-11 17:28:34 +02:00
Andreas Steinmetz
154b39cf7a refclock: add stratum option 2017-10-09 10:39:20 +02:00
Bernhard M. Wiedemann
6f54210db2 configure: allow to override build date
in order to make builds reproducible.
See https://reproducible-builds.org/ for why this is good
and https://reproducible-builds.org/specs/source-date-epoch/
for the definition of this variable.
2017-10-05 18:18:07 +02:00
Miroslav Lichvar
f6539449c5 nameserv: set hints for getaddrinfo() according to -4/-6 option
Avoid sending unnecessary DNS requests when the -4/-6 option is
specified.
2017-10-04 11:20:10 +02:00
Miroslav Lichvar
b8d546a0d1 examples: add leapsectz to configuration examples 2017-09-15 08:32:09 +02:00
Miroslav Lichvar
04e6474b75 reference: check for gmtime() error
Although gmtime() is expected to convert any time of the system clock at
least in the next few NTP eras, a correct code should always check the
returned value and this shouldn't be a fatal error in handling of leap
seconds.
2017-09-15 08:32:08 +02:00
Vincent Blut
eb51c500e8 doc: fix typo in chrony.conf man page 2017-09-11 11:21:13 +02:00
Miroslav Lichvar
6f8fba9a3f conf: check if GLOB_NOMAGIC is defined
This option is not supported by musl and possibly other libc
implementations.
2017-09-01 11:32:16 +02:00
Miroslav Lichvar
750afc30f2 test: fix keys unit test 2017-09-01 11:28:55 +02:00
Miroslav Lichvar
e0e6ec0d84 doc: update NEWS 2017-08-29 14:17:35 +02:00
Miroslav Lichvar
c9f50fc686 update copyright years 2017-08-28 14:38:23 +02:00
Miroslav Lichvar
83c26b458b doc: fix spelling
Don't mix UK and US spelling.
2017-08-28 14:38:19 +02:00
Miroslav Lichvar
b711873f45 test: add 123-mindelay test 2017-08-28 14:27:14 +02:00
Miroslav Lichvar
c68ca40ce4 ntp: improve maxdelayratio test
Similarly to the maxdelaydevratio test, include in the maximum delay
dispersion which accumulated in the interval since the last sample.
Also, enable the test for symmetric associations.
2017-08-28 14:27:14 +02:00
Miroslav Lichvar
51fe80ad95 sourcestats: move maxdelaydevratio test to ntp_core
Instead of giving NTP-specific data to sourcestats in order to perform
the test, provide a function to get all data needed for the test in
ntp_core. While at it, improve the naming of variables.
2017-08-28 14:27:14 +02:00
Miroslav Lichvar
7ffee73524 memory: check for overflow when (re)allocating array
When (re)allocating an array with very large number of elements using
the MallocArray or ReallocArray macros, the calculated size of the array
could overflow size_t and less memory would be allocated than requested.

Add new functions for (re)allocating arrays that check the size and use
them in the MallocArray and ReallocArray macros.

This couldn't be exploited, because all arrays that can grow with cmdmon
or NTP requests already have their size checked before allocation, or
they are much smaller than memory allocated for structures to which they
are related (i.e. ntp_core and sourcestats instances), so a memory
allocation would fail before their size could overflow.

This issue was found in an audit performed by Cure53 and sponsored by
Mozilla.
2017-08-28 14:27:14 +02:00
Miroslav Lichvar
f40b0024bd util: check for gmtime() error
Fix the UTI_TimeToLogForm() function to check if gmtime() didn't fail.
This caused chronyc to crash due to dereferencing a NULL pointer when
a response to the "manual list" request contained time which gmtime()
could not convert to broken-down representation.

This issue was found in an audit performed by Cure53 and sponsored by
Mozilla.
2017-08-28 14:27:14 +02:00
Miroslav Lichvar
a06c9909a6 conf: use enum for RX filter 2017-08-23 15:01:30 +02:00
Miroslav Lichvar
aee42fada8 ntp: allow TX-only HW timestamping by default
If no rxfilter is specified in the hwtimestamp directive and the NIC
doesn't support the all or ntp filter, enable TX-only HW timestamping
with the none filter.
2017-08-23 15:01:30 +02:00
Miroslav Lichvar
3e93068c43 hwclock: improve debug message 2017-08-23 15:01:30 +02:00
Miroslav Lichvar
36291b707b hwclock: check if estimated frequency is sane 2017-08-23 15:01:30 +02:00
Miroslav Lichvar
6dad2c24bf hwclock: drop all samples on reset
On some HW it seems it's possible to get an occasional bad reading of
the PHC (with normal delay), or in a worse case the clock can step due
to a HW/driver bug, which triggers reset of the HW clock instance. To
avoid having a bad estimate of the frequency when the next (good) sample
is accumulated, drop also the last sample which triggered the reset.
2017-08-23 15:01:30 +02:00
Miroslav Lichvar
27cbf20d23 doc: include uncorrected offset in bound on maximum error 2017-08-23 15:01:30 +02:00
Miroslav Lichvar
5c571bbbe7 reference: add new fields to tracking log
Add the root delay, root dispersion and maximum estimated error in the
interval since the previous update to the tracking log.
2017-08-23 15:01:30 +02:00
Miroslav Lichvar
33d65c8614 reference: separate calculation of root dispersion 2017-08-23 15:01:30 +02:00
Miroslav Lichvar
d87db7cdb8 reference: refactor log writing
Remove unnecessary parameters of the write_log() function.
2017-08-23 15:01:30 +02:00
Miroslav Lichvar
45fa4750da reference: don't update fallback drift on manual input
This fixes a crash due to assertion failure in update_fb_drifts() when
fallbackdrift is enabled and manual input is provided.
2017-08-23 15:01:30 +02:00
Miroslav Lichvar
8472fd8133 reference: simplify check for NaN 2017-08-23 15:01:30 +02:00
Miroslav Lichvar
5ab645e310 cmdmon: add new fields to ADD_SERVER/ADD_PEER request 2017-08-23 15:01:30 +02:00
Miroslav Lichvar
8ccda538d3 conf: add mindelay and asymmetry options to NTP sources 2017-08-23 15:01:28 +02:00
Miroslav Lichvar
b06d74ab73 sourcestats: add fixed asymmetry
Rework the code to allow the jitter asymmetry to be specified.
2017-08-23 14:33:23 +02:00
Miroslav Lichvar
d0964ffa83 sourcestats: add fixed minimum delay
If the minimum delay is known (in a static network configuration), it
can replace the measured minimum from the register. This should improve
the stability of corrections for asymmetric jitter, sample weighting and
maxdelay* tests.
2017-08-23 14:14:06 +02:00
Miroslav Lichvar
3d08815efb sys_linux: fix building with older kernel headers
Programming pins for external PHC timestamping was added in Linux 3.15,
but the PHC subsystem is older than that. Compile the programming code
only when the ioctl is defined.
2017-08-15 13:39:39 +02:00
Miroslav Lichvar
a83f0d3cdc util: simplify clamping in UTI_TimespecNetworkToHost()
This should fix a coverity warning.
2017-08-15 13:27:50 +02:00
Miroslav Lichvar
702db726d3 util: add assertion for NTP timestamp size 2017-08-15 13:27:50 +02:00
Miroslav Lichvar
ed5c43204b smooth: don't adjust invalid time of last update 2017-08-15 13:27:50 +02:00
Miroslav Lichvar
f91bdd604d reference: don't adjust invalid reference time 2017-08-15 13:27:50 +02:00
Miroslav Lichvar
3a1dbb1354 test: fix ntp_core unit test
This fixes commit b896bb5a78.
2017-08-09 10:41:30 +02:00
Bryan Christianson
4b511143b8 sys_netbsd: fix adjtime() fault on macOS
On some systems, passing NULL as the first argument to adjtime, will
result in returning the amount of adjustment outstanding from a previous
call to adjtime().

On macOS this is not allowed and the adjtime call will fault. We can
simulate the behaviour of the other systems by cancelling the current
adjustment then restarting the adjustment using the outstanding time
that was returned. On macOS 10.13 and later, the netbsd driver is now
used and must use these semantics when making/measuring corrections.
2017-08-09 09:57:14 +02:00
Miroslav Lichvar
93076e7e1c client: fix parsing of -v command option
The sources and sourcestats commands accept -v as an option, but the
glibc implementation of getopt() reorders the arguments and parses the
option as a command-line option of chronyc.

Add '+' to the getopt string to disable this feature. Other getopt()
implementations should consider it a new command-line option, which will
be handled as an error if present.
2017-08-09 09:57:14 +02:00
Miroslav Lichvar
1c51feb3c5 sched: add new timeout class for peer transmissions
This allows transmissions in symmetric mode to be scheduled
independently from client transmissions. This reduces maximum delay
in scheduling when chronyd is configured with a larger number of
servers.
2017-08-09 09:57:14 +02:00
Miroslav Lichvar
c2773dbc2f test: improve hwclock unit test 2017-08-09 09:57:14 +02:00
Miroslav Lichvar
4534db84c4 hwclock: fix conversion of HW timestamps
Fix a sign error in conversion of HW time to local time, which caused
the jitter to be amplified instead of reduced. NTP with HW timestamping
should now be more stable and able to ignore occasionally delayed
readings of PHC.
2017-08-09 09:57:14 +02:00
Miroslav Lichvar
be8215e181 ntp: minimize data in client mode packets
In basic client mode, set the origin and receive timestamp to zero.
This reduces the amount of information useful for fingerprinting and
improves privacy as the origin timestamp allows a passive observer to
track individual NTP clients as they move across networks. (With chrony
clients that assumes the timestamp wasn't reset by the chronyc offline
and online commands.)

This follows recommendations from the current version of IETF draft on
NTP data minimization [1].

The timestamp could be theoretically useful for enhanced rate limiting
which can limit individual clients behind NAT and better deal with DoS
attacks, but no server implementation is known to do that.

[1] https://tools.ietf.org/html/draft-ietf-ntp-data-minimization-01
2017-08-09 09:57:13 +02:00
Miroslav Lichvar
ae82bbbace examples: improve NetworkManager dispatcher script
When no default route is configured, check each source if it has a
route. If the system has multiple network interfaces, this prevents
setting local NTP servers to offline when they can still be reached over
one of the interfaces.
2017-08-09 09:57:13 +02:00
Miroslav Lichvar
2b6ea41062 doc: fix server mode number in chrony.conf man page 2017-08-09 09:57:13 +02:00
Miroslav Lichvar
d9f745fe70 doc: update chrony.conf man page for recent changes 2017-08-09 09:57:13 +02:00
Miroslav Lichvar
9aac179367 ntp: skip IPv6 extension headers
Handle IPv6 packets with extension headers received from the error queue
on Linux.
2017-08-09 09:57:13 +02:00
Miroslav Lichvar
b896bb5a78 ntp: don't send useless requests in interleaved client mode
In interleaved client mode, when so many consecutive requests were lost
that the first valid (interleaved) response would be dropped for being
too old, switch to basic mode so the response can be accepted if it
doesn't fail in the other tests.

This reworks commit 16afa8eb50.
2017-08-09 09:57:13 +02:00
Miroslav Lichvar
64c2fd9888 ntp: limit number of interleaved responses in symmetric mode
In symmetric mode, don't send a packet in interleaved mode unless it is
the first response to the last valid request received from the peer and
there was just one response to the previous valid request. This prevents
the peer from matching the transmit timestamp with an older response if
it can't detect missed responses.
2017-08-09 09:57:13 +02:00
Miroslav Lichvar
2668a12e4e ntp: improve detection of missed packets in interleaved mode
In interleaved symmetric mode, check if the remote TX timestamp is
before RX timestamp. Only the first response from the peer after
receiving a request should pass this test. Check also the interval
between last two remote transmit timestamps when we know the remote poll
can't be constrained by minpoll. Use the minimum of previous remote and
local poll as a lower bound of the actual interval between peer's
transmissions.
2017-08-09 09:57:13 +02:00
Miroslav Lichvar
e1645966ec ntp: enable maxdelayratio test in interleaved client mode
With more accurate delay in interleaved mode the test should now be as
reliable as in basic mode.
2017-08-09 09:57:13 +02:00
Bryan Christianson
4f1fc1ee78 main: fix -q option
Attempting to step the system clock by using the -q option with chronyd
would fail.
2017-08-09 09:57:13 +02:00
Miroslav Lichvar
d70df3daab logging: enable line buffering of file log
The file log specified with the -l option should have the messages as
soon as they are produced.
2017-08-08 15:37:59 +02:00
Miroslav Lichvar
554b9b06de doc: update NEWS 2017-07-25 17:54:01 +02:00
Miroslav Lichvar
f734bd1a7c sys_linux: allow getrandom in seccomp filter
This fixes commit c5735ebfe9.
2017-07-25 17:40:35 +02:00
Miroslav Lichvar
77fc5c42b9 client: don't allow slash with hostname in allow/deny command 2017-07-25 17:40:35 +02:00
Miroslav Lichvar
ea85bc43e0 conf: don't allow slash with hostname in allow/deny directive 2017-07-25 17:40:35 +02:00
Miroslav Lichvar
e8fb11c433 reference: don't report zero stratum when synchronised
If synchronised to a stratum 15 source, return stratum of 16 instead of
0 in the tracking report. It will not match the value in server mode
packets, but it should be less confusing.
2017-07-25 17:40:35 +02:00
Miroslav Lichvar
01a29c7a11 cmdmon: report offset after manual timestamp as float
Modify the protocol to report the offset as seconds in floating point
instead of integer number of centiseconds.
2017-07-25 17:40:35 +02:00
Miroslav Lichvar
6ec3dc1650 manual: handle failed robust regression 2017-07-25 17:40:35 +02:00
Miroslav Lichvar
0c54cf316d util: avoid undefined behavior in timestamp conversion 2017-07-25 17:40:35 +02:00
Miroslav Lichvar
bd3fb49a1e client: avoid undefined bit shifts 2017-07-25 17:40:35 +02:00
Miroslav Lichvar
f6e72a80e1 regress: avoid undefined behavior in pointer arithmetic 2017-07-21 17:14:15 +02:00
Miroslav Lichvar
c2ab1426e5 ntp: simplify get_poll_adj() 2017-07-21 16:27:03 +02:00
Miroslav Lichvar
fa2c59d78d sourcestats: increase number of samples needed to check delay
Require at least 6 samples to check the increase in the delay of a new
sample to make it more reliable.
2017-07-21 16:27:03 +02:00
Miroslav Lichvar
16afa8eb50 ntp: don't accumulate old samples in interleaved client mode
Check how many responses were missing before accumulating a sample using
old timestamps to avoid correcting the clock with an offset extrapolated
over a long interval.

This should be eventually done in sourcestats for all sources.
2017-07-21 16:27:03 +02:00
Miroslav Lichvar
992590e99c ntp: revert reversed poll tracking in interleaved mode
With the new selection of timestamps in the interleaved mode it's no
longer necessary to reverse the poll tracking in order to reduce the
local and remote intervals of measurements that makes the peer with
higher stratum.

This reverts commit 4a24368763.
2017-07-21 16:27:03 +02:00
Miroslav Lichvar
0baa35eade ntp: select timestamps in interleaved mode
Use previous local TX and remote RX timestamps for the new sample in the
interleaved mode if it will make the local and remote intervals
significantly shorter in order to improve the accuracy of the measured
delay.
2017-07-21 16:27:03 +02:00
Miroslav Lichvar
2e0870ee0c ntp: refactor timestamp selection and interval calculation
Prepare the code for a third option in the timestamp selection and clean
it up a bit.
2017-07-21 16:27:03 +02:00
Miroslav Lichvar
43cd119d6d ntp: add function for zeroing local timestamps 2017-07-21 16:27:03 +02:00
Miroslav Lichvar
62cd319a51 ntp: fix poll in source report
The source report used the local interval, which in symmetric mode may
be longer than the actual interval used for transmission.
2017-07-14 20:25:50 +02:00
Miroslav Lichvar
d0f789425b ntp: ignore saved remote poll when peer is not responding
When a peer stops responding, allow our actual polling interval to be
longer than poll saved from the last valid response.
2017-07-14 20:06:31 +02:00
Miroslav Lichvar
30e6549692 ntp: reset TX counter on all valid responses
Also change it to an unsigned type.
2017-07-14 19:40:44 +02:00
Miroslav Lichvar
043c7d7c9f configure: fix compiler warning in getrandom() test 2017-07-14 10:21:31 +02:00
Miroslav Lichvar
1c277a8850 configure: check for hardening compiler options
If no CFLAGS are specified, check if common security hardening options
are supported and add them to the CFLAGS/LDFLAGS. These are typically
enabled in downstream packages, but users compiling chrony from sources
with default CFLAGS should get hardened binaries too.
2017-07-13 16:12:25 +02:00
Bryan Christianson
ccb94ac5fb sys_macosx: add support for ntp_adjtime() on macOS 10.13+
macOS 10.13 will implement the ntp_adjtime() system call, allowing
better control over the system clock than is possible with the existing
adjtime() system call. chronyd will support both the older and newer
calls, enabling binary code to run without recompilation on macOS 10.9
through macOS 10.13.

Early releases of macOS 10.13 have a very buggy adjtime() call. The
macOS driver tests adjtime() to see if the bug has been fixed. If the
bug persists then the timex driver is invoked otherwise the netbsd
driver.
2017-07-13 16:10:54 +02:00
Miroslav Lichvar
778fce4039 main: don't require root privileges with -Q option
If the -Q option is specified, disable by default pidfile, ntpport,
cmdport, Unix domain command socket, and clock control, in order to
allow starting chronyd without root privileges and/or when another
chronyd instance is already running.
2017-07-13 16:10:54 +02:00
Miroslav Lichvar
9983185d6d ntp: define NTP port for configuration code 2017-07-13 16:10:54 +02:00
Miroslav Lichvar
7bd1c02781 main: refactor check of pidfile 2017-07-13 16:10:54 +02:00
Miroslav Lichvar
760285218f sys_timex: fix update of TAI offset on non-Linux systems
The tai field in struct timex is a Linux-specific feature. It's possible
to read the current offset with ntp_gettime() (or ntp_gettimex() on
Linux), but apparently not all libc implementations support it.

Rework the code to save and adjust the last value instead of reading
the current value from the kernel.
2017-07-11 11:28:34 +02:00
Miroslav Lichvar
4fe0e6b7fd sys_timex: rename status variable 2017-07-10 14:48:47 +02:00
Miroslav Lichvar
0773a1e630 ntp: fix debug message about unknown HW timestamping ifindex 2017-06-30 17:01:06 +02:00
Miroslav Lichvar
4a24368763 ntp: reverse poll tracking in interleaved symmetric mode
Unlike in the basic mode, the peer with a higher stratum needs to wait
for a response before sending the next request in order to minimize the
delay of the measurement and error in the measured delay.

Slightly increase the delay adjustment to make it work with older chrony
versions.
2017-06-30 17:01:06 +02:00
Miroslav Lichvar
577290c5bc ntp: fix poll interleaving with unsynchronised peers
Update the remote poll and remote stratum even for unsychronised peers,
and handle stratum of 0 as 16, so the peers work with the opposite
differences between their strata and can adjust their polling intervals
in order to interleave the packets.
2017-06-30 17:01:01 +02:00
Miroslav Lichvar
854ff69f78 hwclock: decrease tolerance of robust regression to 0.1 ppb 2017-06-30 16:58:57 +02:00
Miroslav Lichvar
29b0ad894c reference: get TAI-UTC offset from leap second timezone
Use the timezone specified by the leapsectz directive to get the
current TAI-UTC offset and set the offset of the system clock in order
to provide correct TAI time to applications using ntp_adjtime(),
ntp_gettime(), or clock_gettime(CLOCK_TAI).
2017-06-30 16:58:53 +02:00
Miroslav Lichvar
cde0a20307 sys_timex: add support for setting TAI-UTC offset 2017-06-30 13:43:35 +02:00
Miroslav Lichvar
a768578a26 local: add support for setting TAI-UTC offset 2017-06-30 13:43:35 +02:00
Miroslav Lichvar
5d838729ef reference: move static tz variables to function using them 2017-06-30 13:43:35 +02:00
Miroslav Lichvar
d6b763dc24 client: check IP address family before printing as refid 2017-06-30 13:43:27 +02:00
Miroslav Lichvar
95adb52a45 configure: add missing object for PHC refclock
This fixes commit eceb8d9937.
2017-06-27 15:29:02 +02:00
Miroslav Lichvar
707d9a3484 test: add regress unit test 2017-06-27 15:29:01 +02:00
Miroslav Lichvar
1872d4d195 test: fix crash when printing debug messages
This fixes commit 6cbeb107db.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
17f32c266e sourcestats: use median distance in weight calculation
Replace mean distance with median distance in the weight calculation.
This should make the weights less sensitive to outliers.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
6207655ab2 regress: provide function to find median 2017-06-27 15:29:01 +02:00
Miroslav Lichvar
5e1e31ad5f regress: reduce maximum number of points to 64
This corresponds to the maximum number of points used by regress users.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
13111c1dd8 regress: use chars instead of ints for flags
This reduces the size of the flags array on stack.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
85c84073c1 regress: fix assertion in robust regression 2017-06-27 15:29:01 +02:00
Miroslav Lichvar
c2944d8727 regress: speed up range expansion in robust regression
Instead of repeatedly expanding the range of b with the same increment,
double the range on each iteration to speed up the expansion. Also, add
a sanity check for the interval.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
e118b9b1e8 regress: fix robust regression
The bisection always terminated after one iteration. Change the code to
check if the middle is different from the lower and upper limits as
suggested in the original recipe.

This fixes commit b14689d59b.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
7fb7f95979 sourcestats: include precision in weight calculation
In order to stabilize the weights of refclock samples which have only
slightly different distances, don't allow the stddev value used in the
weight calculation to be smaller than the precision and also assign
weight of 1 to all samples which have distance < minimum + precision.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
cc507bffae conf: abort when include directive fails
When parsing the include directive, call glob() with the GLOB_ERR and
GLOB_NOMAGIC flags, and abort with an error message when matching of the
pattern failed with other error than GLOB_NOMATCH.

This restores the original behavior of the directive when it didn't
allow patterns, but it will still not fail with patterns not matching
any files in an existing directory.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
0dbfe020ad refclock: set default precision to precision of system clock 2017-06-27 15:29:01 +02:00
Miroslav Lichvar
018a1c42b0 ntp: suggest clients to increase their polling interval
When the poll value in a client request is smaller than the server's NTP
rate limiting interval, set poll in the response to the rate limiting
interval to suggest the client to increase its polling interval.

This follows ntpd as a server. No current client implementation seems to
be increasing its interval by the poll, but it may change in the future.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
c5735ebfe9 util: add support for getrandom()
Add support for the Linux getrandom() system call, which is available
in glibc since 2.25.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
db93180ce1 ntp: apply HW TX/RX compensation to system time
Apply the compensation to the cooked local time instead of HW time. This
might make a difference when the HW clock has a large frequency error.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
39da10d939 doc: update description of hwtimestamp directive 2017-06-27 15:29:01 +02:00
Miroslav Lichvar
f2da253bc3 ntp: add option to select HW RX timestamping filter
Add an rxfilter option to the hwtimestamp directive to select which
received packets should be timestamped. It can be set to "none", "ntp",
or "all". The default value is ntp, which falls back to all when ntp is
not supported.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
934d4047f1 ntp: add support for new Linux timestamping options
New timestamping options may be available in kernel 4.13. They can be
used to get the index of the interface which timestamped incoming packet
together with its length at layer 2, enable simultaneous SW and HW TX
timestamping, and enable a new RX filter for NTP packets.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
b799cfd1c4 ntp: always try to enable SW timestamping on Linux
Request SW timestamps with SCM_TIMESTAMPING even if HW timestamping is
enabled. This replaces SCM_TIMESTAMP(NS) for RX and enables TX SW
timestamping on interfaces that don't support HW timestamping (or don't
have it enabled) if another interface has HW timestamping enabled.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
b712c100d7 main: close logs as last thing before exit
This should prevent losing messages from other finalisation code.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
c049bce007 client: try to connect to all addresses before giving up
Don't give up when one of the addresses/hostnames specified by -h fails
to resolve in DNS_Name2IPAddress(), e.g. with the default setting try to
connect to ::1 even when 127.0.0.1 failed due to the -6 option.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
46fad717e5 client: use getopt() for command line parsing 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
ae0c3bbbe8 main: use getopt() for command line parsing
This allows multiple options to be specified together and also may
options follow configuration directives on systems where getopt()
permutates the arguments.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
f95d57e0d9 doc: fix typo in chronyd man page 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
a1cbd4eb82 main: add option to specify log file
Add -l option to log to a file instead of syslog or terminal.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
6cbeb107db logging: allow logging to file instead of syslog 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
3a5566c6c3 main: use LOG_FATAL to print error when UID is not zero 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
73c548ad01 sourcestats: handle negative elapsed time in SST_GetSelectionData()
Source selection uses the last event time as current time. If it was
called from a refclock which generates a sample in its poll function
(e.g. PHC), the sample time may be later than the event time. This
gives a negative elapsed time in SST_GetSelectionData() and possibly
also a negative root distance, which causes the source to be rejected as
a falseticker.

Use absolute value of the difference in order to always get a positive
root distance.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
82203e12c8 doc: update refclock documentation 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
1ca099473f refclock: add option to filter wrong pulse edges
Add width option to the refclock directive to set expected width of
pulses in a PPS signal. The width adds a limit for the maximum offset
and root distance in order to reject PPS samples from wrong events, e.g.
PHCs which cannot be configured to timestamp only rising of falling
edges.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
eceb8d9937 refclock_phc: add support for timestamping of external PPS
Add extpps driver option to the PHC refclock to enable external
timestamping of PPS signal and also options to configure the channel and
pin index. In this mode, the driver polling function accumulates samples
for hwclock, which is used to convert received timestamping events to
local time.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
4ba92bb6d6 sys_linux: add support for external PHC timestamping 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
f31f68ae8e refclock: add option to treat non-PPS refclocks as PPS
Add pps option to the refclock directive to force chronyd to treat any
refclock as a PPS refclock. This is intended for refclocks that may
provide time off by a whole number of seconds due to missing or wrong
TAI/GPS->UTC conversion.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
cff15f91d4 refclock: allow all drivers to provide PPS samples 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
6b74917954 refclock: allow drivers to provide cooked PPS samples
Split RCL_AddPulse() in order to provide a new function for refclock
drivers which can make PPS samples without having raw system time, e.g.
from PHC timestamps.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
1bf2384a1f refclock: don't require raw time in valid_sample_time()
This makes the check a bit more expensive, but it will be needed to
allow refclocks that don't have raw system time.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
54a12779e2 ntp: include local error in hwclock samples 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
e8b06fef9f ntp: remove unnecessary include 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
653d70ec4e sys_linux: allow sysinfo in seccomp filter
It may be used by glob() in latest glibc.
2017-04-19 14:38:51 +02:00
Miroslav Lichvar
abb09418b1 sys_linux: don't drop PHC samples with zero delay
When processing data from the PTP_SYS_OFFSET ioctl, the sample is
dropped when an interval between two consecutive readings of the system
clock is negative or zero, assuming the clock has been stepped between
the two readings.

With a real PHC the interval is normally expected to be at least a
microsecond, but with a virtual PHC and a low-resolution system clock
it's possible to get two readings with the same system time. Modify the
check to drop only samples with a negative delay.
2017-04-19 13:03:10 +02:00
Miroslav Lichvar
c103bebd9f configure: check for clang
Try clang as the C compiler before cc and use the same -W* CFLAGS as
with gcc.
2017-04-19 13:03:10 +02:00
Miroslav Lichvar
935d855b47 util: indicate truncated Unix socket path in UTI_SockaddrToString()
Specify the maximum length of the path in the snprintf() format to avoid
a new gcc warning (-Wformat-truncation). If the path doesn't fit in the
buffer, indicate with the '>' symbol that it was truncated. The function
is used only for debug messages.
2017-04-19 13:03:10 +02:00
Miroslav Lichvar
f8f9100a0d makefile: run tests in multiple iterations on check
Use the new options of the run script in the check target to make it
reliable for automatic testing without using a fixed random seed and add
a new quickcheck target for the original check using just one iteration.
2017-03-31 14:53:27 +02:00
Miroslav Lichvar
6de7b98e76 test: improve run script
Add options to allow running the tests in multiple iterations while
allowing a small number of failures per test. Some tests are expected to
fail occasionally as they are basically statistical tests. Improving
their reliability is possible, but it's always a compromise between
sensitivity, reliability, and execution time.
2017-03-31 14:53:27 +02:00
Miroslav Lichvar
c390351c65 test: make 118-maxdelay more reliable 2017-03-31 14:53:27 +02:00
Miroslav Lichvar
768bce799b sys_linux: allow getpid in seccomp filter
It seems to be used by syslog() in latest glibc.
2017-03-13 14:42:44 +01:00
Miroslav Lichvar
d3a30142e5 test: fix DEBUG_LOG use in unit tests
This was missing in commit f282856c72.
2017-03-13 12:04:26 +01:00
Chris Perl
3a635fc51f sourcestats: reorder arguments to DEBUG_LOG in SST_IsGoodSample
The delay_increase and allowed_increase variables are backwards with
respect to the ordering of the words in the message.
2017-03-10 16:55:22 +01:00
Miroslav Lichvar
10078566da test: make 117-fallbackdrift more reliable 2017-03-10 16:51:03 +01:00
Miroslav Lichvar
c44346096c sys: add null driver
Add a new clock driver that doesn't actually try to adjust the clock.
It allows chronyd to run without the capability to adjust/set the system
clock, e.g. in some containers. It can be enabled by the -x option.
2017-03-10 16:51:03 +01:00
Miroslav Lichvar
0ff449e6a6 local: improve log message for failed clock step 2017-03-10 16:51:03 +01:00
Miroslav Lichvar
f3a16383b9 main: dump history by default
Always write the measurement history on exit when the dump directory is
specified and silently ignore the dumponexit directive. There doesn't
seem to be a good use case for dumpdir and -r without dumponexit as the
history would be invalidated by adjustments of the clock that happened
between the dump command and chronyd exit.
2017-03-10 16:51:03 +01:00
Miroslav Lichvar
539ef3f770 main: rewrite some error messages 2017-03-10 16:51:03 +01:00
Miroslav Lichvar
f282856c72 logging: remove facility parameter
It was never used for anything and messages in debug output already
include filenames, which can be easily grepped if there is a need
to see log messages only from a particular file.
2017-03-10 16:51:03 +01:00
Miroslav Lichvar
6db8ec1ba2 privops: separate res_init() call
Move the res_init() call from do_name_to_ipaddress() into a separate
privops operation. Use it in ntp_sources and avoid unnecessary
res_init() calls in the main thread.
2017-03-10 16:51:02 +01:00
Miroslav Lichvar
5187c08c90 doc: update NEWS 2017-01-31 11:22:11 +01:00
Miroslav Lichvar
c8076ac10d makefile: fix distclean target to not print errors 2017-01-31 11:22:11 +01:00
Miroslav Lichvar
362d155558 examples: improve configuration examples 2017-01-31 11:22:11 +01:00
Miroslav Lichvar
7b7eb0a6e5 examples: improve systemd unit files
Add the PrivateTmp, ProtectHome, and ProtectSystem directives to better
secure the system from chronyd. It's taken from the Debian chrony
package.
2017-01-31 11:22:11 +01:00
Miroslav Lichvar
d96f49f67d test: add keys unit test 2017-01-31 11:22:11 +01:00
Miroslav Lichvar
43ba5d2126 doc: document rekey in chronyc man page
For some reason this useful command was never documented.
2017-01-31 11:22:11 +01:00
Miroslav Lichvar
48f7598fed client: add rekey to help text 2017-01-31 11:22:11 +01:00
Miroslav Lichvar
510b22e96b util: fix more coverity warnings
Coverity doesn't seem to like the new field in the IPAddr struct (used
as explicit padding of the structure) to be left uninitialized, even
though it's never used for anything and is cleared by memset() in
UTI_IPHostToNetwork() before leaving the process.
2017-01-31 11:22:10 +01:00
Miroslav Lichvar
0a0aff14d8 conf: add rawmeasurements log option
While the measurements log can be useful for debugging problems in NTP
configuration (e.g. authentication failures with symmetric keys), it
seems most users are interested only in valid measurements (e.g. for
producing graphs) and don't expect/handle entries where some of the RFC
5905 tests 1-7 failed. Modify the measurements log option to log only
valid measurements, and for debugging purposes add a new rawmeasurements
option.
2017-01-31 11:22:10 +01:00
Miroslav Lichvar
e225ac68bc test: update 110-chronyc 2017-01-27 11:54:12 +01:00
Miroslav Lichvar
58060c40a5 doc: improve FAQ 2017-01-27 11:45:50 +01:00
Miroslav Lichvar
2ac1b3d5c4 client: print tracking delay/dispersion in nanosecond resolution 2017-01-27 11:35:38 +01:00
Miroslav Lichvar
c174566982 ntp: check supported flags before enabling HW timestamping 2017-01-27 11:35:38 +01:00
Miroslav Lichvar
60fca19d40 ntp: log info message when HW timestamping is enabled 2017-01-27 10:55:28 +01:00
Miroslav Lichvar
8bcb15b02f doc: improve description of some server options 2017-01-27 10:55:28 +01:00
Miroslav Lichvar
65c2cebcd5 reference: report zero root dispersion with local reference
The server's precision is supposed to be included in client's
dispersion. Don't include it in the server's dispersion.
2017-01-27 10:55:28 +01:00
Miroslav Lichvar
2a51b45a43 test: fix memory leaks in unit tests 2017-01-27 10:55:28 +01:00
Miroslav Lichvar
5ac791665e doc: update NEWS 2017-01-24 15:03:24 +01:00
Miroslav Lichvar
a4e3f83611 update copyright years 2017-01-24 15:01:38 +01:00
Miroslav Lichvar
8a837f9c2b test: extend 119-smoothtime 2017-01-23 16:17:39 +01:00
Miroslav Lichvar
da2d33e9a8 ntp: fix time smoothing in interleaved mode
When the server's transmit timestamp was updated with a kernel/HW
timestamp, it didn't include the time smoothing offset. If the offset
was larger than one second, the update failed and clients using the
interleaved mode received less accurate timestamps. If the update
succeeded, the clients received timestamps that were not adjusted for
the time smoothing offset, which added an error of up to 0.5s/1s to
their measured offset/delay.

Fix the update to include the smoothing offset in the new timestamp.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
4b98dadae9 ntp: simplify UTI_Ntp64ToTimespec() callers
Since UTI_Ntp64ToTimespec() was modified to handle zero timestamps, some
of its callers don't need to do that anymore.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
86acea5c46 ntp: add interface index to NTP_Local_Address
This will allow us to get the interface index when sending responses to
clients.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
a60fc73e7b refclock_phc: add nocrossts option 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
50f99ec5f4 conf: add nocrossts option to hwtimestamp directive
This option disables the use of the PTP_SYS_OFFSET_PRECISE ioctl.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
31b6a14444 sys_linux: add support for PTP_SYS_OFFSET_PRECISE
This is for hardware that can precisely cross timestamp the PHC with the
system clock.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
9df4d36157 refclock_phc: use sys_linux code for reading PHC
This drops support for non-ioctl reading of PHC.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
b70f0b674f ntp: move PHC-specific code to sys_linux
This will allow sharing of the code with the PHC refclock driver.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
510784077f conf: add minpoll option to hwtimestamp directive 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
9800e397fb hwclock: make minimum sampling separation configurable 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
1436d9961f conf: add precision option to hwtimestamp directive 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
98f5d05925 ntp: include precision of PHC readings in their selection
Include a fixed non-zero precision (100 nanosecond) in the selection of
PHC readings.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
7a937c7652 conf: return hwtimestamp data in struct 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
b198d76676 ntp: include precision in maxdelay test 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
97d4203354 ntp: adapt sampling separation for short polling intervals 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
beaaaad162 ntp: allow sub-second polling intervals
Change the minimum minpoll to -4, but keep the minimum maxpoll at 0 in
order to not make it too easy to flood distant servers.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
4e78975909 ntp: use current poll when backing off on KoD RATE 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
99147ed8f2 ntp: rename maxdelay constants 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
dec0d3bfc2 ntp: reset ntpdata report on address change 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
cd84c99e70 examples: improve chronyd.service 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
d5c507975c doc: update README 2017-01-12 16:34:28 +01:00
Miroslav Lichvar
b4235abd36 update copyright years 2017-01-12 16:34:28 +01:00
Miroslav Lichvar
1966085a97 test: add ntp_core unit test 2017-01-12 16:34:28 +01:00
Miroslav Lichvar
e31e7af48f test: make 119-smoothtime more reliable 2017-01-12 16:34:28 +01:00
Miroslav Lichvar
adb9123fc3 test: extend util unit test 2017-01-12 16:34:28 +01:00
Miroslav Lichvar
b0f7efd59e util: handle zero in conversion of NTP timestamps
Handle zero NTP timestamp in UTI_Ntp64ToTimespec() as a special value to
make it symmetric with UTI_TimespecToNtp64(). This is needed since
commit d75f6830f1, in which a timestamp is
converted back and forth without checking for zero.

It also makes zero NTP timestamps more apparent in debug output.
2017-01-12 16:34:28 +01:00
Miroslav Lichvar
e28dfada8c rtc: check for backward RTC steps
When accumulating a new sample, check if the new RTC time is newer the
last sample time. If it is not, discard all previous samples, assuming
something has stepped the RTC, or it's a broken RTC/driver.
2017-01-12 16:34:28 +01:00
Miroslav Lichvar
ac0b28cce6 sourcestats: align sample time used for source report
This reduces leak of sample times (and receive timestamps which are
related to sample times), which could be useful in off-path attacks on
unauthenticated symmetric interleaved mode.
2017-01-12 16:34:28 +01:00
Miroslav Lichvar
48b16ae66c local: add assertion for precision 2017-01-12 16:34:28 +01:00
Miroslav Lichvar
061579ec28 ntp: don't send packets with RX equal to TX
Before sending an NTP packet, check whether the TX timestamp is not
equal to the RX timestamp. If it is, generate a new TX timestamp and try
again. This is extremely unlikely to happen in normal operation, but it
is needed for reliable detection of the interleaved mode.
2017-01-12 16:34:28 +01:00
Miroslav Lichvar
f2f834e7e7 ntp: limit maxdelay parameters 2017-01-12 16:34:27 +01:00
Miroslav Lichvar
a7802e9a76 fix some coverity warnings 2017-01-12 16:34:27 +01:00
Miroslav Lichvar
8f7ab95ff0 doc: update NEWS 2017-01-06 13:12:19 +01:00
Miroslav Lichvar
042c670747 doc: improve chrony.conf man page 2017-01-06 13:12:19 +01:00
Miroslav Lichvar
cacbe9976f ntp: add options for compensating HW timestamping errors 2017-01-06 13:12:19 +01:00
Miroslav Lichvar
8efec1d640 ntp: add sanity check for HW timestamps
Accept HW timestamp only if it doesn't differ from the kernel/daemon
timestamp by more than one second.
2017-01-06 13:12:19 +01:00
Miroslav Lichvar
c44d282f0b ntp: ignore zero HW timestamps
Apparently, zero HW timestamps are possible with buggy drivers/HW.
2017-01-06 13:12:19 +01:00
Miroslav Lichvar
4432f29bd2 sources: try to replace jittery sources
Similarly to falsetickers, distant, and unreachable sources, try to
replace sources that have jitter larger than maxjitter.
2017-01-06 13:12:19 +01:00
Miroslav Lichvar
5fee3ed5e9 client: print refid also as string in ntpdata output 2017-01-06 13:12:19 +01:00
Miroslav Lichvar
b76ea64263 ntp: log warning when KoD RATE is received in non-burst mode 2017-01-06 13:12:19 +01:00
Miroslav Lichvar
ed904f08a4 hwclock: return timestamp error
For now, when converting a raw timestamp, return error of the last
sample as the maximum error of the timestamp. This is needed to include
the PHC reading delay in the NTP dispersion.
2017-01-06 13:12:19 +01:00
Miroslav Lichvar
96cc80ffc8 ntp: improve dispersion calculation
Instead of adding precision (sum of the local and remote precision) to
the TX and RX timestamp error, include only the maximum.
2017-01-06 13:12:19 +01:00
Miroslav Lichvar
ab99373cfc conf: change default rate limiting parameters
Change the default NTP rate limiting leak to 2 (25%). Change the default
command rate limiting interval to -4 (16 packets per second) and burst
to 8, so the interval is the only difference between NTP and command
rate limiting defaults.
2017-01-06 13:12:19 +01:00
Miroslav Lichvar
dbfb49384b clientlog: disable NTP response rate limiting by default
This reverts commit 50022e9286.

Testing showed that ntpd as an NTP client performs poorly when it's
getting only 25% of responses. At least for now, disable rate limiting
by default again.
2017-01-06 13:12:18 +01:00
Miroslav Lichvar
14bb9f29a3 ntp: calculate delay relative to local frequency
This should be more accurate as local frequency is usually
combined from multiple sources. This is a partial revert of commit
23a4e8b38d.
2017-01-06 13:12:18 +01:00
Miroslav Lichvar
16519ee2cc doc: update NEWS 2016-12-15 13:47:41 +01:00
Miroslav Lichvar
50022e9286 clientlog: enable NTP response rate limiting by default
Change the default interval of both NTP and command rate limiting to -10
(1024 packets per second) and the burst to 16. The default NTP leak is 2
(rate limiting is enabled by default) and the default command leak is 0
(rate limiting is disabled by default).
2016-12-15 13:47:41 +01:00
Miroslav Lichvar
5059019535 clientlog: randomize alignment of log timestamps 2016-12-15 13:47:41 +01:00
Miroslav Lichvar
c6a38f5069 clientlog: allow very short rate limiting intervals
Support negative token shift to allow coarse rate limiting with
intervals down to -19.
2016-12-15 13:47:41 +01:00
Miroslav Lichvar
11ed197663 configure: don't use recvmmsg() on FreeBSD
Don't try recvmmsg() on FreeBSD, at least for now. It is broken on
FreeBSD 11.0 and it's just a wrapper around recvmsg().
2016-12-15 13:47:41 +01:00
Miroslav Lichvar
5634e6b963 doc: improve hwtimestamp description 2016-12-14 16:19:35 +01:00
Miroslav Lichvar
db312a5ff6 ntp: allow wildcard in hwtimestamp directive
If "*" was specified, use getifaddrs() to get a list of all interfaces,
and try to enable HW timestamping on all of them.
2016-12-14 16:19:35 +01:00
Miroslav Lichvar
88c31b3785 client: improve ntpdata output 2016-12-14 16:19:35 +01:00
Miroslav Lichvar
967f3e4f77 client: don't require address in ntpdata command
If no address is specified, use the SOURCE_DATA command to get addresses
of NTP sources, and request NTP_DATA for all of them.
2016-12-14 16:19:35 +01:00
Miroslav Lichvar
2e311d1766 sourcestats: add upper bound for skew 2016-12-14 16:19:35 +01:00
Miroslav Lichvar
11f7cc0507 examples: avoid Unix domain socket in chrony-wait service
Use the -h option to force chronyc to use internet socket instead of
Unix domain as the access to the socket may be blocked by SELinux and
trying to open it generates SELinux warnings.
2016-12-13 12:57:25 +01:00
Miroslav Lichvar
a4f28892a5 cmdmon: update protocol changelog 2016-12-13 12:57:25 +01:00
Miroslav Lichvar
5bc53741be sourcestats: add lower bound for std dev used for weighting 2016-12-13 12:57:25 +01:00
Miroslav Lichvar
95a4f33265 sourcestats: save asymmetry run in dump files
This allows the asymmetry correction to be applied right after restart.
2016-12-13 12:57:25 +01:00
Miroslav Lichvar
fac1093ebf cmdmon: add reserved fields to ntpdata reply
This might be useful if ntpdata is changed to not require authorization
and new fields need to be added without breaking compatibility.
2016-12-13 12:57:25 +01:00
Miroslav Lichvar
1b1384ccaa nameserv: set CLOEXEC flag on pipe file descriptors 2016-12-13 12:57:25 +01:00
Miroslav Lichvar
0c9a19ded5 stubs: rework emulation of asynchronous resolver to use pipes
With a larger number of configured servers, the handler of the emulated
resolver repeatedly scheduled timeout of zero, which triggered the
infinite loop detection in the scheduler and caused abort. This bug was
introduced in commit 967e358dbc.

Rework the code to use pipes instead of timeouts to avoid this problem.
2016-12-13 12:57:25 +01:00
Miroslav Lichvar
b7bd7469b7 ntp: disable maxdelayratio in interleaved/symmetric mode
It's too unreliable and the maxdelaydevratio test should work better
anyway.
2016-12-13 12:57:24 +01:00
Miroslav Lichvar
9568ff3f06 doc: update NEWS 2016-12-09 09:04:25 +01:00
Miroslav Lichvar
742ddcce11 doc: update README 2016-12-08 16:26:34 +01:00
Miroslav Lichvar
e72cc9e3da test: update 119-smoothtime 2016-12-08 16:25:46 +01:00
Lonnie Abelbeck
3156e5a293 client: add tab-completion with libedit/readline 2016-12-08 15:32:51 +01:00
Miroslav Lichvar
9a901e1cb0 refclock: make maximum lock age configurable
The maxlockage option specifies in number of pulses how old can be
samples from the refclock specified by the lock option to be paired with
the pulses. Increasing this value is useful when the samples are
produced at a lower rate than the pulses.
2016-12-08 14:47:38 +01:00
Miroslav Lichvar
8c11044ee2 refclock: slew last sample even after it was used
It may be needed by locked PPS refclocks.
2016-12-08 14:47:38 +01:00
Miroslav Lichvar
a75d2db75b test: add scan-build compilation test 2016-12-08 14:47:38 +01:00
Miroslav Lichvar
6aac72fd80 configure: use common CPPFLAGS for all objects 2016-12-08 14:47:38 +01:00
Miroslav Lichvar
b692cb720c configure: fix help text 2016-12-08 14:47:38 +01:00
Miroslav Lichvar
25102489f5 ntp: fix clang warning 2016-12-08 14:47:38 +01:00
Miroslav Lichvar
a2d2cad384 hwclock: fix check of sample separation 2016-12-08 14:47:38 +01:00
Miroslav Lichvar
859e0c2323 ntp: add TX error to dispersion 2016-12-08 14:47:36 +01:00
Miroslav Lichvar
e62a39cafe ntp: fix RX error added to dispersion in interleaved mode 2016-12-08 14:47:33 +01:00
Miroslav Lichvar
8bbb8fa062 sources: add configurable limit for jitter
The maxjitter directive sets the maximum allowed jitter of the sources
to not be rejected by the source selection algorithm. This prevents
synchronisation with sources that have a small root distance, but their
time is too variable. By default, the maximum jitter is 1 second.
2016-12-08 14:20:00 +01:00
Miroslav Lichvar
68039e0d14 sourcestats: save variance as standard deviation
This reduces the number of sqrt() calls.
2016-12-06 16:56:38 +01:00
Miroslav Lichvar
65fd30a547 cmdmon: allow all parameters to be set for new sources
Add missing fields to the REQ_NTP_Source structure and add new versions
of the ADD_SERVER/ADD_PEER commands.
2016-12-06 16:56:38 +01:00
Miroslav Lichvar
23a4e8b38d ntp: rework calculation and testing of peer delay
Instead of a worst-case delay use a mean value and relate it to the
source's time. This makes it more stable in the interleaved and
symmetric modes, which should improve the weighting and asymmetry
correction. Modify the test A and B to work with a minimum estimated
delay (delay - dispersion).
2016-12-06 16:56:38 +01:00
Miroslav Lichvar
979b53866d client: print addresses with refids in ntpdata report 2016-12-06 16:56:38 +01:00
Miroslav Lichvar
46061d8eec client: fix truncation of long hostnames 2016-12-06 16:56:38 +01:00
Miroslav Lichvar
946ee8f611 client: fix format specifier for poll in ntpdata report 2016-12-06 16:56:38 +01:00
Miroslav Lichvar
7f757f09ce client: fix add command
The default version changed to 0 (autoselect).
2016-12-06 16:56:38 +01:00
Miroslav Lichvar
9ba8a33966 sys_linux: allow openat in seccomp filter 2016-12-06 16:56:38 +01:00
Miroslav Lichvar
492940568d main: add -t option to usage text 2016-12-06 16:56:38 +01:00
Miroslav Lichvar
b95c2a3f78 configure: rename SOCKDIR to RUNDIR 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
53b661b59d regress: remove unused struct declaration 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
a049c9e0f8 conf: increase default minsamples and polltarget
Change default minsamples to 6 and polltarget to 8. This should improve
stability with extremely small jitters (e.g. HW timestamping) and not
decrease time accuracy at minimum polling interval too much.
2016-12-02 14:53:03 +01:00
Miroslav Lichvar
3513484852 main: add -t option to chronyd
This option sets a timeout (in seconds) after which chronyd will exit.
If the clock is not synchronised, it will exit with a non-zero status.
This is useful with the -q or -Q option to shorten the maximum time
waiting for measurements, or with the -r option to limit the time when
chronyd is running, but still allow it to adjust the frequency of the
system clock.
2016-12-02 14:53:03 +01:00
Miroslav Lichvar
2d67871bbf ntp: don't make client log entries for broadcast TX 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
e6e9a472db ntp: avoid truncation of NTPv4 MACs by default
If the MAC in NTPv4 requests would be truncated, use version 3 by
default to avoid the truncation. This is necessary for compatibility
with older chronyd servers, which do not respond to messages with
truncated MACs.
2016-12-02 14:53:03 +01:00
Miroslav Lichvar
1d5d768545 test: extend 105-ntpauth 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
6c8588c13c ntp: truncate MACs in NTPv4 packets
When sending an NTPv4 packet, truncate long MAC to 192 bits to follow
RFC 7822.
2016-12-02 14:53:03 +01:00
Miroslav Lichvar
89b127bf6c ntp: accept NTPv4 packets with truncated MACs
In order to allow deterministic parsing of NTPv4 extension fields, the
MAC must not be longer than 192 bits (RFC 7822). One way to get around
this limitation when using symmetric keys which produce longer MACs is
to truncate them to 192 bits (32-bit key ID and 160-bit hash).

Modify the code to accept NTPv4 packets with MACs truncated to 192
bits, but still allow long MACs in NTPv4 packets to not break
compatibility with older chrony clients.
2016-12-02 14:53:03 +01:00
Miroslav Lichvar
38c4a7ff97 keys: add support for checking truncated MACs 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
2f5b4aea91 util: move authentication and password decoding functions to keys
This doesn't need to be included in chronyc.
2016-12-02 14:53:03 +01:00
Miroslav Lichvar
4fc6a1b424 doc: update FAQ 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
6b3800cc94 doc: update man pages 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
633a007b7b doc: update README 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
756c2e9afb ntp: fix length modifier of refid in measurements log 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
27ea58d5fd client: zero pad reference ID 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
64f9205189 client: add ntpdata command 2016-11-25 17:33:43 +01:00
Miroslav Lichvar
535ca64bba cmdmon: add ntpdata command 2016-11-25 17:33:43 +01:00
Miroslav Lichvar
7255f9ef74 client: fix format specifiers in sourcestats report 2016-11-25 17:33:43 +01:00
Miroslav Lichvar
cdb0b6124f client: add new format specifiers to print_report() 2016-11-25 17:33:43 +01:00
Miroslav Lichvar
5fb1107cc7 client: print reference ID in hexadecimal
This is an incompatible change in the output of the tracking command,
which may break some scripts, but it's necessary to avoid confusion with
IPv4 addresses when synchronised to an IPv6 server or reference clock.
2016-11-25 17:33:43 +01:00
Miroslav Lichvar
1045adaa88 sources: give access to sourcestats instance
Give access to the sourcestats instance and remove all functions that
just translated to SST calls.
2016-11-25 17:33:43 +01:00
Miroslav Lichvar
ed286f3617 ntp: add new debug message 2016-11-25 17:33:42 +01:00
Miroslav Lichvar
9f9dd7948b ntp: fix logging of RX timestamp source in interleaved mode 2016-11-25 17:33:42 +01:00
Miroslav Lichvar
9c760de676 ntp: don't send presend packets in burst mode 2016-11-25 17:33:42 +01:00
Miroslav Lichvar
90229984cf ntp: allow presend of zero
Don't use zero as a special value for disabled and change the default
presend to a value larger than any valid poll.
2016-11-25 17:33:42 +01:00
Miroslav Lichvar
2b3d64c31d ntp: send two presend packets in interleaved mode
In a burst of three requests (two presend + one normal) the server can
detect the client is using the interleaved mode and save the transmit
timestamp of the second response for the third response. This shortens
the interval in which the server has to keep the state.
2016-11-25 17:33:42 +01:00
Miroslav Lichvar
d23c647e34 ntp: shorten presend delay to 2 seconds 2016-11-25 17:33:42 +01:00
Miroslav Lichvar
2408bbcd77 ntp: process presend responses
Rework the code to make a real request for presend and process the
response, but don't accumulate the sample. This allows presend to work
in the interleaved client mode.
2016-11-25 17:33:42 +01:00
Miroslav Lichvar
d75f6830f1 reference: randomize reference time
In unauthenticated interleaved symmetric NTP mode we should be now
careful with the reference timestamp as it may be useful with the peer
delay for estimating the local receive timestamp and increasing the
chance of spoofing a valid response from the peer.

When updating the reference time, add a random error of up to one second
to make it less sensitive when disclosed to NTP and cmdmon clients.
2016-11-25 17:33:42 +01:00
Miroslav Lichvar
4d7eb2f7a6 ntp: don't reset polling interval when switching to/from online
This allows chronyd to ramp up the polling interval even when the source
is frequently switched between the online and offline modes.
2016-11-25 17:33:42 +01:00
Miroslav Lichvar
3a67dedad6 ntp: fix calculation of PHC sample time 2016-11-23 10:08:36 +01:00
Miroslav Lichvar
518837e17a sys_linux: allow ioctls used with HW timestamping in seccomp filter 2016-11-23 09:24:05 +01:00
Miroslav Lichvar
c7e778757a ntp: transpose HW RX timestamps
We need to transpose HW RX timestamps as HW timestamps are normally
preamble timestamps and RX timestamps in NTP are supposed to be trailer
timestamps. Without raw sockets we don't know the length of the packet
at layer 2, so we make an assumption that UDP data start at the same
position as in the last transmitted packet which had a HW TX timestamp.
2016-11-22 16:15:35 +01:00
Miroslav Lichvar
c45be946ce Merge branch '2.4-stable' into HEAD 2016-11-22 16:06:05 +01:00
Miroslav Lichvar
258bcc21b8 refclock: don't compare sample time with samples from previous poll
This is an improvement of commit 8f85291d23.
2016-11-22 15:58:02 +01:00
Miroslav Lichvar
db286ca6ea doc: update NEWS 2016-11-21 12:03:45 +01:00
Miroslav Lichvar
85fbfd9b15 sources: add new status for sources that overlap trusted sources
Sources that overlap trusted sources should be displayed in the chronyc
sources report with the '-' symbol and they shouldn't trigger a
replacement.
2016-11-21 12:03:45 +01:00
Miroslav Lichvar
b819c7fe55 refclock: don't compare sample time with samples from previous poll
This is an improvement of commit 0a848e2528.
2016-11-21 12:03:27 +01:00
Miroslav Lichvar
2b5c86b9a3 refclock: fix check for old samples
The fix in commit 0a848e2528 was
incorrect.
2016-11-21 12:03:15 +01:00
Miroslav Lichvar
0a848e2528 refclock: require new samples to have newer timestamp
If all or most SHM/SOCK samples collected in a polling interval had the
same local timestamp, the dispersion could end up as nan, which could
trigger an assert failure later in the code.

Before accumulating a refclock sample, check if the timestamp is newer
than the previous one.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
b443ec5ea5 test: add smooth unit test 2016-11-21 12:02:51 +01:00
Miroslav Lichvar
37d1467368 smooth: fix selection of 1st stage direction
When the smoothing process is updated with extremely small (e.g.
sub-nanosecond) values, both directions may give a negative length of
the 1st or 3rd stage due to numerical errors and the selection will fail
an in assertion. Rework the code to select the direction which gives a
smaller error.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
1d9d19d76b client: flush stdout after printing prompt
Apparently fgets() doesn't flush stdout in some libc implementations.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
9603f0552a client: fix printing of negative poll in sources report again
This was broken in commit 3f51805e62.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
12befc2afd ntp: fix processing of kernel timestamps on non-Linux systems
When the SO_TIMESTAMP socket option was enabled, the expected type of
control messages containing timestamps was SO_TIMESTAMP instead of
SCM_TIMESTAMP. This worked on Linux, where the two values are equal, but
not on the other supported systems. The timestamps were ignored and this
probably worsened the accuracy and stability of the synchronisation.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
78f20f7b3e conf: fix parsing of refclock directive
Don't accept refclock directive which has as the last argument an option
that requires a value.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
875b0e262c ntp: add debug message for truncated control messages 2016-11-15 14:55:25 +01:00
Miroslav Lichvar
8823e2b064 ntp: ignore truncated messages
Don't waste time with processing messages that don't fit in the receive
buffer as they most likely wouldn't pass the format check due to an
invalid length of an extension field.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
5b2caf48dc hwclock: fix order of samples
In order to trim oldest samples in the regression function, they need to
be sorted in the data arrays from the oldest to newest.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
7ec048ce7f ntp: detect unexpected TX updates of unknown sources 2016-11-15 14:55:25 +01:00
Miroslav Lichvar
cfb3c3ba44 ntp: improve replay protection in symmetric mode
Always allow update from the first valid response, even if its transmit
timestamp is not newer than the currently saved timestamp. This shoud
provide a temporary protection in the case where the attacker does have
an authenticated packet from future, but the peers are using the same
polling interval and the protocol is already synchronised. This could be
also useful in the case where the attacker cannot observe the traffic
and authentication is disabled.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
4b0ef09221 sched: add more random bits to timeout scheduling
Extend the random value which is included in the calculation of the
delay from 16 to 32 bits. This makes scheduling of NTP transmissions
random to one microsecond for polling intervals up to 17.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
74f581e7ab client: randomize sequence number in requests
Don't rely on random source port of a connected socket alone as a
protection against spoofed packets in chronyc. Generate a fully random
32-bit sequence number for each request and modify the code to not send
a new request until the timeout expires or a valid response is received.
For a monitoring protocol this should be more than good enough.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
07aa54b183 client: fix attempt number in requests to be in network order 2016-11-15 14:55:25 +01:00
Miroslav Lichvar
00da177e51 report: remove unused definition 2016-11-15 14:55:25 +01:00
Miroslav Lichvar
6e9bfac07d sources: add new status for sources that overlap trusted sources
Sources that overlap trusted sources should be displayed in the chronyc
sources report with the '-' symbol and they shouldn't trigger a
replacement.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
06f93e7bf0 sources: don't log warning when opening dump file fails
Instead of complaining when the file doesn't exist, which is common when
using pool servers, log an informational message when the file is
loaded.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
d84a706c08 conf: create socket directory before logdir and dumpdir
This allows sharing of the same directory for sockets, logs and dumps as
the socket directory needs to be created first (with mode 0770) in order
to pass the check of the permissions.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
ea58a1e72c ntp: print offset and delay in debug messages in nanosecond resolution 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
5c691a5460 ntp: fix remote poll in measurements log
Write the poll value from the received packet instead of the saved
value, which doesn't have to be always updated.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
2c877fa149 ntp: add new fields to measurements log
Include reference ID, NTP mode and source of the local transmit and
receive timestamp in the measurements log.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
33053a5e14 ntp: add partial protection against replay attacks on symmetric mode
A recently published paper [1] (section VIII) describes a DoS attack
on symmetric associations authenticated with a symmetric key where the
attacker can only observe and replay packets. Although the attacker
cannot prevent packets from reaching the other peer (not even by
flooding the network for example), s/he has the same power as a MitM
attacker.

As the authors explain, this is a fundamental flaw of the protocol,
which cannot be fixed in the general case. However, we can at least try
to protect associations in a case where the peers use the same polling
interval (i.e. for each request is expected one response) and all peers
that share the symmetric key never start with clocks in future or very
distant past (i.e. the attacker does not have any packets from future
that could be replayed).

Require that updates of the NTP state between requests have increasing
transmit timestamp and when a packet that passed all NTP tests to be
considered a valid response was received, don't allow any more updates
of the state from packets that don't pass the tests. This should ensure
the last update of the state is from the first time the last real
response was received and still allow the protocol to recover in case
one of the peers steps its clock back or the attacker does have a packet
from future and the attack stops.

[1] Aanchal Malhotra, Matthew Van Gundy, Mayank Varia, Haydn Kennedy,
    Jonathan Gardner, and Sharon Goldberg. The Security of NTP's
    Datagram Protocol. https://eprint.iacr.org/2016/1006
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
8662652192 ntp: disable presend in symmetric and interleaved modes
The presend packet can't be used in symmetric and interleaved modes as
it breaks the protocol with unexpected packets.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
227c7e60a4 test: add util unit test 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
6e9c04896b util: add functions for zeroing and comparing NTP timestamps 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
0e273939d2 ntp: fix poll value in broadcast mode packets
Set poll in broadcast mode packets to the rounded log2 value of the
actual interval instead of a hardcoded value.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
14647032b2 doc: update chrony.conf man page for recent changes 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
14a1059e43 ntp: add support for HW timestamping on Linux
Add a new directive to specify interfaces which should be used for HW
timestamping. Extend the Linux ntp_io initialization to enable HW
timestamping, configure the RX filter using the SIOCSHWTSTAMP ioctl,
open their PHC devices, and track them as hwclock instances. When
messages with HW timestamps are received, use the PTP_SYS_OFFSET ioctl
to make PHC samples for hwclock.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
4449259d88 ntp: read interface index from control messages 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
01e5ea7d31 test: add 122-xleave 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
94522bfed1 test: add hwclock unit test 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
9bdd35c9fa hwclock: add support for tracking hardware clocks
Add a general support for tracking independent hardware clocks like PTP
hardware clocks (PHC) or real-time clocks (RTC).
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
d366530699 clientlog: move status check to get_record() 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
96d652e5bd ntp: add support for interleaved client/server mode
Adapt the interleaved symmetric mode for client/server associations.
On server, save the state needed for detection and responding in the
interleaved mode in the client log. On client, enable the interleaved
mode when the server is specified with the xleave option. Always accept
responses in basic mode to allow synchronization with servers that
don't support the interleaved mode, have too many clients, or have
multiple clients behing the same IP address. This is also necessary to
prevent DoS attacks on the client by overwriting or flushing the server
state. Protect the client's state variables against replay attacks as
the timestamps are now needed when processing the subsequent packet.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
bd736f9234 ntp: check also NTP receive timestamp when updating TX timestamp 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
90b25f5b83 ntp: add support for interleaved symmetric mode
Add xleave option to the peer directive to enable an interleaved mode
compatible with ntpd. This allows peers to exchange transmit timestamps
captured after the actual transmission and significantly improve
the accuracy of the measurements.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
997406fe47 ntp: add support for software timestamping on Linux
Enable SCM_TIMESTAMPING control messages and the socket's error queue in
order to receive our transmitted packets with a more accurate transmit
timestamp. Add a new file for Linux-specific NTP I/O and implement
processing of these messages there.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
14c8f07629 ntp: save source of local timestamps
Introduce a new structure for local timestamps that will hold the
timestamp with its estimated error and also its source (daemon, kernel
or HW). While at it, reorder parameters of the functions that accept the
timestamps.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
8f6a1b5318 ntp: add support for processing of transmitted packets
Add new functions for processing of packets after they are actually
sent by the kernel or HW in order to get a more accurate transmit
timestamp. Rename old functions for processing of received packets and
their parameters to make the naming more consistent.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
a8c6bea2d5 sys_linux: add function for checking kernel version 2016-11-10 15:26:55 +01:00
Miroslav Lichvar
19fde8f49c refclock: fix check for old samples
The fix in commit 8f85291d23 was
incorrect.
2016-10-07 11:03:59 +02:00
Miroslav Lichvar
8f85291d23 refclock: require new samples to have newer timestamp
If all or most SHM/SOCK samples collected in a polling interval had the
same local timestamp, the dispersion could end up as nan, which could
trigger an assert failure later in the code.

Before accumulating a refclock sample, check if the timestamp is newer
than the previous one.
2016-10-06 16:09:24 +02:00
Miroslav Lichvar
9c48166e90 ntp: inline send_packet()
Also, reuse existing function for checking server sockets.
2016-09-26 12:40:44 +02:00
Miroslav Lichvar
b536296c05 ntp: use ipi_addr from struct in_pktinfo as local address
Use the ipi_addr field instead of ipi_spec_dst as the local address
after recvmsg() to be consistent with the processing of struct
in6_pktinfo. This may make a difference for messages from the error
queue.
2016-09-26 12:40:44 +02:00
Miroslav Lichvar
d36c522453 ntp: check for missing source address after recvmsg() 2016-09-26 12:40:44 +02:00
Miroslav Lichvar
2577e20f09 ntp: fix updating of transmit delay in symmetric mode
This was broken in commit cea21adbbb.
2016-09-26 12:40:43 +02:00
Miroslav Lichvar
c169ad3f58 sched: add support for handling exceptions on descriptors 2016-09-26 12:40:43 +02:00
Miroslav Lichvar
411f4697ca sys_linux: allow getdents in seccomp filter
This is needed for glob(), which is used with the include and dumpdir
directives.
2016-09-26 12:40:43 +02:00
Miroslav Lichvar
6c5de8dcb0 refclock: use UTI_TimespecToString() in debug message 2016-09-26 12:40:43 +02:00
Miroslav Lichvar
c8373f1649 util: add UTI_IsZeroTimespec() 2016-09-26 12:40:43 +02:00
Miroslav Lichvar
45f86122fa test: add smooth unit test 2016-09-12 13:13:30 +02:00
Miroslav Lichvar
c0a8afdb68 smooth: fix selection of 1st stage direction
When the smoothing process is updated with extremely small (e.g.
sub-nanosecond) values, both directions may give a negative length of
the 1st or 3rd stage due to numerical errors and the selection will fail
an in assertion. Rework the code to select the direction which gives a
smaller error.
2016-09-12 13:13:14 +02:00
Miroslav Lichvar
1afb285aad sched: initialize sub-second part of saved_tv in SCH_MainLoop()
This is needed since commit d0dfa1de9e to
avoid valgrind errors.
2016-09-12 12:49:18 +02:00
Miroslav Lichvar
c08e7e716d use correct facility in LOG messages 2016-09-07 11:16:01 +02:00
Miroslav Lichvar
a06a5f1baa sources: remove dump files on start
When chronyd is starting, after the point where dump files are loaded,
remove all files in the dump directory that match the naming scheme used
for dump files. This prevents loading stale dump files that were not
saved in the latest run of chronyd.
2016-09-07 11:16:01 +02:00
Miroslav Lichvar
fb5d4f1da4 conf: disable dumpdir and logdir by default
Use empty string instead of "." (which is normally the root directory)
as the default value of dumpdir and logdir to indicate they are not
specified. Print warnings in syslog when trying to log or dump
measurements without dumpdir or logdir.
2016-09-07 11:16:01 +02:00
Miroslav Lichvar
d2e5b41369 client: flush stdout after printing prompt
Apparently fgets() doesn't flush stdout in some libc implementations.
2016-09-07 11:16:01 +02:00
Miroslav Lichvar
4b6b6e5cba client: remove out of date comment 2016-09-07 11:16:01 +02:00
Miroslav Lichvar
27b4c396d0 client: fix printing of negative poll in sources report again
This was broken in commit 3f51805e62.
2016-09-07 11:16:01 +02:00
Miroslav Lichvar
41eb5b79cb client: check address in waitsync command 2016-09-07 11:16:01 +02:00
Miroslav Lichvar
23cf74d5c7 util: convert invalid addresses as IPADDR_UNSPEC 2016-09-07 11:15:57 +02:00
Miroslav Lichvar
1a038bfd50 test: add 011-asymjitter 2016-09-06 15:48:59 +02:00
Miroslav Lichvar
dd02d67224 test: add support for testing with asymmetric jitter 2016-09-06 15:48:59 +02:00
Miroslav Lichvar
648bf8bd3e test: extend 113-leapsecond 2016-09-06 15:48:59 +02:00
Miroslav Lichvar
82c4bfe5d2 sources: include trust option in leap second voting
When sources specified with the trust option pass the source selection,
ignore other sources in the vote of leap second status.
2016-09-06 15:48:59 +02:00
Miroslav Lichvar
98ba4ce4d5 configure: add options to set default pidfile and rtcdevice 2016-08-22 15:50:35 +02:00
Bryan Christianson
f63e414024 configure: add option --without-clock-gettime
clock_gettime() will be ignored even if it is present
2016-08-22 15:50:35 +02:00
Miroslav Lichvar
a8886603c2 ntp: fix processing of kernel timestamps on non-Linux systems
When the SO_TIMESTAMP socket option was enabled, the expected type of
control messages containing timestamps was SO_TIMESTAMP instead of
SCM_TIMESTAMP. This worked on Linux, where the two values are equal, but
not on the other supported systems. The timestamps were ignored and this
probably worsened the accuracy and stability of the synchronisation.
2016-08-22 15:50:35 +02:00
Miroslav Lichvar
4f10144b09 ntp: add corrected delay to debug message in process_receive() 2016-08-22 15:50:28 +02:00
Miroslav Lichvar
af664e6cec sourcestats: return success when loading dump file with no samples 2016-08-22 15:05:48 +02:00
Miroslav Lichvar
c30816eb65 sourcestats: remove warning messages from SST_LoadFromFile()
There is a generic error message in SRC_ReloadSources().
2016-08-22 15:05:48 +02:00
Miroslav Lichvar
b1accfd0ff sourcestats: make reading/writing dump files Y2106 ready
The sample times were written and read as unsigned long, which would
overflow in year 2016 on platforms that have 32-bit long.
2016-08-22 15:05:48 +02:00
Miroslav Lichvar
5c45e4ccb5 sources: improve naming of dump files
Include IP address instead of reference ID in the name of dump file
for NTP sources and for reference clocks format the reference ID as a
hexadecimal number instead of quad dotted notation.

Also, avoid dynamic memory allocation and improve warning messages.
2016-08-22 15:05:02 +02:00
Miroslav Lichvar
41cf867738 sourcestats: update regression after loading dump file
Call SST_DoNewRegression() immediately in SST_LoadFromFile instead of
relying on SRC_ReloadSources().
2016-08-19 18:25:02 +02:00
Bryan Christianson
02844e9b01 local: fix typo in strerror() call 2016-08-19 18:25:02 +02:00
Miroslav Lichvar
7a1ebc3467 ntp: add support for SO_TIMESTAMPNS socket option
Enable the SO_TIMESTAMPNS option to get kernel timestamps in nanosecond
resolution.
2016-08-19 13:55:20 +02:00
Miroslav Lichvar
8d89610ff6 local: add support for clock_gettime()
Use clock_gettime() to read the system clock in nanosecond resolution.
2016-08-19 13:54:58 +02:00
Miroslav Lichvar
cfe706f032 util: modify UTI_*ToDouble functions to return double directly 2016-08-19 12:53:09 +02:00
Miroslav Lichvar
99cc94529d util: rename functions dealing with integers in NTP format
This should prevent confusion with int32_t, int64_t and other types.
2016-08-19 12:53:09 +02:00
Miroslav Lichvar
d0dfa1de9e adopt struct timespec
Replace struct timeval with struct timespec as the main data type for
timestamps. This will allow the NTP code to work with timestamps in
nanosecond resolution.
2016-08-19 12:53:09 +02:00
Miroslav Lichvar
0899ab52dd util: return normalised timevals 2016-08-19 11:33:38 +02:00
Miroslav Lichvar
71e0ebcb6b ntp: don't send crypto-NAKs
Crypto-NAK is useful only with Autokey where it allows quick reset
of the association. There is no plan to support Autokey and NTS will
specify its own message for authentication errors.
2016-08-17 11:54:34 +02:00
Miroslav Lichvar
e488371b01 sourcestats: report asymmetry in statistics log 2016-08-11 11:24:48 +02:00
Miroslav Lichvar
39f34eb674 sourcestats: correct offsets with asymmetric network jitter
Estimate asymmetry of network jitter on the path to the source as a
slope of offset against network delay in multiple linear regression. If
the asymmetry is significant and its sign doesn't change frequently, the
measured offsets (which are used later to estimate the offset and
frequency of the clock) are corrected to correspond to the minimum
network delay. This can significantly improve the accuracy and stability
of the estimated offset and frequency.
2016-08-11 11:24:48 +02:00
Miroslav Lichvar
9d9d6c30cf sourcestats: add debug message for regression results 2016-08-11 10:45:48 +02:00
Miroslav Lichvar
27d59e54cc regress: add linear regression with two independent variables 2016-08-11 10:45:48 +02:00
Miroslav Lichvar
507a01ab17 sourcestats: extend array holding peer delays
Keep the same number of peer delays as offsets. This will be needed when
peer delay is included in offset correction.
2016-08-11 10:45:48 +02:00
Miroslav Lichvar
f7b8cd1a09 regress: save arrays of constants in single-precision
No need to waste space on double precision. Also, declare them as const.
2016-08-11 10:45:48 +02:00
Bryan Christianson
8bc48af630 rename 'Mac OS X' to 'macOS'
From the the release of macOS Sierra (Version 10.12) the Macintosh
operating system is called 'macOS'
2016-08-11 10:45:48 +02:00
Miroslav Lichvar
b0838280a9 ntp: reset tentative flag only when sample was accumulated
When selecting sources from a pool, ignore responses which didn't
produce a new sample. Sources with acceptable delay (as configured by
the maxdelay* options) should be prefered.
2016-08-11 10:45:48 +02:00
Miroslav Lichvar
cea21adbbb ntp: close client sockets sooner with unsynchronised sources
When a valid packet is received from an unsynchronised source (i.e. only
a test of leap, stratum or root distance failed), there is no point in
waiting for another packet or the RX timeout, and the client socket can
be immediately closed.
2016-08-11 10:45:48 +02:00
Miroslav Lichvar
c619d555f0 test: add 010-multrecv 2016-08-11 10:45:48 +02:00
Miroslav Lichvar
e306199588 ntp: add support for recvmmsg()
This is used to read multiple packets with one system call. It should
work on Linux and NetBSD.
2016-08-11 10:45:48 +02:00
Miroslav Lichvar
895c15d677 configure: include config.h in test code 2016-08-11 10:45:48 +02:00
Miroslav Lichvar
d18f9ca75a ntp: rework receiving messages
Allocate buffers for received messages on heap instead of stack and
prepare the code for receiving multiple messages at the same time.
2016-08-11 10:45:48 +02:00
Miroslav Lichvar
82e76c39d9 ntp: align buffers for control messages 2016-08-11 10:45:47 +02:00
Miroslav Lichvar
577aed4842 ntp: add support for MS-SNTP authentication in Samba
Add support for authenticating MS-SNTP responses in Samba (ntp_signd).
Supported is currently only the old MS-SNTP authenticator field. It's
disabled by default. It can be enabled with the --enable-ntp-signd
configure option and the ntpsigndsocket directive, which specifies the
location of the Samba ntp_signd socket.
2016-07-29 10:17:33 +02:00
Miroslav Lichvar
2a8ce63fc7 ntp: detect MS-SNTP packets
When a received packet fails to authenticate, check if the digest
contains zeroes and treat it as an MS-SNTP packet with authenticator or
extended authenticator field. For now, discard these packets, i.e. don't
respond with a crypto-NAK.
2016-07-29 10:17:33 +02:00
Miroslav Lichvar
61dd4e0ccb ntp: refactor selection of authentication mode
Replace the flag that enables authentication using a symmetric key with
an enum. Specify crypto-NAK as a special mode used for responses instead
of relying on zero key ID. Also, rework check_packet_auth() to always
save the mode and key ID.
2016-07-29 10:17:13 +02:00
Miroslav Lichvar
8220e51ae4 ntp: check for extension fields only in NTPv4 packets 2016-07-20 12:47:38 +02:00
Miroslav Lichvar
d322c8e6e5 doc: improve description of chronyc -n option 2016-07-20 09:34:11 +02:00
Miroslav Lichvar
3dec266dd5 client: indicate truncated addresses/hostnames
Add symbol > to the end of the resolved hostname or address when it's
truncated.
2016-07-20 09:34:11 +02:00
Miroslav Lichvar
862938cc79 client: truncate long hostnames in clients output 2016-07-20 09:34:11 +02:00
Miroslav Lichvar
ee396702f2 client: print intervals in seconds up to 1200 seconds
This covers random variations in the default maximum polling interval.
2016-07-20 09:34:11 +02:00
Miroslav Lichvar
316d50d6f1 sources: optimize SRC_ReportSource() a bit
Remove unnecessary memset() call and use the default case of the switch
to report the unreachable state.
2016-07-20 09:34:11 +02:00
Miroslav Lichvar
5e92aaf8a5 addressing: pad IPAddr struct explicitly 2016-07-20 09:34:11 +02:00
Miroslav Lichvar
7ffe59a734 util: round up when converting to 32-bit NTP values
NTP clients shouldn't get root delay and dispersion smaller than the
server's values.
2016-07-20 09:17:24 +02:00
Miroslav Lichvar
6cd558398a ntp: add offset option
Add offset option to the server/pool/peer directive. It specifies a
correction which will be applied to offsets measured with the NTP
source. It's particularly useful to compensate for a known asymmetry in
network delay or timestamping errors.
2016-06-28 15:40:58 +02:00
Miroslav Lichvar
632cd1a177 client: rework error printing for unsupported source options 2016-06-28 13:26:09 +02:00
Miroslav Lichvar
223ad0e8aa conf: fix parsing of refclock directive
Don't accept refclock directive which has as the last argument an option
that requires a value.
2016-06-28 13:23:27 +02:00
Miroslav Lichvar
f8bd9ab378 cmdparse: remove CPS_Status
Remove command and option specific error codes. Return zero on any
failure.
2016-06-27 14:52:55 +02:00
Miroslav Lichvar
d78e8f096c cmdparse: refactor CPS_ParseNTPSourceAdd() 2016-06-27 14:52:49 +02:00
Miroslav Lichvar
57fc2ff1be sched: add support for output file event
This allows waiting for non-blocking write operations.
2016-06-23 11:45:49 +02:00
Miroslav Lichvar
d8d096aa54 sched: don't keep prepared fd_set
Instead of copying a prepared fd_set to the fd_set used by select(),
fill it from scratch according to the array of file handlers before each
select() call. This should make the code simpler and save some memory
when other events are supported.
2016-06-23 11:34:00 +02:00
Miroslav Lichvar
0a10545314 sched: rework file handling API
Replace SCH_*InputFileHandler() functions with more general
SCH_*FileHandler(), where events are specified as a new parameter and
which will later support other file events, e.g. file ready for ouput
and exception.

The file handlers have two new parameters: file descriptor and event.
2016-06-23 11:33:54 +02:00
Miroslav Lichvar
aeb57a36b2 logging: fix LOG_MESSAGE macro to not use semicolon 2016-06-16 17:15:36 +02:00
Miroslav Lichvar
b703bc32c9 doc: update NEWS 2016-06-07 11:20:59 +02:00
Miroslav Lichvar
09afdd4b36 doc: update README 2016-06-07 11:20:59 +02:00
Miroslav Lichvar
5cadaf8d55 doc: add question about reference ID to FAQ 2016-06-07 11:20:59 +02:00
Stephen Wadeley
89ac745184 doc: improve chronyc man page
- fix redundant words, word order, articles, consistency, typos
- avoid slashes, contractions, `may`, dashes in running text
- add Oxford commas
- use colon before examples
2016-06-07 09:36:55 +02:00
Stephen Wadeley
e2422023c4 doc: improve chrony.conf man page
- fix word order, articles, consistency, and some typos
- avoid slashes, contractions, `may`, dashes in running text
- use colons before example and code blocks
- add Oxford commas
2016-06-07 09:36:55 +02:00
Miroslav Lichvar
1ec0813663 client: fix compiler warnings on NetBSD 2016-06-07 09:36:55 +02:00
Miroslav Lichvar
6ba7fad2a7 reference: suppress orphan option in special reference modes
This allows a server that will become the orphan source to initialize
its time with the initstepslew directive from the current orphan source
or its clients.
2016-05-30 14:12:42 +02:00
Miroslav Lichvar
962ca91574 doc: improve answer in FAQ for firewall issue 2016-05-23 09:01:10 +02:00
Miroslav Lichvar
91cbebb629 examples: update chrony.spec 2016-05-17 17:57:33 +02:00
Miroslav Lichvar
722f038f1f test: extend 105-ntpauth 2016-05-17 13:03:53 +02:00
Miroslav Lichvar
5e61c002a6 ntp: fix definition of minimum and maximum MAC length
The NTP_*_MAC_LENGTH macros didn't include the key ID, which caused the
NTP authentication check to ignore MACs with 512-bit hashes (SHA512,
WHIRLPOOL).

This was broken since update to NTPv4.
2016-05-17 12:57:28 +02:00
Miroslav Lichvar
46e1e79921 doc: update NEWS 2016-05-16 11:08:00 +02:00
Miroslav Lichvar
546e5e236c make_release: update for changes in documentation 2016-05-13 16:58:07 +02:00
Miroslav Lichvar
16a1a89bf4 makefile: remove config.h and config.log in distclean 2016-05-13 16:58:07 +02:00
Miroslav Lichvar
8a996572d2 update copyright years 2016-05-13 16:58:07 +02:00
Miroslav Lichvar
5f082b9a4d doc: update examples of configuration in isolated networks 2016-05-13 16:58:07 +02:00
Miroslav Lichvar
4622173135 doc: fix typo in chronyc man page 2016-05-13 16:58:07 +02:00
Miroslav Lichvar
99e1c44c25 doc: update FAQ 2016-05-13 16:58:04 +02:00
Miroslav Lichvar
b48e4421de ntp: don't check for synchronization loop in special reference modes
If a special reference mode is enabled, always pass the test for
synchronization loop. This allows chronyd using the initstepslew
directive (or the -q/-Q option) to accept time from its own clients
after restart as is documented in the chrony.conf man page.

This was broken since update to NTPv4.
2016-05-13 13:47:12 +02:00
Miroslav Lichvar
d1f4e5876b refclock: avoid reallocation of refclock instances
Change the array with refclock instances to store just pointers and
avoid reallocation of the instances. This fixes a bug with the SOCK
refclock, which uses the pointer to the instance in a file handler and
which was invalid when the instance was reallocated (after adding
another refclock).

The bug is from commit d92583ed33.
2016-05-12 13:27:46 +02:00
Miroslav Lichvar
71b7e689c0 sched: fix handling of signals after finalization
Don't require the scheduler to be initialized in SCH_QuitProgram().
This fixes a crash when a signal is received between scheduler
finalization and chronyd exit.
2016-04-15 14:49:03 +02:00
Miroslav Lichvar
26b87b844d sources: consider only reachable orphans for selection
Ignore orphan sources that are unreachable (but still have usable stats)
to have a quick and consistent source selection between orphans.

This also fixes the "Unknown local refid in orphan mode" error appearing
when a selected orphan source is removed, as the source is marked as
unreachable and the selection runs with disabled NTP instance before the
source instance is actually removed.
2016-04-13 11:43:36 +02:00
Miroslav Lichvar
1834ee05e5 doc: fix typos in man pages 2016-04-12 12:43:10 +02:00
Miroslav Lichvar
7d7bf915ac doc: improve answer in FAQ for error 501 Not authorised 2016-04-12 12:43:10 +02:00
Miroslav Lichvar
d86e9f4aa3 doc: use https in links to chrony website 2016-04-12 12:24:41 +02:00
Miroslav Lichvar
942b52a3ca client: initialize variables in new local command 2016-04-11 08:28:45 +02:00
Miroslav Lichvar
b252c57a22 reference: rework activation of local reference
Instead of using a timer for switching the reference to the
unsynchronised state (which activates the local reference), check
if it should be active when returning the reference parameters.
2016-04-08 16:47:41 +02:00
Miroslav Lichvar
2aab6a85a4 reference: return real sync status in REF_GetReferenceParams()
If local reference is active, return normal leap, but unsynchronised
status. Update the callers of the function to work with the leap
directly and not change their behaviour.

REF_IsLocalActive() is no longer needed.
2016-04-08 16:29:09 +02:00
Miroslav Lichvar
10719d6d35 reference: report same values in tracking command as in NTP
Use REF_GetReferenceParams() in the tracking command to simplify the
code and report the same values as what NTP clients of the server see.

When the local reference mode is active, this changes the leap status to
synchronised and reference time to one second behind current time. When
not synchronised, the root delay and root dispersion are now 1 second.
2016-04-08 16:21:19 +02:00
Miroslav Lichvar
59938efd23 stubs: add NSR_GetLocalRefid() 2016-04-08 16:21:19 +02:00
Miroslav Lichvar
53b15bd5c7 cmdmon: extend local command to match local directive 2016-04-08 16:21:19 +02:00
Miroslav Lichvar
5084a8b342 reference: clamp local stratum set from cmdmon 2016-04-08 16:21:15 +02:00
Miroslav Lichvar
4d1c795804 cmdparse: check if stratum in local directive is valid 2016-04-06 16:38:14 +02:00
Miroslav Lichvar
a9049569af cmdmon: remove obsolete definition 2016-04-06 15:56:12 +02:00
Miroslav Lichvar
dec1d2bfb2 ntp: ignore order of resolved addresses when replacing tentative source
If the replaced source never had a valid reply (e.g. because it was a
bad replacement), ignore the order of addresses from the resolver to not
get 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.
2016-04-05 18:03:46 +02:00
Miroslav Lichvar
62e66bda60 ntp: mark all new sources and replacements as tentative 2016-04-05 18:03:44 +02:00
Miroslav Lichvar
3abaa92926 doc: update description of local directive 2016-04-01 18:42:43 +02:00
Miroslav Lichvar
37e6357c02 ntp: don't check reference timestamp in received packets
When ntpd as an NTP server has active orphan mode, it doesn't update
its reference time and the reference timestamp may fail the NTP test
3 and 7. (https://bugs.ntp.org/show_bug.cgi?id=1098)

Remove both checks of the timestamp to allow chronyd to operate as
a client of ntpd server in the orphan mode. When ntpd is fixed and
old versions are no longer used, this may be reverted.
2016-04-01 15:13:28 +02:00
Miroslav Lichvar
6accd19eb3 sources: log error when local refid is unknown in orphan mode 2016-04-01 09:37:49 +02:00
Miroslav Lichvar
da96d334ab test: add 121-orphan 2016-03-31 16:12:14 +02:00
Miroslav Lichvar
5a92dbe784 sources: add support for orphan sources
When the local reference is configured with the orphan option, NTP
sources that have stratum equal to the configured local stratum are
considered to be orphans (i.e. serving local time while not being
synchronised with real time) and are excluded from the normal source
selection. Sources with stratum larger than the local stratum are
considered to be directly on indirectly synchronised to an orphan and
are always ignored.

If no selectable source is available and all orphan sources have
reference IDs larger than the local ID, no source will be selected and
the local reference mode will be activated at some point, i.e. this host
will become an orphan. Otherwise, the orphan source with the smallest
reference ID will be selected. This ensures a group of servers polling
each other (with the same orphan configuration) which have no external
source can settle down to a state where only one server is serving its
local unsychronised time and others are synchronised to it.
2016-03-31 16:08:49 +02:00
Miroslav Lichvar
8fe5e9cf1e reference: add orphan mode to local reference
Add orphan option to the local directive. It will enable an orphan mode
compatible with ntpd.
2016-03-31 16:08:49 +02:00
Miroslav Lichvar
81f440a882 reference: activate local reference with large root distance
Since the update to NTPv4, when the clock is in the synchronised state
and the clock updates stop (e.g. sources become unreachable), it doesn't
switch to the unsynchronised state and the local reference is never
activate. This can be a problem for clients that rely on the server to
always have root distance below some value (e.g. chronyd's maxdistance).

Add a timer that will activate the local reference when the root
distance reaches a specified threshold. It can be configured with the
distance option in the local directive (by default 1.0 second).
2016-03-31 16:08:45 +02:00
Miroslav Lichvar
981f897c96 conf: rework local directive to have default stratum
Allow the local directive to be specified without the stratum field.
It's an option now, with default value 10. Also, move the parsing code
to cmdparse.c to make it available to the client.
2016-03-31 16:01:06 +02:00
Miroslav Lichvar
eb75ce7d07 ntp: add function to get local reference ID
When a valid NTP reply is received, save the local address (e.g. from
IP_PKTINFO), so the reference ID which would the source use for this
host can be calculated when needed.
2016-03-31 16:01:02 +02:00
Miroslav Lichvar
5645e57ce0 sys_linux: include <termios.h> for TCGETS 2016-03-29 10:06:21 +02:00
Miroslav Lichvar
a12c7c422b local: make maximum frequency offset configurable
Add maxdrift directive to set the maximum assumed drift of the clock,
which sets the maximum frequency offset chronyd is allowed to use to
to correct the drift.
2016-03-22 17:12:27 +01:00
Miroslav Lichvar
d70e815e9f sources: try to replace NTP sources with bad distance
Similarly to unreachable sources and falsetickers, try to replace
sources with distance larger than the limit set by the maxdistance
directive with a newly resolved address of the hostname.
2016-03-22 17:12:27 +01:00
Miroslav Lichvar
eb329e9f52 client: ignore -v option in csv mode 2016-03-22 16:34:35 +01:00
Miroslav Lichvar
5833be6ccf util: fix UTI_FloatNetworkToHost() with very small exponents
Fix conversion of floating point numbers from the cmdmon format with
very small exponents, as for instance could be in the smoothing report
when the smoothing process ends.

This was broken in commit 8e71a46173.
2016-03-17 16:18:28 +01:00
Miroslav Lichvar
ea3950d57e client: add CSV output mode
Add a new option (-c) to chronyc to enable printing of reports in a
column-separated values (CSV) format. IP addresses will not be resolved
to hostnames, time will be printed as number of seconds since the epoch
and values in seconds will not be converted to other units.
2016-03-17 16:01:58 +01:00
Miroslav Lichvar
3f51805e62 client: rework printing of reports
Add a new printf-like function to allow printing of all fields at once
and rework all commands which print a report to use it. Add functions
for printing of headers and information fields, and formatting of IP
addresses and reference IDs.
2016-03-17 15:50:39 +01:00
Miroslav Lichvar
b45f53dd20 util: randomize hashing of IP addresses
Include a random (constant) value in the hash in UTI_IPToHash() to
randomize the order in which NTP sources are stored in the hash table
and polled on start. This change also randomizes the order of clientlog
records.
2016-03-15 14:29:42 +01:00
Miroslav Lichvar
9749a1c6fc test: make 105-ntpauth more reliable 2016-03-14 17:42:49 +01:00
Miroslav Lichvar
5ca5d279d7 makefile: add distclean target to test/unit/Makefile 2016-03-14 15:55:39 +01:00
Miroslav Lichvar
7b52c1578f makefile: remove Makefile in doc/Makefile on distclean 2016-03-14 15:55:39 +01:00
Miroslav Lichvar
72975ce1f0 ntp: improve error messages for socket options 2016-03-14 15:55:39 +01:00
Miroslav Lichvar
9a4c22db03 cmdmon: extend initialization checks
Move the message size checks to a separate function and check also
header size in the command request and reply to catch incompatible
changes.
2016-03-14 15:34:52 +01:00
Miroslav Lichvar
e7af875b68 rewrite assertions with very long messages 2016-03-14 15:15:51 +01:00
Miroslav Lichvar
4acca9b727 client: add reselectdist to help text 2016-03-11 17:29:10 +01:00
Miroslav Lichvar
b2d93b2e38 git: update .gitignore 2016-03-11 17:29:10 +01:00
Miroslav Lichvar
74afffed0c doc: convert manual from Texinfo to AsciiDoc
Split and convert the manual into four AsciiDoc documents, a document
about installation and three documents in the manpage type for
chrony.conf, chronyd and chronyc. The minimal man pages that were
maintained separately from the manual are replaced by full man pages
generated from AsciiDoc. Info files will no longer be provided.

Some parts of the manual are rewritten, updated or trimmed. The
introduction chapter is partially merged with README. The chapter about
typical operating scenarios is included in the chrony.conf man page.
2016-03-11 17:29:03 +01:00
Miroslav Lichvar
5828426977 doc: update installation instructions 2016-02-16 14:25:38 +01:00
Miroslav Lichvar
d04fb4b7fa doc: improve description of trust option 2016-02-16 13:43:33 +01:00
Miroslav Lichvar
f5fe3ab4a1 test/unit: add sources unit test 2016-02-16 13:43:33 +01:00
Miroslav Lichvar
6b6b097fe8 test/unit: include microseconds in default random seed 2016-02-16 13:43:28 +01:00
Miroslav Lichvar
4998afc9bb test/unit: add more helper functions 2016-02-16 13:43:07 +01:00
Miroslav Lichvar
80f4d75968 test/unit: follow chrony function naming convention 2016-02-15 16:09:05 +01:00
Miroslav Lichvar
910663c37b test: add ntp_sources unit test 2016-02-05 15:20:40 +01:00
Miroslav Lichvar
34a4695e81 test: add clientlog unit test 2016-02-05 15:20:40 +01:00
Miroslav Lichvar
fe00319f45 addrfilt: remove TEST code
A test of the address filter is now included in unit tests.
2016-02-05 15:20:40 +01:00
Miroslav Lichvar
4c77d18416 test: add addrfilt unit test 2016-02-05 15:20:40 +01:00
Miroslav Lichvar
a63e18edb8 test: specify files with path in source commands
This should prevent sourcing of an unrelated file found in $PATH.
2016-02-05 15:20:40 +01:00
Miroslav Lichvar
8b676502de test: don't download files in tests
Remove automatic download and compilation of clknetsim. If clknetsim is
not found, skip all simulation tests, but don't fail "make check".
Also, respect the CLKNETSIM_PATH environment variable.
2016-02-05 15:20:40 +01:00
Miroslav Lichvar
cf5b344ea8 git: update .gitignore 2016-02-05 15:20:40 +01:00
Miroslav Lichvar
4ab98f62e9 test: add support for unit testing 2016-02-05 15:20:40 +01:00
Miroslav Lichvar
e6cc682f86 update NEWS 2016-02-02 16:49:05 +01:00
Miroslav Lichvar
ff541e24fb update README 2016-02-02 12:05:51 +01:00
Miroslav Lichvar
008615370a update copyright years 2016-02-02 12:02:16 +01:00
Miroslav Lichvar
beaf275222 ntp: optimize resizing of hash table with sources 2016-02-02 12:02:16 +01:00
Miroslav Lichvar
400820d3f3 sys_generic: use privops for settimeofday()
This is needed on FreeBSD and Solaris when running without root
privileges.
2016-02-01 16:54:08 +01:00
Miroslav Lichvar
4eabc84a0c clientlog: fix warning reported by static analyzer 2016-02-01 14:37:10 +01:00
Miroslav Lichvar
cf636a969e client: fix format specifiers in client report
This was missing in commit 861ac013bc.
2016-02-01 10:30:31 +01:00
Miroslav Lichvar
e3191e372b cmdmon: update protocol changelog 2016-01-29 17:55:58 +01:00
Miroslav Lichvar
705e32acdc cmdmon: define new types for CLIENT_ACCESSES_BY_INDEX command
There was an incompatible change in the client access report. To avoid
bumping the protocol version drop support for the original request/reply
types and define new CLIENT_ACCESSES_BY_INDEX2 types as a newer version
of the command.
2016-01-29 17:55:58 +01:00
Miroslav Lichvar
6e4dd9302d cmdmon: allow unhandled commands
Replace the assert() with a debug message to not crash if someone
forgets to implement a newly defined command.
2016-01-29 17:55:58 +01:00
Miroslav Lichvar
ea002130d7 cmdmon: reply to invalid commands
If an unknown command is received (e.g. from a future client), it should
get a reply and print an error code instead of timing out.
2016-01-29 17:55:58 +01:00
Miroslav Lichvar
7ba5ffa706 cmdmon: update debug messages 2016-01-29 17:55:58 +01:00
Miroslav Lichvar
861ac013bc cmdmon: use 32-bit fields in client access report
The clientlog record still uses 16-bit integers to count dropped
packets, but this will avoid an incompatible change in the command
reply if there will be a need to count more than 2^16 drops.
2016-01-29 17:55:58 +01:00
Miroslav Lichvar
a6da963f45 clientlog: don't allow rate limiting with noclientlog 2016-01-29 17:55:58 +01:00
Miroslav Lichvar
55ba7ee2a1 doc: update description of clients command 2016-01-29 17:55:58 +01:00
Miroslav Lichvar
3121f31ced doc: describe rate limiting directives 2016-01-29 17:55:58 +01:00
Miroslav Lichvar
da296db91d examples: update for recent changes 2016-01-29 17:55:58 +01:00
Miroslav Lichvar
d36ca9288a doc: update keyfile description 2016-01-29 17:55:58 +01:00
Miroslav Lichvar
8549043a3f conf: set logchange to 1 second by default
logchange is now always enabled, with 1 second threshold by default.
2016-01-29 17:55:58 +01:00
Miroslav Lichvar
e0ae2b4bb5 client: generate key 1 by default in keygen command 2016-01-29 17:55:58 +01:00
Miroslav Lichvar
aad42ceaec keys: warn about short key only if used by source
After restricting authentication of servers and peers to the specified
key, a short key in the key file is a security problem from the client's
point of view only if it's specified for a source.
2016-01-29 17:55:58 +01:00
Miroslav Lichvar
f225469e6e pktlength: fix compiler warning on Mac OS X 2016-01-25 12:33:42 +01:00
Miroslav Lichvar
7cc432ff7e cmdmon: extend initialisation tests 2016-01-22 17:30:55 +01:00
Miroslav Lichvar
0a9d75bfb8 pktlength: rework code to use tables 2016-01-22 17:30:51 +01:00
Miroslav Lichvar
070f2706b7 client: add serverstats command 2016-01-22 14:40:29 +01:00
Miroslav Lichvar
9b019a03e7 cmdmon: add serverstats command
Add a new command to obtain a server report with the new clientlog
statistics.
2016-01-22 13:26:38 +01:00
Miroslav Lichvar
f52a738660 clientlog: count total number of hits and drops
Count total number of NTP and command hits. Count also number of log
records that were replaced when the hash table couldn't be resized due
to the memory limit.
2016-01-22 13:26:04 +01:00
Miroslav Lichvar
b80df5152a Merge branch '2.2-security' 2016-01-20 12:18:42 +01:00
Miroslav Lichvar
beb275a769 doc: update NEWS 2016-01-18 17:37:21 +01:00
Miroslav Lichvar
86c21a3a85 test: extend 105-ntpauth to test symmetric mode 2016-01-18 17:37:21 +01:00
Miroslav Lichvar
05236a4f23 test: allow setting options for each peer side separately 2016-01-18 17:37:21 +01:00
Miroslav Lichvar
a78bf9725a ntp: restrict authentication of server/peer to specified key
When a server/peer was specified with a key number to enable
authentication with a symmetric key, packets received from the
server/peer were accepted if they were authenticated with any of
the keys contained in the key file and not just the specified key.

This allowed an attacker who knew one key of a client/peer to modify
packets from its servers/peers that were authenticated with other
keys in a man-in-the-middle (MITM) attack. For example, in a network
where each NTP association had a separate key and all hosts had only
keys they needed, a client of a server could not attack other clients
of the server, but it could attack the server and also attack its own
clients (i.e. modify packets from other servers).

To not allow the server/peer to be authenticated with other keys
extend the authentication test to check if the key ID in the received
packet is equal to the configured key number. As a consequence, it's
no longer possible to authenticate two peers to each other with two
different keys, both peers have to be configured to use the same key.

This issue was discovered by Matt Street of Cisco ASIG.
2016-01-18 17:28:47 +01:00
Miroslav Lichvar
82fbb5c2f5 privops: reload DNS configuration
The helper process needs to call res_init() before DNS_Name2IPAddress()
in order to see changes in resolv.conf.
2016-01-15 16:58:12 +01:00
Miroslav Lichvar
a592d82ad9 client: improve waitsync help text 2016-01-14 14:45:52 +01:00
Miroslav Lichvar
7fcf69ce5f client: add keygen command
Add a new command that will generate a random key from /dev/urandom with
given ID, hash function and length.
2016-01-14 14:45:52 +01:00
Miroslav Lichvar
32ac6ffa26 util: add UTI_GetRandomBytesUrandom()
This function always uses /dev/urandom, even if arc4random() is
available, and is intended for generating long-term keys.
2016-01-14 14:45:52 +01:00
Miroslav Lichvar
0d12410eaa keys: warn when loaded key is shorter than 80 bits
Consider 80 bits as the absolute minimum for a secure symmetric key.  If
a loaded key is shorter, send a warning to the system log to encourage
the admin to replace it with a longer key.
2016-01-14 14:45:52 +01:00
Miroslav Lichvar
54c8732c46 sys_linux: use privops helper when running with seccomp filter
Enable the PRV_Name2IPAddress() function with seccomp support and start
the helper process before loading the seccomp filter (but after dropping
root privileges). This will move the getaddrinfo() call outside the
seccomp filter and should make it more reliable as the list of required
system calls won't depend on what glibc NSS modules are used on the
system.
2016-01-14 14:45:48 +01:00
Miroslav Lichvar
9b9d6ab150 privops: add support for privileged DNS_Name2IPAddress() 2016-01-13 11:25:45 +01:00
Miroslav Lichvar
c6554bfd30 nameserv: return at most 16 addresses from DNS_Name2IPAddress()
This is the same limit as in the asynchronous resolver. Use common macro
for all buffers storing IP addresses.
2016-01-13 11:25:26 +01:00
Miroslav Lichvar
83cd8ae39b test: don't check packet intervals in 009-sourceselection
Since commit 8b235297, which changed address hashing, the first packet
is not sent to the first server and doesn't have the extra delay. If the
last packet is sent to the first server, the mean outgoing interval will
be significantly longer than the incoming interval and the check will
fail.
2016-01-08 14:30:23 +01:00
Miroslav Lichvar
23b9d80897 test: add 120-selectoptions 2016-01-08 14:30:23 +01:00
Miroslav Lichvar
e98f76e084 sources: add require option
Require that at least one of the sources specified with this option is
selectable (i.e. recently reachable and not a falseticker) before
updating the clock. Together with the trust option this may be useful to
allow a trusted, but not very precise, reference clock or a trusted
authenticated NTP source to be safely combined with unauthenticated NTP
sources in order to improve the accuracy of the clock. They can be
selected and used for synchronization only if they agree with the
trusted and required source.
2016-01-08 14:30:17 +01:00
Miroslav Lichvar
936f5cb0f1 sources: add trust option
Assume time from a source that is specified with the trust option is
always true.  It can't be rejected as falseticker in the source
selection if sources that are specified without this option don't agree
with it.
2016-01-07 16:20:27 +01:00
Miroslav Lichvar
fa15fb3d53 sources: turn select options into flags
This will allow adding new options for source selection which can be
combined with others.
2015-12-18 16:29:47 +01:00
Miroslav Lichvar
62d61de93d sources: fix formatting of selection intervals in comment
It was mangled in commit 6f84d2fac1.
2015-12-18 16:25:26 +01:00
Miroslav Lichvar
ba81d68b07 refclock: ignore samples with unsynchronised leap status 2015-12-18 16:25:23 +01:00
Miroslav Lichvar
ba25fb1bcc refclock: describe fields in SOCK sample 2015-12-18 12:44:15 +01:00
Miroslav Lichvar
69642dd440 fix undefined shift operations on signed integers 2015-12-17 12:09:45 +01:00
Miroslav Lichvar
e5a593f013 conf: update default ratelimit configuration 2015-12-17 09:44:21 +01:00
Miroslav Lichvar
acec7d0e28 clientlog: use token buckets for response rate limiting
Replace thresholds that activated rate limiting with token buckets.
Response rate limiting is now not just active or inactive, the interval
and burst options directly control the response rate.
2015-12-17 09:42:48 +01:00
Miroslav Lichvar
a4c89e5bbe clientlog: refactor updating of record data 2015-12-14 17:05:02 +01:00
Miroslav Lichvar
c5265f6070 doc: update description of -u option and user directive 2015-12-10 16:30:38 +01:00
Miroslav Lichvar
0a10df1cf5 sys_linux: keep CAP_NET_BIND_SERVICE only if NTP port can be opened
If port is set to 0 in the config file, the server port cannot be opened
and there is no point in keeping the binding capability.
2015-12-10 16:30:38 +01:00
Miroslav Lichvar
30f2a2003c sys: remove unused code 2015-12-10 16:30:38 +01:00
Miroslav Lichvar
67b108d1ce sys_solaris: add support for dropping root privileges
On Solaris, use the privops helper for the ntp_adjtime(),
settimeofday(), and bind() system calls.
2015-12-10 16:30:38 +01:00
Miroslav Lichvar
8a95631e39 sys_solaris: fix building with current timex driver
The SYS_Timex_InitialiseWithFunctions() call in the Solaris driver
wasn't updated in commit d6fdae5f1d.
2015-12-10 16:30:38 +01:00
Miroslav Lichvar
82510e6b1f sys_netbsd: add support for dropping root privileges on FreeBSD
On FreeBSD, use the privops helper for the adjtime(), ntp_adjtime(),
settimeofday(), and bind() system calls.
2015-12-10 16:30:38 +01:00
Miroslav Lichvar
12ee4bf6ac sys_timex: add support for ntp_adjtime() via privops 2015-12-10 16:30:38 +01:00
Miroslav Lichvar
3cb0351aff privops: add support for privileged ntp_adjtime() 2015-12-10 16:30:38 +01:00
Miroslav Lichvar
d5bc4e92e6 sys_timex: move inclusion of sys/timex.h to sysincl.h
It will be needed by privops.
2015-12-10 16:30:38 +01:00
Miroslav Lichvar
8e327bb0a3 privops: ignore signals in helper
If the whole process group receives a signal (e.g. CTRL-C in terminal),
the helper process needs to keep running until it gets the QUIT request,
so the system drivers can still use it in their finalisation, e.g. to
cancel remaining slew.
2015-12-10 16:30:31 +01:00
Miroslav Lichvar
fbf170a6c2 privops: compile only required helper functions 2015-12-10 15:31:55 +01:00
Miroslav Lichvar
cd472e6aaf privops: return from PRV functions with helper response code
In receive_reponse() don't interpret return codes in helper responses as
a non-zero value may not necessarily mean an error. Just copy errno if
it's not zero and let PRV_* functions deal with the return code.
2015-12-10 15:30:45 +01:00
Miroslav Lichvar
e9487b1a1a privops: make naming of fields and functions more consistent 2015-12-10 15:25:56 +01:00
Miroslav Lichvar
3cf6acdf24 util: add function for dropping root privileges
Share the code for dropping supplementary groups and setting effective,
saved, and real user UID/GID between system drivers.
2015-12-10 15:25:56 +01:00
Miroslav Lichvar
334ac06102 main: initialise privops sooner
System drivers may need it in their initialisation.
2015-12-10 15:25:56 +01:00
Bryan Christianson
2d9486ec7c sys_macosx: fix adjustment correction after step
The desired offset was being added to the current time instead of being
subtracted.
2015-12-10 15:25:24 +01:00
Miroslav Lichvar
fe502128b8 main: fix compiler warning 2015-12-08 18:02:05 +01:00
Miroslav Lichvar
46f0ad6b53 sys_netbsd: use privileged helper for socket binding
When dropping root privileges, start the helper to allow binding
of server sockets later.
2015-12-08 18:02:05 +01:00
Miroslav Lichvar
fedc605956 configure: rework setting of privops macros
Prepare a list of required privileged operations first and from that
define the PRIVOPS macros. This will reduce the amount of code that will
be needed when the privileged helper is used on other platforms.
2015-12-08 18:02:05 +01:00
Miroslav Lichvar
d44e26ba22 configure: fix check of date output 2015-12-08 18:02:05 +01:00
Miroslav Lichvar
610f234043 privops: refactor initialisation/finalisation
Rename PRV_Initialise() to PRV_StartHelper() and add a new
initialisation function, which just sets the helper fd to -1. Move
the initialision/finalisation calls from the system drivers to main.c.
If privops is not included in the build, define empty macros for the
function names, so their calls don't have to be wrapped in #ifdefs.
2015-12-08 18:02:05 +01:00
Miroslav Lichvar
aa9a4c697c privops: wait for helper pid
Save the pid of the helper process and replace wait() with waitpid().
2015-12-08 18:01:59 +01:00
Miroslav Lichvar
1b8ee3259e privops: stop helper on exit
With SOCK_DGRAM sockets, the helper doesn't stop as there is no error
received when the socket is closed on the daemon side.

Add a QUIT operation to the protocol which is requested when the daemon
is exiting. It has no response. Register the stopping function with
atexit() to stop the helper even when the daemon is not exiting cleanly,
e.g. due to a fatal error.
2015-12-08 17:50:09 +01:00
Miroslav Lichvar
c7ae4940c3 privops: split send_to_helper()
Split out the sending part of the function into send_request() and
rename it to submit_request(). This will be useful to send a request
without waiting for a response.

Also, remove the fd parameter from the functions and just use helper_fd
directly.
2015-12-08 17:50:07 +01:00
Miroslav Lichvar
aa4bf41400 privops: use SOCK_SEQPACKET sockets when supported
SOCK_SEQPACKET is preferred over SOCK_DGRAM for communication with the
helper as the process will get an error when the other end of the socket
is closed. It's not supported on all platforms.

If SOCK_SEQPACKET is defined, try creating the pair of sockets with this
type first and if that fails, fall back to SOCK_DGRAM.
2015-12-08 17:38:56 +01:00
Miroslav Lichvar
4e32de09a2 sys_linux: allow mremap in seccomp filter 2015-12-07 11:35:27 +01:00
Bryan Christianson
86e0399ea9 sys_macosx: synchronise RTC from system time
When the rtcsync directive is specified in the chronyd config file,
chronyd will update the RTC via settimeofday() every 60 minutes if
the system time is synchronised to NTP.
2015-12-03 12:20:54 +01:00
Miroslav Lichvar
302abf8480 client: print invalid intervals as dash
Instead of printing some large arbitrary values use dash in the LastRx
column of the sources output and the Last/Int columns in the clients
output when no sample or hit is recorded.
2015-12-03 11:43:06 +01:00
Miroslav Lichvar
f50b520557 sourcestats: use maximum value as invalid age in source report 2015-12-03 11:43:06 +01:00
Miroslav Lichvar
d4074c7993 clientlog: fix counting of command drops 2015-12-03 11:43:06 +01:00
Miroslav Lichvar
d3096c3b5e clientlog: save time of last hit with sub-second resolution
Instead of time_t use a 32-bit fixed point representation with 4-bit
fraction to save the time of the last hit. The rate can now be measured
up to 16 packets per second. Maximum interval between hits is about 4
years.
2015-12-03 11:43:06 +01:00
Miroslav Lichvar
98947a4e52 conf: inline one-line parse_* functions 2015-12-03 11:43:06 +01:00
Miroslav Lichvar
bafb434f06 main: assert supported integer size, representation and conversion
Abort immediately on start if chronyd is compiled on a platform with int
shorter than 32 bits, using other representation than two's complement,
or unexpected conversion of large unsigned integers to signed.
2015-12-03 11:43:06 +01:00
Miroslav Lichvar
8e71a46173 fix undefined shift operations on signed integers 2015-12-02 12:06:01 +01:00
Miroslav Lichvar
934df19c52 array: always return non-NULL pointer from ARR_GetElements()
Some libc calls like memcpy() expect the pointer to be valid even when
the size is zero and there is nothing to do. Instead of checking the
size before all such calls, modify ARR_GetElements() to return a pointer
to the array instance itself if data was not allocated yet.
2015-12-02 12:04:09 +01:00
Bryan Christianson
024842a38b contrib: update chronylogrotate.sh script
1. Remove obsolete options when running chronyc
2. Add copyright/licence notice
3. Use logger utility to print/store error messages
2015-11-30 17:50:55 +01:00
Miroslav Lichvar
657929f8ec cmdmon: update CLIENT_ACCESSES_BY_INDEX command
Add new fields from clientlog to the report and print them in chronyc.
Rework the code to skip empty records in the hash table. The reply no
longer has variable length, all client fields are filled even if some
are empty. Reply with RPY_NULL when the facility is disabled.
2015-11-30 17:50:55 +01:00
Miroslav Lichvar
b506594c2d clientlog: limit response rate
When the measured NTP or command request rate of a client exceeds
a threshold, reply only to a small fraction of the requests to reduce
the network traffic. Clients are allowed to send a burst of requests.
Try to detect broken clients which increase the request rate when not
getting replies and suppress the rate limiting for them.

Add ratelimit and cmdratelimit directives to configure the thresholds,
bursts and leak rates independently for NTP and command response rate
limiting. Both are disabled by default. Commands from localhost are
never limited.
2015-11-30 17:50:55 +01:00
Miroslav Lichvar
830135edea clientlog: measure request rates
Extend the record with estimates of the current client's NTP and command
request rates. Store them as 8-bit scaled log2 values to save memory.
2015-11-30 17:50:55 +01:00
Miroslav Lichvar
464cdbbb6e clientlog: store records in hash table instead of tree
This simplifies the code and allows older records to be reused when no
more memory can be allocated for new addresses. Each slot of the hash
table has 16 records and there is no chaining between different slots.
Reused records may be newer than records in other slots, but the search
time remains constant.
2015-11-30 17:50:55 +01:00
Miroslav Lichvar
086e886d1e clientlog: reduce amount of logged information
Don't log NTP peer access and auth/bad command access. Also, change
types for logging number of hits from long to uint32_t. This reduces the
size of the node and allows more clients to be monitored in the same
amount of memory.
2015-11-30 17:50:44 +01:00
Miroslav Lichvar
f2b82c1e1d conf: don't allow disabling clientloglimit
Don't treat zero as a special value disabling clientloglimit. It's not
useful, the amount of available memory is never unlimited.
2015-11-30 17:34:53 +01:00
Miroslav Lichvar
801830df57 util: add macros for maximum, minimum and clamp
If MAX/MIN are defined in system headers, undefine them first.
2015-11-30 17:34:53 +01:00
Miroslav Lichvar
8b235297a5 util: add function for IP address hashing
Move the hashing function from find_slot() in ntp_sources to make it
available to clientlog and improve the hashing a bit.
2015-11-30 17:34:50 +01:00
Miroslav Lichvar
59a3140621 cmdmon: tidy up declarations in read_from_cmd_socket() 2015-11-26 10:10:24 +01:00
Bryan Christianson
16bd56ae7e sys_macosx: tidy up includes
Use "sysincl.h" in place of the common system include files
2015-11-24 10:15:47 +01:00
Bryan Christianson
750d82f1d1 sys_macosx: drop root privileges
Run chronyd as a non-privileged user, using the privops helper to
perform adjtime(), settimeofday() and bind() functions on its behalf.
2015-11-24 09:29:22 +01:00
Bryan Christianson
139fc667aa add support for privilege separation
Privileged helper that will perform adjtime(), settimeofday(), bind() on
behalf of chronyd when running as non-root user.
2015-11-20 18:01:22 +01:00
Miroslav Lichvar
f21e5f6cc5 sys_linux: allow ioctl(TCGETS) in seccomp filter
This seems to be needed to allow fopen() called on /dev/urandom to check
if it's a terminal.
2015-11-18 12:49:11 +01:00
Miroslav Lichvar
f660aa9d7a conf: don't allow invalid last refclock option 2015-11-18 12:49:05 +01:00
Miroslav Lichvar
d28d644b04 ntp: ignore poll in KoD RATE packets
The meaning of the poll value in KoD RATE packets is not currently
defined in the NTP specification (RFC 5905). In the reference NTP
implementation it signals the minimum acceptable polling interval to the
clients. In chrony the minimum poll is set to the KoD RATE poll if it's
larger, but not to a larger value than 10.

The problem is that ntpd as a server sets the KoD RATE poll to the
maximum of the client's poll and the configured rate limiting interval.
An attacker can send a burst of spoofed packets to the server to trigger
the client's request rate limit. When the client sends its next request
and the server responds with a KoD RATE packet, the client will set its
minimum poll to the current poll and it will no longer be able to switch
to a shorter poll when needed.

ntpd could be fixed to always set the KoD RATE poll to the rate limiting
interval. Unfortunately, ntpd as a client seems to depend on the current
behavior. It tries to follow the server poll and if the KoD RATE poll
was shorter than the current poll, the polling interval would be
reduced, defeating the purpose of KoD RATE. The server fix will probably
need to wait until clients are fixed and that could take a very long
time.

For now, ignore the poll value in KoD RATE packets. Just add an extra
delay based on the current poll to the next transmit timeout and stop an
ongoing burst.
2015-11-16 17:28:30 +01:00
Miroslav Lichvar
a634fd3a2d doc: update description of offline command
Reachability and online/offline mode has no effect on source selection
since version 2.0.
2015-11-16 14:52:05 +01:00
Miroslav Lichvar
045794df4c ntp: adjust initial delay for polling interval
First packet after setting a source to online was sent with constant
delay (0.2s). If the period in which the source was offline was shorter
than the current polling interval, the new packet was sent sooner than
it would be if the source wasn't switched to offline and back.

Don't reset the local tx timestamp when mode is changed. When starting
the initial transmit timeout, adjust the delay to make the interval
between the two packets at least as long as the current polling
interval.
2015-11-16 14:48:02 +01:00
Miroslav Lichvar
dfc96e4702 sched: update timeout randomization
Use UTI_GetRandomBytes() instead of random() to calculate the random
part of the timeout. This was the only remaining use of random() in the
code and the srandom() call can be removed.
2015-11-16 10:30:59 +01:00
Miroslav Lichvar
8225bf01f7 ntp: don't reveal local clock in client packets
In client packets set the leap, stratum, reference ID, reference time,
root delay and root dispersion to constant values to not reveal the
state of the synchronization. Use precision 32 to make the receive and
transmit timestamps completely random and not reveal the local time.
2015-11-16 10:30:52 +01:00
Miroslav Lichvar
116c697282 util: rework timestamp fuzzing
Use UTI_GetRandomBytes() instead of random() to generate random bits
below precision. Save the result in NTP_int64 in the network order and
allow precision in the full range from -32 to 32. With precision 32
the fuzzing now makes the timestamp completely random and can be used to
hide the time.
2015-11-16 10:26:14 +01:00
Miroslav Lichvar
6199a89170 util: add function to generate random bytes
Add a function to fill a buffer with random bytes which uses a better
PRNG than random(). Use arc4random() if it's available on the system.
Fall back to reading from /dev/urandom, which should be available on
all currently supported systems.
2015-11-16 10:26:14 +01:00
Miroslav Lichvar
cbd77c9752 ntp: don't keep client sockets open for longer than necessary
After sending a client packet, schedule a timeout to close the socket
at the time when all server replies would fail the delay test, so the
socket is not open for longer than necessary (e.g. when the server is
unreachable). With the default maxdelay of 3 seconds the timeout is 7
seconds.
2015-11-16 10:26:14 +01:00
Miroslav Lichvar
df9b5d8c22 ntp: check remote interval in client mode
For testA in the client mode require also that the time the server
needed to process the client request is not longer than 4 seconds.
With maximum peer delay this limits the interval in which the client can
accept a server reply.
2015-11-16 10:26:14 +01:00
Miroslav Lichvar
66d534417b sched: use shorter data type for timeout IDs 2015-11-16 10:26:14 +01:00
Miroslav Lichvar
8803ab27c6 sched: don't allow SCH_RemoveTimeout() with invalid non-zero ID 2015-11-16 10:26:14 +01:00
Miroslav Lichvar
38910424f2 sched: don't return currently used timeout ID
To avoid problems in the very unlikely case where a timeout is so long
and new IDs are allocated so frequently that they would have a chance
to overflow and catch up with it, make sure before returning new ID that
it's currently not in use.
2015-11-16 10:25:33 +01:00
Miroslav Lichvar
0076458e9d sched: always return non-zero timeout ID
Timeout ID of zero can be now safely used to indicate that the timer is
not running. Remove the extra timer_running variables that were
necessary to track that.
2015-11-10 14:52:52 +01:00
Miroslav Lichvar
bdb1650ed8 sys_linux: allow more syscalls in seccomp filter
These seem to be needed by getaddrinfo() in default NSS configuration
on recent Fedora.
2015-11-04 15:17:16 +01:00
Miroslav Lichvar
a030ed4f39 doc: update NEWS 2015-10-19 11:18:37 +02:00
Miroslav Lichvar
9fc15394de configure: disable scfilter by default
As an experimental feature it should be explicitly enabled.
2015-10-19 11:18:17 +02:00
Miroslav Lichvar
34ea8770d0 client: add debug message for recv() error 2015-10-15 11:59:13 +02:00
Miroslav Lichvar
a5897840a0 doc: add minimum recommended configuration to FAQ 2015-10-14 16:53:37 +02:00
Miroslav Lichvar
59087dd0ff doc: include chrony version in manual title 2015-10-14 15:03:45 +02:00
Miroslav Lichvar
1924481077 doc: update comparison with ntpd 2015-10-14 15:03:45 +02:00
Miroslav Lichvar
da1f7563e9 doc: remove obsolete section on contributing 2015-10-14 15:03:45 +02:00
Miroslav Lichvar
7496a14d2d doc: improve maxdistance description 2015-10-14 15:03:45 +02:00
Miroslav Lichvar
6e6dead680 logging: don't ignore message severity with debug support
The severity was fixed for all messages to LOGS_DEBUG. This was broken
in commit 7b2430fc3c.
2015-10-12 13:41:41 +02:00
Miroslav Lichvar
55dbbab5eb configure: check for struct in_pktinfo with ipi_spec_dst
On NetBSD there is a struct in_pktinfo, but it doesn't have the
ipi_spec_dst field and it breaks compilation.
2015-10-12 13:41:35 +02:00
Miroslav Lichvar
d6b6461658 configure: improve description of struct in6_pktinfo check 2015-10-12 13:41:18 +02:00
Miroslav Lichvar
85f7a4054d configure: include IPV6_PKTINFO in struct in6_pktinfo check 2015-10-12 13:40:02 +02:00
Miroslav Lichvar
01965d147a doc: update NEWS 2015-10-09 13:39:44 +02:00
Miroslav Lichvar
6a84126c28 examples: use one-second check interval in chrony-wait.service 2015-10-09 13:39:37 +02:00
Miroslav Lichvar
32f8bec92d configure: make default hwclockfile configurable 2015-10-08 15:20:32 +02:00
Miroslav Lichvar
00a6394b48 rtc: improve logging
Improve, shorten, or convert to debug log messages.
2015-10-08 15:20:28 +02:00
Miroslav Lichvar
ca5a791d09 client: make waitsync check interval configurable 2015-10-07 15:52:37 +02:00
Miroslav Lichvar
6a9c756cf0 rtc: restore time from driftfile if later than RTC time
This is useful on computers that have an RTC, but there is no battery to
keep the time when they are turned off and start with the same time on
each boot.
2015-10-06 15:52:36 +02:00
Miroslav Lichvar
1714d3e8ae rtc: don't run time_init function if pre_init failed 2015-10-06 13:23:14 +02:00
Miroslav Lichvar
25b7d47b34 doc: reduce makestep threshold in examples to 1 second 2015-10-05 10:15:02 +02:00
Miroslav Lichvar
9e8b4bae11 sys_linux: abort when loading seccomp rules fails 2015-10-05 09:56:58 +02:00
Miroslav Lichvar
a466395a19 doc: update NEWS 2015-10-02 11:50:08 +02:00
Miroslav Lichvar
a3cb3fc490 doc: update README 2015-10-01 18:09:44 +02:00
Miroslav Lichvar
3396778061 update copyright years 2015-10-01 18:07:10 +02:00
Miroslav Lichvar
01cef64070 client: remove unreachable code 2015-10-01 09:28:55 +02:00
Miroslav Lichvar
a9bfaf9e54 client: don't try sending request with invalid socket 2015-09-30 14:58:17 +02:00
Miroslav Lichvar
cec7c44f61 client: don't shorten default timeout with ASYNCDNS
With connected sockets recv() should fail immediately if chronyd is not
listening on localhost and with the Unix socket connecting should fail.
2015-09-30 14:35:05 +02:00
Miroslav Lichvar
38ac2b39ce stubs: add NSR_RefreshAddresses() 2015-09-30 13:33:27 +02:00
Miroslav Lichvar
967e358dbc stubs: don't call DNS_Name2IPAddress handler directly
Instead of calling the handler directly schedule a timeout with zero
delay for resolving to make the function behave similarly to the real
asynchronous resolver. This should prevent problems with code that
inadvertently depends on this behavior and which would break only when
compiled without support for asynchronous resolving.
2015-09-29 18:06:33 +02:00
Miroslav Lichvar
60721d2cc1 client: improve signal handling
After receiving a signal, don't process new command from readline() and
break from waitsync command.
2015-09-29 18:05:50 +02:00
Miroslav Lichvar
b698184939 doc: document refresh command 2015-09-29 18:05:45 +02:00
Miroslav Lichvar
c6c833fb9c client: update help text 2015-09-29 16:42:21 +02:00
Gautier PHILIPPON
3eb43f4619 cmdmon: add refresh command
This command can be used to resolve the names of configured sources to
IP addresses again.
2015-09-29 16:42:18 +02:00
Miroslav Lichvar
440c159217 client: fix compiler warning on extra printf argument 2015-09-29 16:28:28 +02:00
Miroslav Lichvar
b49dcfbef7 doc: update for recent changes 2015-09-25 19:08:01 +02:00
Miroslav Lichvar
a4d9cfaaeb client: update help text
Update the text for recent changes, add missing commands and indent the
description in the output.
2015-09-25 19:07:58 +02:00
Miroslav Lichvar
7b2430fc3c logging: don't save debugging arguments when debug is disabled
Don't save the facility number, line number, function name and filename
in the compiled binary unless the debugging support is enabled.
2015-09-24 18:32:23 +02:00
Miroslav Lichvar
bd8be7133d sys: use NetBSD driver on FreeBSD
The NetBSD driver now provides fast slewing using adjtime(), which
can be used on FreeBSD too.
2015-09-23 11:19:34 +02:00
Miroslav Lichvar
692ef0549b sys_netbsd: add fast slewing based on adjtime()
Implement slewing based on adjtime() that the generic driver can use to
correct offsets larger than 1 second with 5000 ppm slewing rate.
2015-09-23 11:19:34 +02:00
Miroslav Lichvar
d6fdae5f1d sys_generic: allow fast slewing with system driver
The system drivers may implement their own slewing which the generic
driver can use to slew faster than the maximum frequency the driver is
allowed to set directly.
2015-09-23 11:19:09 +02:00
Miroslav Lichvar
8feb37df2b sys_solaris: use timex driver
Remove driver functions based on adjtime() and switch to the new timex
driver. The kernel allows the timex frequency to be set in the full
range of int32_t, which gives a maximum frequency of 32768 ppm. Round
the limit to 32500 ppm.
2015-09-18 16:42:40 +02:00
Miroslav Lichvar
1d2b481069 sys_timex: set timex constant on Solaris
The kernel apparently checks the constant even when it's not being set
with MOD_TIMECONST and may return EINVAL on an uninitialized value.
2015-09-18 16:42:40 +02:00
Miroslav Lichvar
c062fa2fa9 client: fix binding of Unix socket on Solaris
bind() needs to be called before connect(), otherwise it fails with
EINVAL.
2015-09-18 16:42:40 +02:00
Miroslav Lichvar
f444561a10 fix building on Solaris
- a feature test macro is needed to get msg_control in struct msghdr
- variables must not be named sun to avoid conflict with a macro
- res_init() needs -lresolv
- configure tests for IPv6 and getaddrinfo need -lsocket -lnsl
- pid_t is defined as long and needs to be cast for %d format
2015-09-18 16:42:28 +02:00
Miroslav Lichvar
046f219a0e clean up sysincl.h more
Define feature test macros in config.h if needed.
2015-09-18 10:07:56 +02:00
Miroslav Lichvar
3cd32ed660 configure: check if C compiler works
Check if the C compiler works to get a useful error message when it
doesn't or it's missing. If the CC environment variable is not set, try
gcc and then cc.
2015-09-17 15:57:48 +02:00
Miroslav Lichvar
4f172f6f9f configure: prefix error messages 2015-09-17 15:57:48 +02:00
Miroslav Lichvar
22fc0a6846 configure: don't set any arch-specific CFLAGS 2015-09-17 15:57:48 +02:00
Miroslav Lichvar
71e596b443 configure: ignore architecture in system selection
Assume chrony can be compiled and work on all architectures supported by
the operating systems.
2015-09-17 15:57:48 +02:00
Miroslav Lichvar
98c245ed7b sys: drop SunOS driver
On FreeBSD is used the new timex driver and SunOS 4 is not supported
anymore.
2015-09-17 15:57:48 +02:00
Miroslav Lichvar
bf57222e96 sys: use timex driver on FreeBSD
Switch from the SunOS adjtime() based driver to the timex driver.
There is no FreeBSD-specific code, so call SYS_Timex_Initialise()
and SYS_Timex_Finalise() directly from sys.c.
2015-09-17 15:57:48 +02:00
Miroslav Lichvar
c075c070f0 clean up sysincl.h 2015-09-17 15:57:44 +02:00
Miroslav Lichvar
4bc6950632 drop WINNT-specific code
This was never really supported and it would probably require a lot of
work to get a usable chronyd in Cygwin. Remove all WINNT-specific code.
2015-09-17 15:52:49 +02:00
Miroslav Lichvar
bde279c093 sys: don't allow empty SYS_Initialise()/SYS_Finalise()
Require one system-specific macro to be defined to always call an
initialization/finalization function.
2015-09-17 15:52:49 +02:00
Miroslav Lichvar
4f6ab8ac93 sys: move DRIFT_REMOVAL_INTERVAL definition
In the SunOS and Solaris drivers DRIFT_REMOVAL_INTERVAL needs to be
defined before it's used. This was broken in commit
b6a27df5b9.
2015-09-17 15:52:49 +02:00
Miroslav Lichvar
d2d82e2e5f sys_netbsd: use timex driver
Remove the driver functions based on adjtime() and switch to the new
timex driver, which is based on ntp_adjtime(). This allows chronyd to
control the kernel frequency, adjust the offset with sub-microsecond
accuracy, and set the kernel leap and sync status. A drawback is that
the maximum slew rate is now limited by the 500 ppm maximum frequency
offset, while adjtime() on NetBSD slewed by up to 5000 ppm.
2015-09-17 15:52:49 +02:00
Miroslav Lichvar
1b2510e4b2 sys_linux: use timex driver
Remove functions that are included in the new timex driver. Keep only
functions that have extended functionality, i.e. read and set the
frequency using the timex tick field and apply step offset with
ADJ_SETOFFSET.

Merge the code from wrap_adjtimex.c that is still needed with
sys_linux.c and remove the file.
2015-09-17 15:52:49 +02:00
Miroslav Lichvar
e735be59a7 sys: add generic timex driver
This is based on sys_linux.c and wrap_adjtimex.c. It's intended for all
systems that support the adjtimex() or ntp_adjtime() system call. The
driver functions can be replaced with extended system-specific versions
(e.g. to control the frequency with the tick field on Linux).
2015-09-17 15:52:49 +02:00
Miroslav Lichvar
5190539ce1 test: add tests for system adjtime() and ntp_adjtime()
Include a test program to determine how the adjtime() implementation
behaves. Check the range of supported offset, support for readonly
operation, and slew rate with different update intervals and offsets.

Also, add a test for ntp_adjtime() to check what frequency range it
supports.
2015-09-17 10:56:51 +02:00
Miroslav Lichvar
5776eb35b6 git: use absolute paths in .gitignore 2015-09-14 16:54:04 +02:00
Miroslav Lichvar
f102acd423 sys_linux: allow uname in seccomp filter
It may be called from res_init() apparently.
2015-09-14 16:53:25 +02:00
Miroslav Lichvar
06486f3162 util: print expected uid/gid in UTI_CheckDirPermissions() 2015-09-09 17:19:07 +02:00
Miroslav Lichvar
1619453b2b sys_linux: allow setting IP_FREEBIND option in seccomp filter
This is needed when chronyd is started with no allow directive, but the
NTP server socket is opened by the allow command later.
2015-09-09 17:19:07 +02:00
Miroslav Lichvar
5a40950ffd test: extend compilation/001-features 2015-09-09 17:19:07 +02:00
Miroslav Lichvar
16eb18e797 stubs: add CAM_OpenUnixSocket()
It is needed to build with disabled cmdmon.
2015-09-09 17:19:07 +02:00
Miroslav Lichvar
7bf0684557 configure: add --disable-scfilter option 2015-09-09 17:19:07 +02:00
Miroslav Lichvar
961c490436 configure: update chronyc feature list 2015-09-09 17:19:07 +02:00
Miroslav Lichvar
d8b0a4a288 doc: update section on isolated networks
Since the NTPv4 update, the detection of synchronization loops based on
the refid prevents a server to initialize its clock from its clients
after restart. Remove that part from the recommended configuration.
Also, mention the time smoothing feature.
2015-09-09 16:46:09 +02:00
Miroslav Lichvar
76d12ac136 doc: update for recent changes 2015-09-09 13:33:34 +02:00
Miroslav Lichvar
434faeecb8 sys_linux: add support for seccomp filters
The Linux secure computing (seccomp) facility allows a process to
install a filter in the kernel that will allow only specific system
calls to be made. The process is killed when trying to make other system
calls. This is useful to reduce the kernel attack surface and possibly
prevent kernel exploits when the process is compromised.

Use the libseccomp library to add rules and load the filter into the
kernel. Keep a list of system calls that are always allowed after
chronyd is initialized. Restrict arguments that may be passed to the
socket(), setsockopt(), fcntl(), and ioctl() system calls. Arguments
to socketcall(), which is used on some architectures as a multiplexer
instead of separate socket system calls, are not restricted for now.
The mailonchange directive is not allowed as it calls sendmail.

Calls made by the libraries that chronyd is using have to be covered
too. It's difficult to determine which system calls they need as it may
change after an upgrade and it may depend on their configuration (e.g.
resolver in libc). There are also differences between architectures. It
can all break very easily and is therefore disabled by default. It can
be enabled with the new -F option.

This is based on a patch from Andrew Griffiths <agriffit@redhat.com>.
2015-09-04 17:56:51 +02:00
Miroslav Lichvar
ea2858b323 main: install signal handler sooner 2015-09-04 17:03:00 +02:00
Miroslav Lichvar
3391d5f846 doc: fix typo in chronyd man page 2015-09-04 17:03:00 +02:00
Miroslav Lichvar
7d6de7afe6 rtc: fix setting time from driftfile when RTC reading fails
Fix RTC_Linux_TimePreInit() to return 0 when the RTC device can be
opened, but reading its time fails to at least have the time restored
from the driftfile.
2015-09-03 11:42:58 +02:00
Bryan Christianson
67ce6bd279 sys_macosx: reset drift removal timer after spike in offset_sd
When a large spike occurs in offset_sd the drift removal interval can be
set to an excessively long time, although what ever event caused the
perturbation has passed. At the next set_sync_status() we now compare
the expected drift removal interval with that currently in effect. If
they are significantly different, the current timer is cancelled and new
cycle started using the new drift removal interval.
2015-08-31 16:29:24 +02:00
Miroslav Lichvar
770db1fe02 sys_linux: always call TMX_SetLeap() in set_leap()
The optimization avoiding unnecessary setting of the kernel leap status
can cause a problem when something outside chronyd sets the status to
the new expected value. There will be no TMX_SetLeap() call which would
update the saved status and the kernel status will be overwritten with
the old (incorrect) value in a later TMX_*() call.

Always call TMX_SetLeap() to save the new value and for the log message
selection just check if a leap second has been applied.
2015-08-27 13:26:12 +02:00
Miroslav Lichvar
d73394dde1 reference: call LCL_SetSystemLeap() only on leap changes 2015-08-26 14:42:14 +02:00
Miroslav Lichvar
eb0c7e33d2 examples: update for removed cmdmon authentication 2015-08-26 10:19:33 +02:00
Bryan Christianson
b9cfdaf666 sys_macosx: add option to run chronyd as real-time process
Adds option -P to chronyd on MacOS X which can be used to enable the
thread time constraint scheduling policy. This near real-time scheduling
policy removes a 1usec bias from the 'System time' offset.
2015-08-25 17:43:57 +02:00
Miroslav Lichvar
5039f959e0 sources: add option to limit selection by root distance
Add maxdistance directive to set the maximum root distance the sources
are allowed to have to be selected. This is useful to reject NTPv4
sources that are no longer synchronized and report large dispersion.
The default value is 3 seconds.
2015-08-25 17:35:34 +02:00
Miroslav Lichvar
b7a54f8cd8 configure: add new options to disable dropping root privileges 2015-08-25 17:09:55 +02:00
Miroslav Lichvar
7b6435b2b8 sys_netbsd: allow running without root privileges
On NetBSD programs with write access to /dev/clockctl can adjust or set
the system clock without the root privileges. Add a function to drop the
privileges and check if the process has write access to the device to
get a more descriptive error message when the chrony uid/gid doesn't
match the owner of the device.
2015-08-25 17:09:55 +02:00
Miroslav Lichvar
8854c00d48 main: open cmdmon and NTP internet sockets before dropping root
Call the CAM, NIO, NCR initialization functions and setup the access
restrictions before root is dropped. This will be needed on NetBSD,
where it's not possible to bind sockets to privileged ports without the
root privileges. Split the creation of the Unix domain command socket
from the CAM initialization to keep the chrony user as the owner of the
socket.
2015-08-25 17:09:18 +02:00
Miroslav Lichvar
c0867b58f5 conf: allow wildcard patterns in include directive
Use glob() to match and read multiple configuration files with one
include directive.
2015-08-24 15:25:02 +02:00
Miroslav Lichvar
05183748a8 conf: extend logging in CNF_ReadFile() 2015-08-24 14:57:39 +02:00
Miroslav Lichvar
e56154a687 sys_linux: remove unused variables 2015-08-24 13:25:39 +02:00
Miroslav Lichvar
e5784c1ca8 cmdmon: update candm.h
Remove the auth fields in the command request/reply and replace the
token and utoken fields with padding.
2015-08-21 13:26:46 +02:00
Miroslav Lichvar
282a9c7d7c keys: remove support for command key
Without the cmdmon authentication, there is no need for command keys.
2015-08-21 13:26:46 +02:00
Miroslav Lichvar
b11ca92ca6 client: remove authentication support
Follow the removal of the server authentication support and remove also
the client support. The -a and -f options are now silently ignored to
not break scripts. The authhash and password commands print a warning,
but they don't return an error.
2015-08-21 13:26:46 +02:00
Miroslav Lichvar
49846b3e68 cmdmon: remove authentication support
With the new support for cmdmon over Unix domain sockets, authentication
is no longer necessary to authorize a client running on localhost with
the permissions of the root or chrony user/group. Remove the cmdmon
authentication support to simplify the code and significantly reduce the
attack surface of the protocol.

Only monitoring commands are now allowed remotely. Users that need to
configure chronyd remotely or locally without root/chrony permissions
are advised to use ssh and/or sudo.
2015-08-21 13:26:46 +02:00
Miroslav Lichvar
0887824324 cmdmon: allow unauthenticated commands from Unix domain socket
Allow all commands received from the Unix domain command socket (which
is accessible only by the root and chrony user/group), even when they
are not authenticated with the command key.
2015-08-21 13:26:46 +02:00
Miroslav Lichvar
fbe65f2c71 client: connect to Unix domain socket by default
The default value of the -h option is now
/var/run/chrony/chronyd.sock,127.0.0.1,::1.
2015-08-21 13:26:46 +02:00
Miroslav Lichvar
eb5a412bed configure: add option to set default location of Unix domain sockets 2015-08-21 13:26:46 +02:00
Miroslav Lichvar
0cc8f68754 client: reconnect with multiple addresses
Allow multiple hostnames/addresses separated by comma to be specified
with the -h option. Hostnames are resolved to up to 16 addresses. When
connecting to an address fails or no reply is received, try the next
address in the list.

Set the default value for the -h option to 127.0.0.1,::1.
2015-08-21 13:26:45 +02:00
Miroslav Lichvar
7079ca2718 client: allow connecting to Unix domain sockets
If the specified hostname starts with /, consider it to be the path of
the chronyd Unix domain command socket. Create the client socket in the
same directory as the server socket (which is not accessible by others)
and change its permission to 0666 to allow chronyd running without root
privileges to send a reply. Remove the socket on exit.
2015-08-21 13:26:45 +02:00
Miroslav Lichvar
70ad0bc573 client: connect socket
Call connect() on the socket to set the remote address and switch from
sendto()/recvfrom() to send()/recv(). Setting the IP_RECVERR option no
longer seems to be necessary in order to get ECONNREFUSED errors.
2015-08-21 13:26:45 +02:00
Miroslav Lichvar
22345c5ddf client: add -d option to print debug messages 2015-08-21 13:26:45 +02:00
Miroslav Lichvar
28b0a23949 client: convert disabled printf() calls to debug messages 2015-08-21 13:26:45 +02:00
Miroslav Lichvar
1b57a796b1 client: use LOG macro for error messages 2015-08-21 13:26:45 +02:00
Miroslav Lichvar
0abb470022 cmdmon: print path of Unix command socket in debug messages 2015-08-20 14:35:40 +02:00
Miroslav Lichvar
b7a4b84f0a cmdmon: fix handling of packets from unbound Unix sockets
When a packet is received from an unbound Unix domain socket, recvfrom()
may return with zero addrlen.
2015-08-20 14:35:40 +02:00
Bryan Christianson
794a1e6cfe contrib: add Mac OS X support files
launchd plist files for chronyd and logrotation.
shell script for logrotation
README file with detailed installation instructions
2015-08-20 14:35:26 +02:00
Bryan Christianson
7c4db99d44 sys_macosx: make drift removal interval dynamic
Adjust the drift removal interval based on the observed offset_sd.
A newly calculated interval goes into effect after the current drift
removal has completed. When offset_sd is high, the interval is increased
resulting in fewer wakeups to adjust the drift offset. At lower values
of offset_sd the drift removal adjustment interval is pinned to 0.5
seconds. The predicted error applied at the start of an adjustment is
based on the remaining time of the drift removal that is currently in
effect. Default drift removal adjustment interval is 4.0 seconds (was
1.0). If not synchronised set interval to  maximum of default interval
and current interval. Clamp elapsed drift removal time to
[0, current_drift_removal_interval] to cover clock stepping.
2015-08-18 10:43:20 +02:00
Miroslav Lichvar
30b6213910 util: set uid/gid of created directory even when zero
Call chown() in create_dir() even when the specified uid/gid is zero.
This is needed on BSD systems, where directories are created with gid
of the parent directory.
2015-08-13 17:15:50 +02:00
Miroslav Lichvar
b6a27df5b9 sys: include predicted drift in adjtime() offset
In drivers with periodic drift removal include in the adjustment also a
prediction of the error gained in half of the interval to move the mean
offset of the clock closer to zero. E.g. offset of a stable clock
drifting by 10 ppm should now be closer to 0 +/- 5 microseconds instead
of 5 +/- 5 microseconds.
2015-08-12 16:09:24 +02:00
Miroslav Lichvar
18d514d552 sys: define NETBSD macro on NetBSD 2015-08-12 14:45:23 +02:00
Miroslav Lichvar
f1ed08abf0 conf: create directory for Unix domain command socket
Try to create the directory where will be the Unix domain command socket
bound to allow starting with empty /var/run. Check the permissions and
owner/group in case the directory already existed. It MUST NOT be
accessible by others as permissions on Unix domain sockets are ignored
on some systems (e.g. Solaris).
2015-08-12 14:45:23 +02:00
Miroslav Lichvar
6d42dd8603 conf: create directories before dropping root
Create logdir and dumpdir before dropping root. Set their uid/gid to the
user chronyd will switch to. This allows chronyd to create the
directories in a directory where the user won't have write permissions
(e.g. /var/lib).
2015-08-12 14:45:20 +02:00
Miroslav Lichvar
e7100e106d main: always call getpwnam()
Don't hardcode root as the user with zero uid/gid.
2015-08-12 14:34:28 +02:00
Miroslav Lichvar
6402350c83 sys: move getpwnam() call to main.c
Pass uid/gid instead of user name to the root dropping function.
2015-08-10 16:06:39 +02:00
Miroslav Lichvar
236576c124 util: add mode, uid, gid parameters to UTI_CreateDirAndParents() 2015-08-10 14:56:17 +02:00
Miroslav Lichvar
9a83cab2f8 util: don't try to create current directory
This prevents error messages when running chronyd -d/-q/-Q with default
logdir in a directory chronyd is not allowed do access after dropping
the root privileges.
2015-08-05 18:07:39 +02:00
Miroslav Lichvar
92706b158e move mkdirpp code to util.c 2015-08-05 18:07:39 +02:00
Miroslav Lichvar
ad34b26955 client: check if memory allocation fails 2015-08-05 18:07:39 +02:00
Miroslav Lichvar
12c434fdc0 client: add logging function to allow linking with memory.o 2015-08-05 18:07:39 +02:00
Miroslav Lichvar
9ceaef6479 doc: update FAQ 2015-08-05 11:26:26 +02:00
Bryan Christianson
abb56bded2 sys: add drift removal to Mac OS X driver
The darwin kernel implementation of adjtime() does not require the
adjustment to be aligned to a tickadj boundary, and we can apply
adjustments to the nearest microsecond. Rounding is accounted for by
adding any rounding errors back into the offset.
2015-08-03 17:28:19 +02:00
Miroslav Lichvar
0bcd10560a cmdmon: listen on Unix domain socket
In addition to the IPv4/IPv6 command sockets, create also a Unix domain
socket to process cmdmon requests. For now, there is no difference for
authorized commands, packets from all sockets need to be authenticated.

The default path of the socket is /var/run/chrony/chronyd.sock. It can
be configured with the bindcmdaddress directive with an address starting
with /.
2015-07-28 15:29:30 +02:00
Miroslav Lichvar
46b7148f3b clientlog: refactor CLG_Log*Access functions a bit 2015-07-28 14:46:03 +02:00
Miroslav Lichvar
37732130e1 clientlog: allow unspecified address in CLG_Log*Access functions 2015-07-28 14:32:54 +02:00
Miroslav Lichvar
7a3b1414cd util: add function to get sockaddr family name 2015-07-28 13:11:00 +02:00
Miroslav Lichvar
a4a21c1dca client: handle signals
Add a signal handler and rework the code to go through close_io() even
when terminated by a signal. This will allow chronyc to remove Unix
domain sockets on exit.
2015-07-28 11:57:57 +02:00
Miroslav Lichvar
206e597b04 util: use sigaction() to set signal handler 2015-07-28 11:57:57 +02:00
Miroslav Lichvar
ceef8ad2d8 main: move signal handler setting to util.c 2015-07-28 11:57:57 +02:00
Miroslav Lichvar
2d581a6a86 cmdmon: add debug messages for receiving/sending packets 2015-07-28 11:57:57 +02:00
Miroslav Lichvar
82f7fa3887 util: remove INLINE_UTILITIES support 2015-07-28 11:57:57 +02:00
Miroslav Lichvar
f88a01e8c7 remove getdate.c from repository
Building from repository now requires installed bison, but released
tarballs will still include a generated getdate.c.
2015-07-28 11:57:57 +02:00
Miroslav Lichvar
ca8e03b785 include config.h in all compiled files
After running configure script (new config.h written), all objects
should be recompiled.
2015-07-28 11:57:57 +02:00
Miroslav Lichvar
15932c9d7b sys: add new log message for kernel status reset after leap second
When a leap second is applied by the kernel, it doesn't actually clear
the STA_INS|STA_DEL bits from the status word, but the state returned
by ntp_adjtime()/adjtimex() is TIME_WAIT until the application clears
the bits.

Add "System clock status reset after leap second" log message for this
case.
2015-07-27 12:35:21 +02:00
Bryan Christianson
0fc0f906e1 util: fix rounding of negative numbers in UTI_DoubleToTimeval() 2015-07-23 14:53:00 +02:00
Miroslav Lichvar
7f58852ec0 util: fix UTI_Log2ToDouble() for maximum/minimum exponent 2015-07-23 12:04:27 +02:00
Miroslav Lichvar
85a9a53e69 configure: replace echo -n with printf
POSIX doesn't require echo to support -n.
2015-07-23 11:27:16 +02:00
Miroslav Lichvar
aa0c0fc401 make_release: don't package chrony.txt 2015-07-22 18:14:40 +02:00
Miroslav Lichvar
0e694e08fc makefile: install chrony.txt in install-doc only
Don't install chrony.txt in make install to avoid dependency on makeinfo
since chrony.texi is prepared by configure to set the default paths in
the documentation.
2015-07-22 17:42:42 +02:00
Miroslav Lichvar
c2ddcc9f36 makefile: don't install COPYING and README 2015-07-22 17:38:00 +02:00
Miroslav Lichvar
7a7cf6a5ce doc: update NEWS 2015-06-23 16:02:17 +02:00
Miroslav Lichvar
c2f83bd8a4 sys: fix clock stepping by integer number of seconds on Linux
The kernel requires in the ADJ_SETOFFSET | ADJ_NANO mode that the
timex.time.tv_usec value is smaller than 10^9 nanosecond, which wasn't
the case with a negative integer offset (e.g. inserted leap second).
2015-06-23 15:08:42 +02:00
Miroslav Lichvar
1f0e6296c6 doc: update NEWS 2015-06-22 12:54:52 +02:00
Miroslav Lichvar
ab1f01bacd ntp: use specific reference ID when smoothing served time
Set refid in server/broadcast packets to 127.127.1.255 when a time
smoothing offset is applied to the timestamps. This allows the clients
and administrators to detect that the server is not serving its best
estimate of the true time.
2015-06-22 12:54:50 +02:00
Miroslav Lichvar
b9b896d8e7 ntp: remove unnecessary casting 2015-06-22 12:54:33 +02:00
Miroslav Lichvar
6be54f366c reference: move definition of special refids to ntp.h 2015-06-22 12:54:22 +02:00
Miroslav Lichvar
802cdb3230 test: require latest clknetsim 2015-06-19 14:20:58 +02:00
Miroslav Lichvar
7e27880cb6 doc: update leapsecmode and smoothtime descriptions 2015-06-19 14:20:58 +02:00
Miroslav Lichvar
d3ad85aa43 doc: add Mac OS X to supported platforms 2015-06-18 16:35:26 +02:00
Miroslav Lichvar
59192fc695 update copyright years 2015-06-18 15:30:22 +02:00
Miroslav Lichvar
9095b80c5b doc: refer to authhash command in password command description 2015-06-17 18:31:16 +02:00
Miroslav Lichvar
ed5b78bf09 doc: convert FAQ to AsciiDoc and update it
It's now in a separate file again.
2015-06-17 18:05:14 +02:00
Bryan Christianson
d6aafa3f64 sys: MacOS X driver ported from NetBSD 2015-06-15 14:40:54 +02:00
Miroslav Lichvar
8de04a808d ntp: add debug message to print number of resolved addresses 2015-06-15 13:03:11 +02:00
260 changed files with 33901 additions and 18627 deletions

35
.gitignore vendored
View File

@@ -1,21 +1,28 @@
.deps
.vimrc
*.gcda
*.gcno
*.o
*.swp
RELEASES
Makefile
chrony.conf.5
chrony.info
chrony.html
chrony.texi
chrony.txt
chronyc
chronyc.1
chronyd
chronyd.8
config.h
config.log
*.dSYM
*.DS_Store
core.*
tags
version.h
/RELEASES
/Makefile
/chronyc
/chronyd
/config.h
/config.log
/doc/Makefile
/doc/*.html
/doc/*.man
/doc/*.man.in
/doc/*.txt
/getdate.c
/version.h
/test/simulation/clknetsim
/test/simulation/tmp
/test/unit/Makefile
/test/unit/*.test
/test/unit/*.o

View File

@@ -21,66 +21,60 @@
#
# Makefile template
SYSCONFDIR=@SYSCONFDIR@
BINDIR=@BINDIR@
SBINDIR=@SBINDIR@
MANDIR=@MANDIR@
INFODIR=@INFODIR@
DOCDIR=@DOCDIR@
LOCALSTATEDIR=@LOCALSTATEDIR@
CHRONYVARDIR=@CHRONYVARDIR@
SYSCONFDIR = @SYSCONFDIR@
BINDIR = @BINDIR@
SBINDIR = @SBINDIR@
LOCALSTATEDIR = @LOCALSTATEDIR@
CHRONYVARDIR = @CHRONYVARDIR@
DESTDIR =
CC = @CC@
CFLAGS = @CFLAGS@
CPPFLAGS = @CPPFLAGS@
DESTDIR=
HASH_OBJ = @HASH_OBJ@
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o mkdirpp.o \
reference.o regress.o rtc.o sched.o sources.o sourcestats.o stubs.o \
sys.o smooth.o tempcomp.o util.o $(HASH_OBJ)
EXTRA_OBJS=@EXTRA_OBJECTS@
CLI_OBJS = client.o nameserv.o getdate.o cmdparse.o \
pktlength.o util.o $(HASH_OBJ)
ALL_OBJS = $(OBJS) $(EXTRA_OBJS) $(CLI_OBJS)
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
EXTRA_LIBS=@EXTRA_LIBS@
EXTRA_CLI_LIBS=@EXTRA_CLI_LIBS@
EXTRA_OBJS = @EXTRA_OBJS@
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \
reference.o regress.o rtc.o samplefilt.o sched.o socket.o sources.o sourcestats.o \
stubs.o smooth.o sys.o sys_null.o tempcomp.o util.o $(EXTRA_OBJS)
EXTRA_CLI_OBJS = @EXTRA_CLI_OBJS@
CLI_OBJS = array.o client.o cmdparse.o getdate.o memory.o nameserv.o \
pktlength.o socket.o util.o $(EXTRA_CLI_OBJS)
ALL_OBJS = $(OBJS) $(CLI_OBJS)
LIBS = @LIBS@
EXTRA_LIBS = @EXTRA_LIBS@
EXTRA_CLI_LIBS = @EXTRA_CLI_LIBS@
# Until we have a main procedure we can link, just build object files
# to test compilation
all : chronyd chronyc
chronyd : $(OBJS) $(EXTRA_OBJS)
$(CC) $(CFLAGS) -o chronyd $(OBJS) $(EXTRA_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS)
chronyd : $(OBJS)
$(CC) $(CFLAGS) -o chronyd $(OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS)
chronyc : $(CLI_OBJS)
$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS)
client.o : client.c
$(CC) $(CFLAGS) $(CPPFLAGS) @READLINE_COMPILE@ -c $<
$(HASH_OBJ) : $(patsubst %.o,%.c,$(HASH_OBJ))
$(CC) $(CFLAGS) $(CPPFLAGS) @HASH_COMPILE@ -c $<
distclean : clean
-rm -f Makefile
-rm -f chrony.conf.5 chrony.texi chronyc.1 chronyd.8
$(MAKE) -C doc distclean
$(MAKE) -C test/unit distclean
-rm -f .DS_Store
-rm -f Makefile config.h config.log
clean :
-rm -f *.o *.s chronyc chronyd core *~ chrony.info chrony.html chrony.txt
$(MAKE) -C test/unit clean
-rm -f *.o *.s chronyc chronyd core.* *~
-rm -f *.gcda *.gcno
-rm -rf .deps
-rm -rf *.dSYM
getdate.c :
getdate.c : getdate.y
bison -o getdate.c getdate.y
# This can be used to force regeneration of getdate.c
@@ -90,15 +84,10 @@ getdate :
# For install, don't use the install command, because its switches
# seem to vary between systems.
install: chronyd chronyc chrony.txt
install: chronyd chronyc
[ -d $(DESTDIR)$(SYSCONFDIR) ] || mkdir -p $(DESTDIR)$(SYSCONFDIR)
[ -d $(DESTDIR)$(SBINDIR) ] || mkdir -p $(DESTDIR)$(SBINDIR)
[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
[ -d $(DESTDIR)$(DOCDIR) ] || mkdir -p $(DESTDIR)$(DOCDIR)
[ -d $(DESTDIR)$(MANDIR)/man1 ] || mkdir -p $(DESTDIR)$(MANDIR)/man1
[ -d $(DESTDIR)$(MANDIR)/man5 ] || mkdir -p $(DESTDIR)$(MANDIR)/man5
[ -d $(DESTDIR)$(MANDIR)/man8 ] || mkdir -p $(DESTDIR)$(MANDIR)/man8
[ -d $(DESTDIR)$(DOCDIR) ] || mkdir -p $(DESTDIR)$(DOCDIR)
[ -d $(DESTDIR)$(CHRONYVARDIR) ] || mkdir -p $(DESTDIR)$(CHRONYVARDIR)
if [ -f $(DESTDIR)$(SBINDIR)/chronyd ]; then rm -f $(DESTDIR)$(SBINDIR)/chronyd ; fi
if [ -f $(DESTDIR)$(BINDIR)/chronyc ]; then rm -f $(DESTDIR)$(BINDIR)/chronyc ; fi
@@ -106,18 +95,13 @@ install: chronyd chronyc chrony.txt
chmod 755 $(DESTDIR)$(SBINDIR)/chronyd
cp chronyc $(DESTDIR)$(BINDIR)/chronyc
chmod 755 $(DESTDIR)$(BINDIR)/chronyc
cp chrony.txt $(DESTDIR)$(DOCDIR)/chrony.txt
chmod 644 $(DESTDIR)$(DOCDIR)/chrony.txt
cp COPYING $(DESTDIR)$(DOCDIR)/COPYING
chmod 644 $(DESTDIR)$(DOCDIR)/COPYING
cp README $(DESTDIR)$(DOCDIR)/README
chmod 644 $(DESTDIR)$(DOCDIR)/README
cp chronyc.1 $(DESTDIR)$(MANDIR)/man1
chmod 644 $(DESTDIR)$(MANDIR)/man1/chronyc.1
cp chronyd.8 $(DESTDIR)$(MANDIR)/man8
chmod 644 $(DESTDIR)$(MANDIR)/man8/chronyd.8
cp chrony.conf.5 $(DESTDIR)$(MANDIR)/man5
chmod 644 $(DESTDIR)$(MANDIR)/man5/chrony.conf.5
$(MAKE) -C doc install
docs :
$(MAKE) -C doc docs
install-docs :
$(MAKE) -C doc install-docs
%.o : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $<
@@ -125,30 +109,18 @@ install: chronyd chronyc chrony.txt
%.s : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -S $<
check : chronyd chronyc
quickcheck : chronyd chronyc
$(MAKE) -C test/unit check
cd test/simulation && ./run
cd test/system && ./run
install-docs : docs
[ -d $(DESTDIR)$(DOCDIR) ] || mkdir -p $(DESTDIR)$(DOCDIR)
cp chrony.txt $(DESTDIR)$(DOCDIR)/chrony.txt
chmod 644 $(DESTDIR)$(DOCDIR)/chrony.txt
cp chrony.html $(DESTDIR)$(DOCDIR)/chrony.html
chmod 644 $(DESTDIR)$(DOCDIR)/chrony.html
[ -d $(DESTDIR)$(INFODIR) ] || mkdir -p $(DESTDIR)$(INFODIR)
cp chrony.info* $(DESTDIR)$(INFODIR)
chmod 644 $(DESTDIR)$(INFODIR)/chrony.info*
check : chronyd chronyc
$(MAKE) -C test/unit check
cd test/simulation && ./run -i 20 -m 2
cd test/system && ./run
docs : chrony.txt chrony.html chrony.info
chrony.txt : chrony.texi
makeinfo --no-headers --number-sections -o chrony.txt chrony.texi
chrony.html : chrony.texi
command -v texi2html > /dev/null 2>&1 && texi2html chrony.texi || \
makeinfo --no-split --html --number-sections -o chrony.html chrony.texi
chrony.info : chrony.texi
makeinfo chrony.texi
print-chronyd-objects :
@echo $(OBJS)
Makefile : Makefile.in configure
@echo

294
NEWS
View File

@@ -1,12 +1,306 @@
New in version 4.0
==================
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 -L option to limit log messages by severity
* Avoid replacing NTP sources with unreachable addresses
* Improve NTP loop test to prevent synchronisation to itself
* Update clock synchronisation status and leap status more frequently
* Update seccomp filter
* Add "add pool" command
* Add -N option and sourcename command to print original names of sources
* Add -a option to source/sourcestats command to print unresolved sources
* Add reset command to drop all measurements
Bug fixes
---------
* Handle RTCs that don't support interrupts
* Respond to command requests with correct address on multihomed hosts
Removed features
----------------
* Drop support for RIPEMD keys (RMD128, RMD160, RMD256, RMD320)
New in version 3.5
==================
Enhancements
------------
* Add support for more accurate reading of PHC on Linux 5.0
* Add support for hardware timestamping on interfaces with read-only
timestamping configuration
* Add support for memory locking and real-time priority on FreeBSD,
NetBSD, Solaris
* Update seccomp filter to work on more architectures
* Validate refclock driver options
Bug fixes
---------
* Fix bindaddress directive on FreeBSD
* Fix transposition of hardware RX timestamp on Linux 4.13 and later
* Fix building on non-glibc systems
New in version 3.4
==================
Enhancements
------------
* Add filter option to server/pool/peer directive
* Add minsamples and maxsamples options to hwtimestamp directive
* Add support for faster frequency adjustments in Linux 4.19
* Change default pidfile to /var/run/chrony/chronyd.pid to allow
chronyd without root privileges to remove it on exit
* Disable sub-second polling intervals for distant NTP sources
* Extend range of supported sub-second polling intervals
* Get/set IPv4 destination/source address of NTP packets on FreeBSD
* Make burst options and command useful with short polling intervals
* Modify auto_offline option to activate when sending request failed
* Respond from interface that received NTP request if possible
* Add onoffline command to switch between online and offline state
according to current system network configuration
* Improve example NetworkManager dispatcher script
Bug fixes
---------
* Avoid waiting in Linux getrandom system call
* Fix PPS support on FreeBSD and NetBSD
New in version 3.3
==================
Enhancements
------------
* Add burst option to server/pool directive
* Add stratum and tai options to refclock directive
* Add support for Nettle crypto library
* Add workaround for missing kernel receive timestamps on Linux
* Wait for late hardware transmit timestamps
* Improve source selection with unreachable sources
* Improve protection against replay attacks on symmetric mode
* Allow PHC refclock to use socket in /var/run/chrony
* Add shutdown command to stop chronyd
* Simplify format of response to manual list command
* Improve handling of unknown responses in chronyc
Bug fixes
---------
* Respond to NTPv1 client requests with zero mode
* Fix -x option to not require CAP_SYS_TIME under non-root user
* Fix acquisitionport directive to work with privilege separation
* Fix handling of socket errors on Linux to avoid high CPU usage
* Fix chronyc to not get stuck in infinite loop after clock step
New in version 3.2
==================
Enhancements
------------
* Improve stability with NTP sources and reference clocks
* Improve stability with hardware timestamping
* Improve support for NTP interleaved modes
* Control frequency of system clock on macOS 10.13 and later
* Set TAI-UTC offset of system clock with leapsectz directive
* Minimise data in client requests to improve privacy
* Allow transmit-only hardware timestamping
* Add support for new timestamping options introduced in Linux 4.13
* Add root delay, root dispersion and maximum error to tracking log
* Add mindelay and asymmetry options to server/peer/pool directive
* Add extpps option to PHC refclock to timestamp external PPS signal
* Add pps option to refclock directive to treat any refclock as PPS
* Add width option to refclock directive to filter wrong pulse edges
* Add rxfilter option to hwtimestamp directive
* Add -x option to disable control of system clock
* Add -l option to log to specified file instead of syslog
* Allow multiple command-line options to be specified together
* Allow starting without root privileges with -Q option
* Update seccomp filter for new glibc versions
* Dump history on exit by default with dumpdir directive
* Use hardening compiler options by default
Bug fixes
---------
* Don't drop PHC samples with low-resolution system clock
* Ignore outliers in PHC tracking, RTC tracking, manual input
* Increase polling interval when peer is not responding
* Exit with error message when include directive fails
* Don't allow slash after hostname in allow/deny directive/command
* Try to connect to all addresses in chronyc before giving up
New in version 3.1
==================
Enhancements
------------
* Add support for precise cross timestamping of PHC on Linux
* Add minpoll, precision, nocrossts options to hwtimestamp directive
* Add rawmeasurements option to log directive and modify measurements
option to log only valid measurements from synchronised sources
* Allow sub-second polling interval with NTP sources
Bug fixes
---------
* Fix time smoothing in interleaved mode
New in version 3.0
==================
Enhancements
------------
* Add support for software and hardware timestamping on Linux
* Add support for client/server and symmetric interleaved modes
* Add support for MS-SNTP authentication in Samba
* Add support for truncated MACs in NTPv4 packets
* Estimate and correct for asymmetric network jitter
* Increase default minsamples and polltarget to improve stability
with very low jitter
* Add maxjitter directive to limit source selection by jitter
* Add offset option to server/pool/peer directive
* Add maxlockage option to refclock directive
* Add -t option to chronyd to exit after specified time
* Add partial protection against replay attacks on symmetric mode
* Don't reset polling interval when switching sources to online state
* Allow rate limiting with very short intervals
* Improve maximum server throughput on Linux and NetBSD
* Remove dump files after start
* Add tab-completion to chronyc with libedit/readline
* Add ntpdata command to print details about NTP measurements
* Allow all source options to be set in add server/peer command
* Indicate truncated addresses/hostnames in chronyc output
* Print reference IDs as hexadecimal numbers to avoid confusion with
IPv4 addresses
Bug fixes
---------
* Fix crash with disabled asynchronous name resolving
New in version 2.4.1
====================
Bug fixes
---------
* Fix processing of kernel timestamps on non-Linux systems
* Fix crash with smoothtime directive
* Fix validation of refclock sample times
* Fix parsing of refclock directive
New in version 2.4
==================
Enhancements
------------
* Add orphan option to local directive for orphan mode compatible with ntpd
* Add distance option to local directive to set activation threshold
(1 second by default)
* Add maxdrift directive to set maximum allowed drift of system clock
* Try to replace NTP sources exceeding maximum distance
* Randomise source replacement to avoid getting stuck with bad sources
* Randomise selection of sources from pools on start
* Ignore reference timestamp as ntpd doesn't always set it correctly
* Modify tracking report to use same values as seen by NTP clients
* Add -c option to chronyc to write reports in CSV format
* Provide detailed manual pages
Bug fixes
---------
* Fix SOCK refclock to work correctly when not specified as last refclock
* Fix initstepslew and -q/-Q options to accept time from own NTP clients
* Fix authentication with keys using 512-bit hash functions
* Fix crash on exit when multiple signals are received
* Fix conversion of very small floating-point numbers in command packets
Removed features
----------------
* Drop documentation in Texinfo format
New in version 2.3
==================
Enhancements
------------
* Add support for NTP and command response rate limiting
* Add support for dropping root privileges on Mac OS X, FreeBSD, Solaris
* Add require and trust options for source selection
* Enable logchange by default (1 second threshold)
* Set RTC on Mac OS X with rtcsync directive
* Allow binding to NTP port after dropping root privileges on NetBSD
* Drop CAP_NET_BIND_SERVICE capability on Linux when NTP port is disabled
* Resolve names in separate process when seccomp filter is enabled
* Replace old records in client log when memory limit is reached
* Don't reveal local time and synchronisation state in client packets
* Don't keep client sockets open for longer than necessary
* Ignore poll in KoD RATE packets as ntpd doesn't always set it correctly
* Warn when using keys shorter than 80 bits
* Add keygen command to generate random keys easily
* Add serverstats command to report NTP and command packet statistics
Bug fixes
---------
* Fix clock correction after making step on Mac OS X
* Fix building on Solaris
New in version 2.2.1
====================
Security fixes
--------------
* Restrict authentication of NTP server/peer to specified key (CVE-2016-1567)
New in version 2.2
==================
Enhancements
------------
* Add support for configuration and monitoring over Unix domain socket
(accessible by root or chrony user when root privileges are dropped)
* Add support for system call filtering with seccomp on Linux (experimental)
* Add support for dropping root privileges on NetBSD
* Control frequency of system clock on FreeBSD, NetBSD, Solaris
* Add system leap second handling mode on FreeBSD, NetBSD, Solaris
* Add dynamic drift removal on Mac OS X
* Add support for setting real-time priority on Mac OS X
* Add maxdistance directive to limit source selection by root distance
(3 seconds by default)
* Add refresh command to get new addresses of NTP sources
* Allow wildcard patterns in include directive
* Restore time from driftfile with -s option if later than RTC time
* Add configure option to set default hwclockfile
* Add -d option to chronyc to enable debug messages
* Allow multiple addresses to be specified for chronyc with -h option
and reconnect when no valid reply is received
* Make check interval in waitsync command configurable
Bug fixes
---------
* Fix building on NetBSD, Solaris
* Restore time from driftfile with -s option if reading RTC failed
Removed features
----------------
* Drop support for authentication with command key (run-time configuration
is now allowed only for local users that can access the Unix domain socket)
New in version 2.1.1
====================
Bug fixes
---------
* Fix clock stepping by integer number of seconds on Linux
New in version 2.1
==================
Enhancements
------------
* Add support for Mac OS X
* Try to replace unreachable and falseticker servers/peers specified
by name like pool sources
* Add leaponly option to smoothtime directive to allow synchronised
leap smear between multiple servers
* Use specific reference ID when smoothing served time
* Add smoothing command to report time smoothing status
* Add smoothtime command to activate or reset time smoothing

174
README
View File

@@ -4,7 +4,7 @@ What is chrony?
===============
chrony is a versatile implementation of the Network Time Protocol (NTP).
It can synchronize the system clock with NTP servers, reference clocks
It can synchronise the system clock with NTP servers, reference clocks
(e.g. GPS receiver), and manual input using wristwatch and keyboard.
It can also operate as an NTPv4 (RFC 5905) server and peer to provide
a time service to other computers in the network.
@@ -14,10 +14,10 @@ intermittent network connections, heavily congested networks, changing
temperatures (ordinary computer clocks are sensitive to temperature),
and systems that do not run continuosly, or run on a virtual machine.
Typical accuracy between two machines on a LAN is in tens, or a few
hundreds, of microseconds; over the Internet, accuracy is typically
within a few milliseconds. With a good hardware reference clock
sub-microsecond accuracy is possible.
Typical accuracy between two machines synchronised over the Internet is
within a few milliseconds; on a LAN, accuracy is typically in tens of
microseconds. With hardware timestamping, or a hardware reference clock,
sub-microsecond accuracy may be possible.
Two programs are included in chrony, chronyd is a daemon that can be
started at boot time and chronyc is a command-line interface program
@@ -27,33 +27,29 @@ operating parameters whilst it is running.
What will chrony run on?
========================
The software is known to work on Linux, FreeBSD, NetBSD 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.
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.
How do I set it up?
===================
The file INSTALL gives instructions. On supported systems the
compilation process should be automatic.
You will need an ANSI C compiler -- gcc is recommended.
The manual (in texinfo and text formats) describes how to set the
software up for the less straightforward cases.
compilation process should be automatic. You will need a C compiler,
e.g. gcc or clang.
What documentation is there?
============================
A manual is supplied in Texinfo format (chrony.texi) and
ready-formatted plain text (chrony.txt) in the distribution.
The distribution includes manual pages and a document containing
Frequently Asked Questions (FAQ).
There is also information available on the chrony web pages, accessible
The documentation is also available on the chrony web pages, accessible
through the URL
http://chrony.tuxfamily.org/
https://chrony.tuxfamily.org/
Where are new versions announced?
=================================
@@ -84,141 +80,85 @@ 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
=======
chrony is distributed under the GNU General Public License version 2.
Author
======
Authors
=======
Richard P. Curnow <rc@rc0.org.uk>
Maintainers
===========
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.
The following people have provided patches and other major contributions
to the program :
Lonnie Abelbeck <lonnie@abelbeck.com>
Benny Lyne Amorsen <benny@amorsen.dk>
Patch to add minstratum option
Andrew Bishop <amb@gedanken.demon.co.uk>
Fixes for bugs in logging when in daemon mode
Fixes for compiler warnings
Robustness improvements for drift file
Improve installation (directory checking etc)
Entries in contrib directory
Improvements to 'sources' and 'sourcestats' output from chronyc
Improvements to documentation
Investigation of required dosynctodr behaviour for various Solaris
versions.
Vincent Blut <vincent.debian@free.fr>
Stephan I. Boettcher <stephan@nevis1.columbia.edu>
Entries in contrib directory
Goswin Brederlow <brederlo@informatik.uni-tuebingen.de>
Leigh Brown <leigh@solinno.co.uk>
Erik Bryer <ebryer@spots.ab.ca>
Entries in contrib directory
Jonathan Cameron <jic23@cam.ac.uk>
Bryan Christianson <bryan@whatroute.net>
Juliusz Chroboczek <jch@pps.jussieu.fr>
Fix install rule in Makefile if chronyd file is in use.
Christian Ehrhardt <christian.ehrhardt@canonical.com>
Paul Elliott <pelliott@io.com>
DNSchrony (in contrib directory), a tool for handling NTP servers
with variable IP addresses.
Stefan R. Filipek <srfilipek@gmail.com>
Mike Fleetwood <mike@rockover.demon.co.uk>
Fixes for compiler warnings
Alexander Gretencord <arutha@gmx.de>
Changes to installation directory system to make it easier for
package builders.
Andrew Griffiths <agriffit@redhat.com>
Walter Haidinger <walter.haidinger@gmx.at>
Providing me with login access to a Linux installation where v1.12
wouldn't compile, so I could develop the fixes for v1.13. Also, for
providing the disc space so I can keep an independent backup of the
sources.
Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
Port to NetBSD
John Hasler <john@dhh.gt.org>
Project and website at tuxfamily.org
Changes to support 64 bit machines (i.e. those where
sizeof(unsigned long) > 4)
Bug fix to initstepslew directive
Fix to remove potential buffer overrun errors.
Memory locking and real-time scheduler support
Fix fault where chronyd enters an endless loop
Tjalling Hattink <t.hattink@fugro.nl>
Fix scheduler to allow stepping clock from timeout handler
Patch to take leap second in PPS refclock from locked source
Patch to make reading of RTC for initial trim more reliable
Liam Hatton <me@liamhatton.com>
Advice on configuring for Linux on PPC
Jachym Holecek <jakym@volny.cz>
Patch to make Linux real time clock work with devfs
Håkan Johansson <f96hajo@chalmers.se>
Patch to avoid large values in sources and sourcestats output
Jim Knoble <jmknoble@pobox.com>
Fixes for compiler warnings
Antti Jrvinen <costello@iki.fi>
Advice on configuring for BSD/386
Miroslav Lichvar <mlichvar@redhat.com>
Reference clock support
IPv6 support
Linux capabilities support
Leap second support
Improved source selection
Improved sample history trimming
Improved polling interval adjustment
Improved stability with temporary asymmetric delays
Temperature compensation
Many other bug fixes and improvements
Eric Lammerts <eric@lammerts.org>
Stefan Lucke <stefan@lucke.in-berlin.de>
Victor Lum <viclum@vanu.com>
Kevin Lyda <kevin@ie.suberic.net>
Paul Menzel <paulepanter@users.sourceforge.net>
Vladimir Michl <vladimir.michl@seznam.cz>
Victor Moroz <vim@prv.adlum.ru>
Patch to support Linux with HZ!=100
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
acquisitionport support
Frank Otto <sandwichmacher@web.de>
Handling arbitrary HZ values
Denny Page <dennypage@me.com>
Chris Perl <cperl@janestreet.com>
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Andreas Piesk <apiesk@virbus.de>
Patch to make chronyc use the readline library if available
Andreas Steinmetz <ast@domdv.de>
NAKAMURA Takumi <takumi@ps.sakura.ne.jp>
Timo Teras <timo.teras@iki.fi>
Patch to reply correctly on multihomed hosts
Bill Unruh <unruh@physics.ubc.ca>
Stephen Wadeley <swadeley@redhat.com>
Bernhard Weiss <lisnablagh@web.de>
Wolfgang Weisselberg <weissel@netcologne.de>
Entries in contrib directory
Bernhard M. Wiedemann <bwiedemann@suse.de>
Joachim Wiedorn <ad_debian@joonet.de>
Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
Many robustness and security improvements
Ulrich Windl <ulrich.windl@rz.uni-regensburg.de> for the
Providing me with information about the Linux 2.2 kernel
functionality compared to 2.0.
Ulrich Windl <ulrich.windl@rz.uni-regensburg.de>
Doug Woodward <dougw@whistler.com>
Advice on configuring for Solaris 2.8 on x86
Thomas Zajic <zlatko@zlatko.fdns.net>
Many other people have contributed bug reports and suggestions. I'm
sorry I can't identify all of you individually.
Many other people have contributed bug reports and suggestions. We are sorry
we cannot identify all of you individually.

View File

@@ -30,27 +30,36 @@
#include "sysincl.h"
/* This type is used to represent an IPv4 address or IPv6 address.
Addresses which are not resolved yet can be represented with an ID.
All parts are in HOST order, NOT network order. */
#define IPADDR_UNSPEC 0
#define IPADDR_INET4 1
#define IPADDR_INET6 2
#define IPADDR_ID 3
typedef struct {
union {
uint32_t in4;
uint8_t in6[16];
uint32_t id;
} addr;
uint16_t family;
uint16_t _pad;
} IPAddr;
typedef struct {
IPAddr ip_addr;
unsigned short port;
} NTP_Remote_Address;
uint16_t port;
} IPSockAddr;
typedef IPSockAddr NTP_Remote_Address;
#define INVALID_IF_INDEX -1
typedef struct {
IPAddr ip_addr;
int if_index;
int sock_fd;
} NTP_Local_Address;

View File

@@ -62,7 +62,7 @@ split_ip6(IPAddr *ip, uint32_t *dst)
int i;
for (i = 0; i < 4; i++)
dst[i] = ip->addr.in6[i * 4 + 0] << 24 |
dst[i] = (uint32_t)ip->addr.in6[i * 4 + 0] << 24 |
ip->addr.in6[i * 4 + 1] << 16 |
ip->addr.in6[i * 4 + 2] << 8 |
ip->addr.in6[i * 4 + 3];
@@ -247,6 +247,8 @@ set_subnet_(ADF_AuthTable table,
set_subnet(&table->base6, ip6, 4, 0, new_state, delete_children) == ADF_SUCCESS)
return ADF_SUCCESS;
break;
default:
break;
}
return ADF_BADSUBNET;
@@ -359,9 +361,9 @@ ADF_IsAllowed(ADF_AuthTable table,
case IPADDR_INET6:
split_ip6(ip_addr, ip6);
return check_ip_in_node(&table->base6, ip6);
}
default:
return 0;
}
}
/* ================================================== */
@@ -401,117 +403,3 @@ ADF_IsAnyAllowed(ADF_AuthTable table, int family)
return 0;
}
}
/* ================================================== */
#if defined TEST
static void print_node(TableNode *node, uint32_t *addr, int ip_len, int shift, int subnet_bits)
{
uint32_t new_addr[4];
int i;
TableNode *sub_node;
for (i=0; i<subnet_bits; i++) putchar(' ');
if (ip_len == 1)
printf("%d.%d.%d.%d",
((addr[0] >> 24) & 255),
((addr[0] >> 16) & 255),
((addr[0] >> 8) & 255),
((addr[0] ) & 255));
else {
for (i=0; i<4; i++) {
if (addr[i])
printf("%d.%d.%d.%d",
((addr[i] >> 24) & 255),
((addr[i] >> 16) & 255),
((addr[i] >> 8) & 255),
((addr[i] ) & 255));
putchar(i < 3 ? ':' : '\0');
}
}
printf("/%d : %s\n",
subnet_bits,
(node->state == ALLOW) ? "allow" :
(node->state == DENY) ? "deny" : "as parent");
if (node->extended) {
for (i=0; i<16; i++) {
sub_node = &(node->extended[i]);
new_addr[0] = addr[0];
new_addr[1] = addr[1];
new_addr[2] = addr[2];
new_addr[3] = addr[3];
new_addr[ip_len - 1 - shift / 32] |= ((uint32_t)i << (shift % 32));
print_node(sub_node, new_addr, ip_len, shift - 4, subnet_bits + 4);
}
}
}
static void print_table(ADF_AuthTable table)
{
uint32_t addr[4];
memset(addr, 0, sizeof (addr));
printf("IPv4 table:\n");
print_node(&table->base4, addr, 1, 28, 0);
memset(addr, 0, sizeof (addr));
printf("IPv6 table:\n");
print_node(&table->base6, addr, 4, 124, 0);
}
/* ================================================== */
int main (int argc, char **argv)
{
IPAddr ip;
ADF_AuthTable table;
table = ADF_CreateTable();
ip.family = IPADDR_INET4;
ip.addr.in4 = 0x7e800000;
ADF_Allow(table, &ip, 9);
ip.addr.in4 = 0x7ecc0000;
ADF_Deny(table, &ip, 14);
#if 0
ip.addr.in4 = 0x7f000001;
ADF_Deny(table, &ip, 32);
ip.addr.in4 = 0x7f000000;
ADF_Allow(table, &ip, 8);
#endif
printf("allowed: %d\n", ADF_IsAllowed(table, &ip));
ip.addr.in4 ^= 1;
printf("allowed: %d\n", ADF_IsAllowed(table, &ip));
ip.family = IPADDR_INET6;
memcpy(ip.addr.in6, "abcdefghijklmnop", 16);
ADF_Deny(table, &ip, 66);
ADF_Allow(table, &ip, 59);
memcpy(ip.addr.in6, "xbcdefghijklmnop", 16);
ADF_Deny(table, &ip, 128);
ip.addr.in6[15] ^= 3;
ADF_Allow(table, &ip, 127);
printf("allowed: %d\n", ADF_IsAllowed(table, &ip));
ip.addr.in4 ^= 1;
printf("allowed: %d\n", ADF_IsAllowed(table, &ip));
print_table(table);
ADF_DestroyTable(table);
return 0;
}
#endif /* defined TEST */

13
array.c
View File

@@ -66,8 +66,6 @@ ARR_DestroyInstance(ARR_Instance array)
static void
realloc_array(ARR_Instance array, unsigned int min_size)
{
size_t data_size;
assert(min_size <= 2 * min_size);
if (array->allocated >= min_size && array->allocated <= 2 * min_size)
return;
@@ -79,10 +77,7 @@ realloc_array(ARR_Instance array, unsigned int min_size)
array->allocated = min_size;
}
data_size = (size_t)array->elem_size * array->allocated;
assert(data_size / array->elem_size == array->allocated);
array->data = Realloc(array->data, data_size);
array->data = Realloc2(array->data, array->allocated, array->elem_size);
}
void *
@@ -103,6 +98,12 @@ ARR_GetElement(ARR_Instance array, unsigned int index)
void *
ARR_GetElements(ARR_Instance array)
{
/* Return a non-NULL pointer when the array has zero size */
if (!array->data) {
assert(!array->used);
return array;
}
return array->data;
}

189
candm.h
View File

@@ -31,7 +31,6 @@
#include "sysincl.h"
#include "addressing.h"
#include "hash.h"
/* This is the default port to use for CANDM, if no alternative is
defined */
@@ -91,18 +90,28 @@
#define REQ_MODIFY_MAKESTEP 50
#define REQ_SMOOTHING 51
#define REQ_SMOOTHTIME 52
#define N_REQUEST_TYPES 53
#define REQ_REFRESH 53
#define REQ_SERVER_STATS 54
#define REQ_CLIENT_ACCESSES_BY_INDEX2 55
#define REQ_LOCAL2 56
#define REQ_NTP_DATA 57
#define REQ_ADD_SERVER2 58
#define REQ_ADD_PEER2 59
#define REQ_ADD_SERVER3 60
#define REQ_ADD_PEER3 61
#define REQ_SHUTDOWN 62
#define REQ_ONOFFLINE 63
#define REQ_ADD_SOURCE 64
#define REQ_NTP_SOURCE_NAME 65
#define REQ_RESET 66
#define N_REQUEST_TYPES 67
/* Special utoken value used to log on with first exchange being the
password. (This time value has long since gone by) */
#define SPECIAL_UTOKEN 0x10101010
/* Structure used to exchange timevals independent on size of time_t */
/* Structure used to exchange timespecs independent of time_t size */
typedef struct {
uint32_t tv_sec_high;
uint32_t tv_sec_low;
uint32_t tv_nsec;
} Timeval;
} Timespec;
/* This is used in tv_sec_high for 32-bit timestamps */
#define TV_NOHIGHSEC 0x7fffffff
@@ -201,18 +210,20 @@ typedef struct {
} REQ_Modify_Makestep;
typedef struct {
Timeval ts;
Timespec ts;
int32_t EOR;
} REQ_Logon;
typedef struct {
Timeval ts;
Timespec ts;
int32_t EOR;
} REQ_Settime;
typedef struct {
int32_t on_off;
int32_t stratum;
Float distance;
int32_t orphan;
int32_t EOR;
} REQ_Local;
@@ -237,23 +248,47 @@ typedef struct {
int32_t EOR;
} REQ_Ac_Check;
/* Source types in NTP source requests */
#define REQ_ADDSRC_SERVER 1
#define REQ_ADDSRC_PEER 2
#define REQ_ADDSRC_POOL 3
/* Flags used in NTP source requests */
#define REQ_ADDSRC_ONLINE 0x1
#define REQ_ADDSRC_AUTOOFFLINE 0x2
#define REQ_ADDSRC_IBURST 0x4
#define REQ_ADDSRC_PREFER 0x8
#define REQ_ADDSRC_NOSELECT 0x10
#define REQ_ADDSRC_TRUST 0x20
#define REQ_ADDSRC_REQUIRE 0x40
#define REQ_ADDSRC_INTERLEAVED 0x80
#define REQ_ADDSRC_BURST 0x100
#define REQ_ADDSRC_NTS 0x200
typedef struct {
IPAddr ip_addr;
uint32_t type;
int8_t name[256];
uint32_t port;
int32_t minpoll;
int32_t maxpoll;
int32_t presend_minpoll;
uint32_t min_stratum;
uint32_t poll_target;
uint32_t version;
uint32_t max_sources;
int32_t min_samples;
int32_t max_samples;
uint32_t authkey;
uint32_t nts_port;
Float max_delay;
Float max_delay_ratio;
Float max_delay_dev_ratio;
Float min_delay;
Float asymmetry;
Float offset;
uint32_t flags;
int32_t filter_length;
uint32_t reserved[3];
int32_t EOR;
} REQ_NTP_Source;
@@ -284,7 +319,7 @@ typedef struct {
typedef struct {
uint32_t first_index;
uint32_t n_indices;
uint32_t n_clients;
int32_t EOR;
} REQ_ClientAccessesByIndex;
@@ -306,6 +341,16 @@ typedef struct {
int32_t EOR;
} REQ_SmoothTime;
typedef struct {
IPAddr ip_addr;
int32_t EOR;
} REQ_NTPData;
typedef struct {
IPAddr ip_addr;
int32_t EOR;
} REQ_NTPSourceName;
/* ================================================== */
#define PKT_TYPE_CMD_REQUEST 1
@@ -335,7 +380,16 @@ typedef struct {
Version 6 : added padding to requests to prevent amplification attack,
changed maximum number of samples in manual list to 16, new commands: modify
makestep, smoothing report, smoothtime command
makestep, smoothing, smoothtime
Support for authentication was removed later in version 6 of the protocol
and commands that required authentication are allowed only locally over Unix
domain socket.
Version 6 (no authentication) : changed format of client accesses by index
(using new request/reply types) and manual timestamp, added new fields and
flags to NTP source request and report, made length of manual list constant,
added new commands: ntpdata, refresh, serverstats, shutdown
*/
#define PROTO_VERSION_NUMBER 6
@@ -349,7 +403,7 @@ typedef struct {
#define PROTO_VERSION_PADDING 6
/* The maximum length of padding in request packet, currently
defined by CLIENT_ACCESSES_BY_INDEX and MANUAL_LIST */
defined by MANUAL_LIST */
#define MAX_PADDING_LENGTH 396
/* ================================================== */
@@ -364,8 +418,8 @@ typedef struct {
(count up from zero for same sequence
number) */
uint32_t sequence; /* Client's sequence number */
uint32_t utoken; /* Unique token per incarnation of daemon */
uint32_t token; /* Command token (to prevent replay attack) */
uint32_t pad1;
uint32_t pad2;
union {
REQ_Null null;
@@ -398,17 +452,14 @@ typedef struct {
REQ_ManualDelete manual_delete;
REQ_ReselectDistance reselect_distance;
REQ_SmoothTime smoothtime;
REQ_NTPData ntp_data;
REQ_NTPData ntp_source_name;
} data; /* Command specific parameters */
/* The following fields only set the maximum size of the packet.
There are no holes between them and the actual data. */
/* Padding used to prevent traffic amplification */
/* Padding used to prevent traffic amplification. It only defines the
maximum size of the packet, there is no hole after the data field. */
uint8_t padding[MAX_PADDING_LENGTH];
/* Authentication data */
uint8_t auth[MAX_HASH_LENGTH];
} CMD_Request;
/* ================================================== */
@@ -434,7 +485,13 @@ typedef struct {
#define RPY_MANUAL_LIST 11
#define RPY_ACTIVITY 12
#define RPY_SMOOTHING 13
#define N_REPLY_TYPES 14
#define RPY_SERVER_STATS 14
#define RPY_CLIENT_ACCESSES_BY_INDEX2 15
#define RPY_NTP_DATA 16
#define RPY_MANUAL_TIMESTAMP2 17
#define RPY_MANUAL_LIST2 18
#define RPY_NTP_SOURCE_NAME 19
#define N_REPLY_TYPES 20
/* Status codes */
#define STT_SUCCESS 0
@@ -458,6 +515,7 @@ typedef struct {
#define STT_INVALIDAF 17
#define STT_BADPKTVERSION 18
#define STT_BADPKTLENGTH 19
#define STT_INVALIDNAME 21
typedef struct {
int32_t EOR;
@@ -481,6 +539,8 @@ typedef struct {
#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;
@@ -502,7 +562,7 @@ typedef struct {
IPAddr ip_addr;
uint16_t stratum;
uint16_t leap_status;
Timeval ref_time;
Timespec ref_time;
Float current_correction;
Float last_offset;
Float rms_offset;
@@ -530,7 +590,7 @@ typedef struct {
} RPY_Sourcestats;
typedef struct {
Timeval ref_time;
Timespec ref_time;
uint16_t n_samples;
uint16_t n_runs;
uint32_t span_seconds;
@@ -540,7 +600,7 @@ typedef struct {
} RPY_Rtc;
typedef struct {
uint32_t centiseconds;
Float offset;
Float dfreq_ppm;
Float new_afreq_ppm;
int32_t EOR;
@@ -548,11 +608,14 @@ typedef struct {
typedef struct {
IPAddr ip;
uint32_t client_hits;
uint32_t peer_hits;
uint32_t cmd_hits_auth;
uint32_t cmd_hits_normal;
uint32_t cmd_hits_bad;
uint32_t ntp_hits;
uint32_t cmd_hits;
uint32_t ntp_drops;
uint32_t cmd_drops;
int8_t ntp_interval;
int8_t cmd_interval;
int8_t ntp_timeout_interval;
int8_t pad;
uint32_t last_ntp_hit_ago;
uint32_t last_cmd_hit_ago;
} RPY_ClientAccesses_Client;
@@ -565,10 +628,19 @@ typedef struct {
int32_t EOR;
} RPY_ClientAccessesByIndex;
typedef struct {
uint32_t ntp_hits;
uint32_t cmd_hits;
uint32_t ntp_drops;
uint32_t cmd_drops;
uint32_t log_drops;
int32_t EOR;
} RPY_ServerStats;
#define MAX_MANUAL_LIST_SAMPLES 16
typedef struct {
Timeval when;
Timespec when;
Float slewed_offset;
Float orig_offset;
Float residual;
@@ -602,6 +674,44 @@ typedef struct {
int32_t EOR;
} RPY_Smoothing;
#define RPY_NTP_FLAGS_TESTS 0x3ff
#define RPY_NTP_FLAG_INTERLEAVED 0x4000
#define RPY_NTP_FLAG_AUTHENTICATED 0x8000
typedef struct {
IPAddr remote_addr;
IPAddr local_addr;
uint16_t remote_port;
uint8_t leap;
uint8_t version;
uint8_t mode;
uint8_t stratum;
int8_t poll;
int8_t precision;
Float root_delay;
Float root_dispersion;
uint32_t ref_id;
Timespec ref_time;
Float offset;
Float peer_delay;
Float peer_dispersion;
Float response_time;
Float jitter_asymmetry;
uint16_t flags;
uint8_t tx_tss_char;
uint8_t rx_tss_char;
uint32_t total_tx_count;
uint32_t total_rx_count;
uint32_t total_valid_count;
uint32_t reserved[4];
int32_t EOR;
} RPY_NTPData;
typedef struct {
int8_t name[256];
int32_t EOR;
} RPY_NTPSourceName;
typedef struct {
uint8_t version;
uint8_t pkt_type;
@@ -614,9 +724,9 @@ typedef struct {
uint16_t pad2;
uint16_t pad3;
uint32_t sequence; /* Echo of client's sequence number */
uint32_t utoken; /* Unique token per incarnation of daemon */
uint32_t token; /* New command token (only if command was successfully
authenticated) */
uint32_t pad4;
uint32_t pad5;
union {
RPY_Null null;
RPY_N_Sources n_sources;
@@ -626,15 +736,14 @@ typedef struct {
RPY_Sourcestats sourcestats;
RPY_Rtc rtc;
RPY_ClientAccessesByIndex client_accesses_by_index;
RPY_ServerStats server_stats;
RPY_ManualList manual_list;
RPY_Activity activity;
RPY_Smoothing smoothing;
RPY_NTPData ntp_data;
RPY_NTPSourceName ntp_source_name;
} data; /* Reply specific parameters */
/* authentication of the packet, there is no hole after the actual data
from the data union, this field only sets the maximum auth size */
uint8_t auth[MAX_HASH_LENGTH];
} CMD_Reply;
/* ================================================== */

View File

@@ -1,67 +0,0 @@
.TH chrony.conf 5 "@MAN_DATE@" "chrony @VERSION@" "Configuration Files"
.SH NAME
chrony.conf \- chronyd configuration file
.SH SYNOPSIS
.B @SYSCONFDIR@/chrony.conf
.SH DESCRIPTION
\fIchrony\fR is a pair of programs for maintaining the accuracy of computer
clocks. \fIchronyd\fR is a background daemon program that can be started at
boot time.
Assuming that you have found some servers, you need to set up a
configuration file to run \fIchrony\fR. The (compiled-in) default location
for this file is \fB@SYSCONFDIR@/chrony.conf\fR. Assuming that your NTP
servers are called `foo.example.net', `bar.example.net' and `baz.example.net',
your \fBchrony.conf\fR file could contain as a minimum
.EX
server foo.example.net
server bar.example.net
server baz.example.net
.EE
However, you will probably want to include some of the other directives
described in detail in the documentation supplied with the distribution
(\fIchrony.txt\fR and \fIchrony.texi\fR). The following directives may be
particularly useful : `driftfile', `makestep', `rtcsync'. Also, the `iburst'
server option is useful to speed up the initial synchronization. The smallest
useful configuration file would look something like
.EX
server foo.example.net iburst
server bar.example.net iburst
server baz.example.net iburst
driftfile @CHRONYVARDIR@/drift
makestep 10 3
rtcsync
.EE
When using a pool of NTP servers (one name is used for multiple servers which
may change over time), it's better to specify them with the `pool' directive
instead of multiple `server' directives. The configuration file could in this
case look like
.EX
pool pool.ntp.org iburst
driftfile @CHRONYVARDIR@/drift
makestep 10 3
rtcsync
.EE
.SH "SEE ALSO"
.BR chronyc(1),
.BR chronyd(8)
.I http://chrony.tuxfamily.org/
.SH AUTHOR
Richard Curnow <rc@rc0.org.uk>
This man-page was written by Jan Schaumann <jschauma@netmeister.org> as part of "The Missing
Man Pages Project". Please see \fIhttp://www.netmeister.org/misc/m2p2/index.html\fR
for details.
The complete chrony documentation is supplied in texinfo format.

File diff suppressed because it is too large Load Diff

View File

@@ -1,74 +0,0 @@
.TH CHRONYC 1 "@MAN_DATE@" "chrony @VERSION@" "User's Manual"
.SH NAME
chronyc \- command-line interface for chronyd
.SH SYNOPSIS
.B chronyc
[\fIOPTIONS\fR]
.SH DESCRIPTION
\fIchrony\fR is a pair of programs for maintaining the accuracy of computer
clocks.
\fBchronyc\fR is a command-line interface program which can be used to
monitor \fIchronyd\fR's performance and to change various operating
parameters whilst it is running.
.SH USAGE
A detailed description of all commands supported by \fBchronyc\fR is available
via the documentation supplied with the distribution (\fIchrony.txt\fR and
\fIchrony.texi\fR).
.SH OPTIONS
A summary of the options supported by \fBchronyc\fR is included below.
.TP
\fB\-h\fR \fIhostname\fR
specify hostname (default 127.0.0.1)
.TP
\fB\-p\fR \fIport-number\fR
specify port-number
.TP
\fB\-n\fR
display raw IP addresses (don't attempt to look up hostnames)
.TP
\fB\-4\fR
resolve hostnames only to IPv4 addresses
.TP
\fB\-6\fR
resolve hostnames only to IPv6 addresses
.TP
\fB\-m\fR
allow multiple commands to be specified on the command line. Each argument
will be interpreted as a whole command.
.TP
\fB\-f\fR \fIconf-file\fR
This option can be used to specify an alternate location for the
configuration file (default \fI@SYSCONFDIR@/chrony.conf\fR). The configuration file is
needed for the \fB-a\fR option.
.TP
\fB\-a\fR
With this option chronyc will try to authenticate automatically on
start. It will read the configuration file, read the command key from the
keyfile and run the authhash and password commands.
.TP
\fIcommand\fR
specify command. If no command is given, chronyc will read commands
interactively.
.SH BUGS
To report bugs, please visit \fIhttp://chrony.tuxfamily.org\fR
.SH "SEE ALSO"
.BR chronyd(8)
.I http://chrony.tuxfamily.org/
.SH AUTHOR
Richard Curnow <rc@rc0.org.uk>
This man-page was written by Jan Schaumann <jschauma@netmeister.org> as part of "The Missing
Man Pages Project". Please see \fIhttp://www.netmeister.org/misc/m2p2/index.html\fR
for details.
The complete chrony documentation is supplied in texinfo format.

View File

@@ -1,152 +0,0 @@
.TH CHRONYD 8 "@MAN_DATE@" "chrony @VERSION@" "System Administration"
.SH NAME
chronyd \- chrony background daemon
.SH SYNOPSIS
.B chronyd
[\fIOPTIONS\fR] [\fIconfiguration commands\fR]
.SH DESCRIPTION
\fIchrony\fR is a pair of programs for maintaining the accuracy of computer
clocks. \fBchronyd\fR is a background daemon program that can be started at boot
time.
\fBchronyd\fR is a daemon which runs in background on the
system. It obtains measurements (e.g. via the network) of the
system's offset relative to other systems, and adjusts the system
time accordingly. For isolated systems, the user can periodically
enter the correct time by hand (using \fIchronyc\fR). In either case,
\fBchronyd\fR determines the rate at which the computer
gains or loses time, and compensates for this.
.SH USAGE
\fBchronyd\fR is usually started at boot-time and requires superuser
privileges.
If \fBchronyd\fR has been installed to its default location
\fI@SBINDIR@/chronyd\fR, starting it is simply a matter of entering the
command:
\fI@SBINDIR@/chronyd\fR
Information messages and warnings will be logged to syslog.
If no configuration commands are specified on the command line,
\fBchronyd\fR will read the commands from the configuration file
(default \fI@SYSCONFDIR@/chrony.conf\fR).
.SH OPTIONS
A summary of the options supported by \fBchronyd\fR is included below.
.TP
\fB\-P\fR \fIpriority\fR
This option will select the SCHED_FIFO real-time scheduler at the specified
priority (which must be between 0 and 100). This mode is supported only on
Linux.
.TP
.B \-m
This option will lock chronyd into RAM so that it will never be paged out.
This mode is only supported on Linux.
.TP
.B \-n
When run in this mode, the program will not detach itself from the
terminal.
.TP
.B \-d
When run in this mode, the program will not detach itself from the
terminal, and all messages will be sent to the terminal instead of
to syslog. When \fBchronyd\fR was compiled with debugging support,
this option can be used twice to print also debugging messages.
.TP
\fB\-f\fR \fIconf-file\fR
This option can be used to specify an alternate location for the
configuration file (default \fI@SYSCONFDIR@/chrony.conf\fR).
.TP
.B \-r
This option will reload sample histories for each of the servers being used.
These histories are created by using the \fIdump\fR command in \fIchronyc\fR,
or by setting the \fIdumponexit\fR directive in the configuration file. This
option is useful if you want to stop and restart \fBchronyd\fR briefly for any
reason, e.g. to install a new version. However, it only makes sense on
systems where the kernel can maintain clock compensation whilst not under
\fBchronyd\fR's control. The only version where this happens so far is Linux.
On other systems this option should not be used.
.TP
.B \-R
When this option is used, the \fIinitstepslew\fR directive and the
\fImakestep\fR directive used with a positive limit will be ignored. This
option is useful when restarting \fBchronyd\fR and can be used in conjunction
with the \fB-r\fR option.
.TP
.B \-s
This option will set the system clock from the computer's real-time
clock. This is analogous to supplying the \fI-s\fR flag to the
\fI/sbin/hwclock\fR program during the Linux boot sequence.
Support for real-time clocks is limited at present - the criteria
are described in the section on the \fIrtcfile\fR directive in the
documentation supplied with the distribution.
If used in conjunction with the \fB-r\fR flag, \fBchronyd\fR will attempt
to preserve the old samples after setting the system clock from
the real time clock (RTC). This can be used to allow \fBchronyd\fR to
perform long term averaging of the gain or loss rate across system
reboots, and is useful for dial-up systems that are shut down when
not in use. For this to work well, it relies on \fBchronyd\fR having
been able to determine accurate statistics for the difference
between the RTC and system clock last time the computer was on.
If \fBchronyd\fR doesn't support the RTC on your computer or there is no RTC
installed, the system clock will be set with this option forward to the time of
the last modification of the drift file (specified by the \fIdriftfile\fR
directive) to restore the system time at which \fBchronyd\fR was previously
stopped.
.TP
\fB\-u\fR \fIuser\fR
This option sets the name of the user to which will \fBchronyd\fR switch to
drop root privileges if compiled with Linux capabilities support (default
\fB@DEFAULT_USER@\fR).
.TP
.B \-q
When run in this mode, chronyd will set the system clock once
and exit. It will not detach from the terminal.
.TP
.B \-Q
This option is similar to \fB\-q\fR, but it will only print the offset and
not correct the clock.
.TP
.B \-v
This option displays \fBchronyd\fR's version number to the terminal and exits
.TP
.B \-4
Resolve hostnames only to IPv4 addresses and create only IPv4 sockets.
.TP
.B \-6
Resolve hostnames only to IPv6 addresses and create only IPv6 sockets.
.SH FILES
\fI@SYSCONFDIR@/chrony.conf\fR
.SH BUGS
To report bugs, please visit \fIhttp://chrony.tuxfamily.org/\fR
.SH "SEE ALSO"
\fBchronyd\fR is documented in detail in the documentation supplied with the
distribution (\fIchrony.txt\fR and \fIchrony.texi\fR).
.BR chronyc(1),
.BR chrony.conf(5),
.BR hwclock(8),
.BR ntpd(8)
.I http://chrony.tuxfamily.org/
.SH AUTHOR
Richard Curnow <rc@rc0.org.uk>
This man-page was written by Jan Schaumann <jschauma@netmeister.org> as part
of "The Missing Man Pages Project". Please see
\fIhttp://www.netmeister.org/misc/m2p2/index.html\fR for details.
The complete chrony documentation is supplied in texinfo format.

2591
client.c

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009
* Copyright (C) Miroslav Lichvar 2009, 2015-2017
*
* 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
@@ -34,117 +34,271 @@
#include "config.h"
#include "sysincl.h"
#include "array.h"
#include "clientlog.h"
#include "conf.h"
#include "memory.h"
#include "ntp.h"
#include "reports.h"
#include "util.h"
#include "logging.h"
/* Number of bits of address per layer of the table. This value has
been chosen on the basis that a server will predominantly be serving
a lot of hosts in a few subnets, rather than a few hosts scattered
across many subnets. */
#define NBITS 8
/* Number of entries in each subtable */
#define TABLE_SIZE (1UL<<NBITS)
typedef struct _Node {
typedef struct {
IPAddr ip_addr;
unsigned long client_hits;
unsigned long peer_hits;
unsigned long cmd_hits_bad;
unsigned long cmd_hits_normal;
unsigned long cmd_hits_auth;
time_t last_ntp_hit;
time_t last_cmd_hit;
} Node;
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;
int8_t ntp_timeout_rate;
uint8_t flags;
NTP_int64 ntp_rx_ts;
NTP_int64 ntp_tx_ts;
} Record;
typedef struct _Subnet {
void *entry[TABLE_SIZE];
} Subnet;
/* Hash table of records, there is a fixed number of records per slot */
static ARR_Instance records;
/* ================================================== */
#define SLOT_BITS 4
/* Table for the IPv4 class A subnet */
static Subnet top_subnet4;
/* Table for IPv6 */
static Subnet top_subnet6;
/* Number of records in one slot of the hash table */
#define SLOT_SIZE (1U << SLOT_BITS)
/* Table containing pointers directly to all nodes that have been
allocated. */
static Node **nodes = NULL;
/* Minimum number of slots */
#define MIN_SLOTS 1
/* Number of nodes actually in the table. */
static int n_nodes = 0;
/* Maximum number of slots, this is a hard limit */
#define MAX_SLOTS (1U << (24 - SLOT_BITS))
/* Number of entries for which the table has been sized. */
static int max_nodes = 0;
/* Number of slots in the hash table */
static unsigned int slots;
/* Maximum number of slots given memory allocation limit */
static unsigned int max_slots;
/* Times of last hits are saved as 32-bit fixed point values */
#define TS_FRAC 4
#define INVALID_TS 0
/* Static offset included in conversion to the fixed-point timestamps to
randomise their alignment */
static uint32_t ts_offset;
/* Request rates are saved in the record as 8-bit scaled log2 values */
#define RATE_SCALE 4
#define MIN_RATE (-14 * RATE_SCALE)
#define INVALID_RATE -128
/* Response rates are controlled by token buckets. The capacity and
number of tokens spent on response are determined from configured
minimum inverval between responses (in log2) and burst length. */
#define MIN_LIMIT_INTERVAL (-15 - TS_FRAC)
#define MAX_LIMIT_INTERVAL 12
#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;
/* 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;
/* Rates at which responses are randomly allowed (in log2) when the
buckets don't have enough tokens. This is necessary in order to
prevent an attacker sending requests with spoofed source address
from blocking responses to the address completely. */
#define MIN_LEAK_RATE 1
#define MAX_LEAK_RATE 4
static int ntp_leak_rate;
static int cmd_leak_rate;
/* Flag indicating whether the last response was dropped */
#define FLAG_NTP_DROPPED 0x1
/* NTP limit interval in log2 */
static int ntp_limit_interval;
/* Flag indicating whether facility is turned on or not */
static int active = 0;
static int active;
/* Flag indicating whether memory allocation limit has been reached
and no new nodes or subnets should be allocated */
static int alloc_limit_reached;
/* 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_record_drops;
static unsigned long alloc_limit;
static unsigned long alloced;
#define NSEC_PER_SEC 1000000000U
/* ================================================== */
static void
split_ip6(IPAddr *ip, uint32_t *dst)
{
int i;
static int expand_hashtable(void);
for (i = 0; i < 4; i++)
dst[i] = ip->addr.in6[i * 4 + 0] << 24 |
ip->addr.in6[i * 4 + 1] << 16 |
ip->addr.in6[i * 4 + 2] << 8 |
ip->addr.in6[i * 4 + 3];
/* ================================================== */
static int
compare_ts(uint32_t x, uint32_t y)
{
if (x == y)
return 0;
if (y == INVALID_TS)
return 1;
return (int32_t)(x - y) > 0 ? 1 : -1;
}
/* ================================================== */
inline static uint32_t
get_subnet(uint32_t *addr, unsigned int where)
static Record *
get_record(IPAddr *ip)
{
int off;
unsigned int first, i;
time_t last_hit, oldest_hit = 0;
Record *record, *oldest_record;
off = where / 32;
where %= 32;
if (!active || (ip->family != IPADDR_INET4 && ip->family != IPADDR_INET6))
return NULL;
return (addr[off] >> (32 - NBITS - where)) & ((1UL << NBITS) - 1);
}
while (1) {
/* Get index of the first record in the slot */
first = UTI_IPToHash(ip) % slots * SLOT_SIZE;
/* ================================================== */
for (i = 0, oldest_record = NULL; i < SLOT_SIZE; i++) {
record = ARR_GetElement(records, first + i);
if (!UTI_CompareIPs(ip, &record->ip_addr, NULL))
return record;
static void
clear_subnet(Subnet *subnet)
{
int i;
if (record->ip_addr.family == IPADDR_UNSPEC)
break;
for (i=0; i<TABLE_SIZE; i++) {
subnet->entry[i] = NULL;
last_hit = compare_ts(record->last_ntp_hit, record->last_cmd_hit) > 0 ?
record->last_ntp_hit : record->last_cmd_hit;
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_record = record;
oldest_hit = last_hit;
}
}
/* If the slot still has an empty record, use it */
if (record->ip_addr.family == IPADDR_UNSPEC)
break;
/* Resize the table if possible and try again as the new slot may
have some empty records */
if (expand_hashtable())
continue;
/* There is no other option, replace the oldest record */
record = oldest_record;
total_record_drops++;
break;
}
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;
record->ntp_timeout_rate = INVALID_RATE;
record->flags = 0;
UTI_ZeroNtp64(&record->ntp_rx_ts);
UTI_ZeroNtp64(&record->ntp_tx_ts);
return record;
}
/* ================================================== */
static int
expand_hashtable(void)
{
ARR_Instance old_records;
Record *old_record, *new_record;
unsigned int i;
old_records = records;
if (2 * slots > max_slots)
return 0;
records = ARR_CreateInstance(sizeof (Record));
slots = MAX(MIN_SLOTS, 2 * slots);
assert(slots <= max_slots);
ARR_SetSize(records, slots * SLOT_SIZE);
/* Mark all new records as empty */
for (i = 0; i < slots * SLOT_SIZE; i++) {
new_record = ARR_GetElement(records, i);
new_record->ip_addr.family = IPADDR_UNSPEC;
}
if (!old_records)
return 1;
/* Copy old records to the new hash table */
for (i = 0; i < ARR_GetSize(old_records); i++) {
old_record = ARR_GetElement(old_records, i);
if (old_record->ip_addr.family == IPADDR_UNSPEC)
continue;
new_record = get_record(&old_record->ip_addr);
assert(new_record);
*new_record = *old_record;
}
ARR_DestroyInstance(old_records);
return 1;
}
/* ================================================== */
static void
clear_node(Node *node)
set_bucket_params(int interval, int burst, uint16_t *max_tokens,
uint16_t *tokens_per_packet, int *token_shift)
{
node->client_hits = 0;
node->peer_hits = 0;
node->cmd_hits_auth = 0;
node->cmd_hits_normal = 0;
node->cmd_hits_bad = 0;
node->last_ntp_hit = (time_t) 0;
node->last_cmd_hit = (time_t) 0;
interval = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
burst = CLAMP(MIN_LIMIT_BURST, burst, MAX_LIMIT_BURST);
if (interval >= -TS_FRAC) {
/* Find the smallest shift with which the maximum number fits in 16 bits */
for (*token_shift = 0; *token_shift < interval + TS_FRAC; (*token_shift)++) {
if (burst << (TS_FRAC + interval - *token_shift) < 1U << 16)
break;
}
} else {
/* Coarse rate limiting */
*token_shift = interval + TS_FRAC;
*tokens_per_packet = 1;
burst = MAX(1U << -*token_shift, burst);
}
*tokens_per_packet = 1U << (TS_FRAC + interval - *token_shift);
*max_tokens = *tokens_per_packet * burst;
DEBUG_LOG("Tokens max %d packet %d shift %d",
*max_tokens, *tokens_per_packet, *token_shift);
}
/* ================================================== */
@@ -152,21 +306,47 @@ clear_node(Node *node)
void
CLG_Initialise(void)
{
clear_subnet(&top_subnet4);
clear_subnet(&top_subnet6);
if (CNF_GetNoClientLog()) {
active = 0;
} else {
active = 1;
int interval, burst, leak_rate;
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;
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);
}
nodes = NULL;
max_nodes = 0;
n_nodes = 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);
}
alloced = 0;
alloc_limit = CNF_GetClientLogLimit();
alloc_limit_reached = 0;
active = !CNF_GetNoClientLog();
if (!active) {
if (ntp_leak_rate || cmd_leak_rate)
LOG_FATAL("ratelimit cannot be used with noclientlog");
return;
}
/* Calculate the maximum number of slots that can be allocated in the
configured memory limit. Take into account expanding of the hash
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);
slots = 0;
records = NULL;
expand_hashtable();
UTI_GetRandomBytes(&ts_offset, sizeof (ts_offset));
ts_offset %= NSEC_PER_SEC / (1U << TS_FRAC);
}
/* ================================================== */
@@ -174,226 +354,342 @@ CLG_Initialise(void)
void
CLG_Finalise(void)
{
int i;
if (!active)
return;
for (i = 0; i < n_nodes; i++)
Free(nodes[i]);
Free(nodes);
ARR_DestroyInstance(records);
}
/* ================================================== */
static void check_alloc_limit() {
if (alloc_limit_reached)
return;
static uint32_t
get_ts_from_timespec(struct timespec *ts)
{
uint32_t sec = ts->tv_sec, nsec = ts->tv_nsec;
if (alloced >= alloc_limit) {
LOG(LOGS_WARN, LOGF_ClientLog, "Client log memory limit reached");
alloc_limit_reached = 1;
nsec += ts_offset;
if (nsec >= NSEC_PER_SEC) {
nsec -= NSEC_PER_SEC;
sec++;
}
/* This is fast and accurate enough */
return sec << TS_FRAC | (140740U * (nsec >> 15)) >> (32 - TS_FRAC);
}
/* ================================================== */
static void
create_subnet(Subnet *parent_subnet, int the_entry)
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)
{
parent_subnet->entry[the_entry] = (void *) MallocNew(Subnet);
clear_subnet((Subnet *) parent_subnet->entry[the_entry]);
alloced += sizeof (Subnet);
check_alloc_limit();
uint32_t interval, now_ts, prev_hit, new_tokens;
int interval2;
now_ts = get_ts_from_timespec(now);
prev_hit = *last_hit;
*last_hit = now_ts;
(*hits)++;
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;
else
new_tokens = (now_ts - prev_hit) << -token_shift;
*tokens = MIN(*tokens + new_tokens, max_tokens);
/* Convert the interval to scaled and rounded log2 */
if (interval) {
interval += interval >> 1;
for (interval2 = -RATE_SCALE * TS_FRAC; interval2 < -MIN_RATE;
interval2 += RATE_SCALE) {
if (interval <= 1)
break;
interval >>= 1;
}
} else {
interval2 = -RATE_SCALE * (TS_FRAC + 1);
}
/* Update the rate in a rough approximation of exponential moving average */
if (*rate == INVALID_RATE) {
*rate = -interval2;
} else {
if (*rate < -interval2) {
(*rate)++;
} else if (*rate > -interval2) {
if (*rate > RATE_SCALE * 5 / 2 - interval2)
*rate = RATE_SCALE * 5 / 2 - interval2;
else
*rate = (*rate - interval2 - 1) / 2;
}
}
}
/* ================================================== */
static void
create_node(Subnet *parent_subnet, int the_entry)
static int
get_index(Record *record)
{
Node *new_node;
new_node = MallocNew(Node);
parent_subnet->entry[the_entry] = (void *) new_node;
clear_node(new_node);
alloced += sizeof (Node);
if (n_nodes == max_nodes) {
if (nodes) {
assert(max_nodes > 0);
max_nodes *= 2;
nodes = ReallocArray(Node *, max_nodes, nodes);
} else {
assert(max_nodes == 0);
max_nodes = 16;
nodes = MallocArray(Node *, max_nodes);
}
alloced += sizeof (Node *) * (max_nodes - n_nodes);
}
nodes[n_nodes++] = (Node *) new_node;
check_alloc_limit();
return record - (Record *)ARR_GetElements(records);
}
/* ================================================== */
/* Recursively seek out the Node entry for a particular address,
expanding subnet tables and node entries as we go if necessary. */
static void *
find_subnet(Subnet *subnet, uint32_t *addr, int addr_len, int bits_consumed)
int
CLG_GetClientIndex(IPAddr *client)
{
uint32_t this_subnet;
Record *record;
this_subnet = get_subnet(addr, bits_consumed);
bits_consumed += NBITS;
record = get_record(client);
if (record == NULL)
return -1;
if (bits_consumed < 32 * addr_len) {
if (!subnet->entry[this_subnet]) {
if (alloc_limit_reached)
return NULL;
create_subnet(subnet, this_subnet);
return get_index(record);
}
/* ================================================== */
int
CLG_LogNTPAccess(IPAddr *client, struct timespec *now)
{
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);
}
/* ================================================== */
int
CLG_LogCommandAccess(IPAddr *client, struct timespec *now)
{
Record *record;
total_cmd_hits++;
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);
DEBUG_LOG("Cmd hits %"PRIu32" rate %d tokens %d",
record->cmd_hits, record->cmd_rate, record->cmd_tokens);
return get_index(record);
}
/* ================================================== */
static int
limit_response_random(int leak_rate)
{
static uint32_t rnd;
static int bits_left = 0;
int r;
if (bits_left < leak_rate) {
UTI_GetRandomBytes(&rnd, sizeof (rnd));
bits_left = 8 * sizeof (rnd);
}
return find_subnet((Subnet *) subnet->entry[this_subnet], addr, addr_len, bits_consumed);
} else {
if (!subnet->entry[this_subnet]) {
if (alloc_limit_reached)
return NULL;
create_node(subnet, this_subnet);
/* Return zero on average once per 2^leak_rate */
r = rnd % (1U << leak_rate) ? 1 : 0;
rnd >>= leak_rate;
bits_left -= leak_rate;
return r;
}
/* ================================================== */
int
CLG_LimitNTPResponseRate(int index)
{
Record *record;
int drop;
if (!ntp_tokens_per_packet)
return 0;
record = ARR_GetElement(records, index);
record->flags &= ~FLAG_NTP_DROPPED;
if (record->ntp_tokens >= ntp_tokens_per_packet) {
record->ntp_tokens -= ntp_tokens_per_packet;
return 0;
}
return subnet->entry[this_subnet];
drop = limit_response_random(ntp_leak_rate);
/* Poorly implemented clients may send new requests at even 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)
drop = !drop;
if (!drop) {
record->ntp_tokens = 0;
return 0;
}
record->flags |= FLAG_NTP_DROPPED;
record->ntp_drops++;
total_ntp_drops++;
return 1;
}
/* ================================================== */
int
CLG_LimitCommandResponseRate(int index)
{
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;
}
/* ================================================== */
void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts)
{
Record *record;
record = ARR_GetElement(records, index);
*rx_ts = &record->ntp_rx_ts;
*tx_ts = &record->ntp_tx_ts;
}
/* ================================================== */
int
CLG_GetNtpMinPoll(void)
{
return ntp_limit_interval;
}
/* ================================================== */
int
CLG_GetNumberOfIndices(void)
{
if (!active)
return -1;
return ARR_GetSize(records);
}
/* ================================================== */
static int get_interval(int rate)
{
if (rate == INVALID_RATE)
return 127;
rate += rate > 0 ? RATE_SCALE / 2 : -RATE_SCALE / 2;
return rate / -RATE_SCALE;
}
/* ================================================== */
static uint32_t get_last_ago(uint32_t x, uint32_t y)
{
if (y == INVALID_TS || (int32_t)(x - y) < 0)
return -1;
return (x - y) >> TS_FRAC;
}
/* ================================================== */
int
CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timespec *now)
{
Record *record;
uint32_t now_ts;
if (!active || index < 0 || index >= ARR_GetSize(records))
return 0;
record = ARR_GetElement(records, index);
if (record->ip_addr.family == IPADDR_UNSPEC)
return 0;
now_ts = get_ts_from_timespec(now);
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);
return 1;
}
/* ================================================== */
void
CLG_LogNTPClientAccess (IPAddr *client, time_t now)
CLG_GetServerStatsReport(RPT_ServerStatsReport *report)
{
uint32_t ip6[4];
Node *node;
if (active) {
switch (client->family) {
case IPADDR_INET4:
node = (Node *) find_subnet(&top_subnet4, &client->addr.in4, 1, 0);
break;
case IPADDR_INET6:
split_ip6(client, ip6);
node = (Node *) find_subnet(&top_subnet6, ip6, 4, 0);
break;
default:
assert(0);
}
if (node == NULL)
return;
node->ip_addr = *client;
++node->client_hits;
node->last_ntp_hit = now;
}
}
/* ================================================== */
void
CLG_LogNTPPeerAccess(IPAddr *client, time_t now)
{
uint32_t ip6[4];
Node *node;
if (active) {
switch (client->family) {
case IPADDR_INET4:
node = (Node *) find_subnet(&top_subnet4, &client->addr.in4, 1, 0);
break;
case IPADDR_INET6:
split_ip6(client, ip6);
node = (Node *) find_subnet(&top_subnet6, ip6, 4, 0);
break;
default:
assert(0);
}
if (node == NULL)
return;
node->ip_addr = *client;
++node->peer_hits;
node->last_ntp_hit = now;
}
}
/* ================================================== */
void
CLG_LogCommandAccess(IPAddr *client, CLG_Command_Type type, time_t now)
{
uint32_t ip6[4];
Node *node;
if (active) {
switch (client->family) {
case IPADDR_INET4:
node = (Node *) find_subnet(&top_subnet4, &client->addr.in4, 1, 0);
break;
case IPADDR_INET6:
split_ip6(client, ip6);
node = (Node *) find_subnet(&top_subnet6, ip6, 4, 0);
break;
default:
assert(0);
}
if (node == NULL)
return;
node->ip_addr = *client;
node->last_cmd_hit = now;
switch (type) {
case CLG_CMD_AUTH:
++node->cmd_hits_auth;
break;
case CLG_CMD_NORMAL:
++node->cmd_hits_normal;
break;
case CLG_CMD_BAD_PKT:
++node->cmd_hits_bad;
break;
default:
assert(0);
break;
}
}
}
/* ================================================== */
CLG_Status
CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report,
time_t now, unsigned long *n_indices)
{
Node *node;
*n_indices = n_nodes;
if (!active) {
return CLG_INACTIVE;
} else {
if ((index < 0) || (index >= n_nodes)) {
return CLG_INDEXTOOLARGE;
}
node = nodes[index];
report->ip_addr = node->ip_addr;
report->client_hits = node->client_hits;
report->peer_hits = node->peer_hits;
report->cmd_hits_auth = node->cmd_hits_auth;
report->cmd_hits_normal = node->cmd_hits_normal;
report->cmd_hits_bad = node->cmd_hits_bad;
report->last_ntp_hit_ago = now - node->last_ntp_hit;
report->last_cmd_hit_ago = now - node->last_cmd_hit;
return CLG_SUCCESS;
}
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->log_drops = total_record_drops;
}

View File

@@ -33,32 +33,18 @@
extern void CLG_Initialise(void);
extern void CLG_Finalise(void);
extern void CLG_LogNTPClientAccess(IPAddr *client, time_t now);
extern void CLG_LogNTPPeerAccess(IPAddr *client, time_t now);
/* When logging command packets, there are several subtypes */
typedef enum {
CLG_CMD_AUTH, /* authenticated */
CLG_CMD_NORMAL, /* normal */
CLG_CMD_BAD_PKT /* bad version or packet length */
} CLG_Command_Type;
extern void CLG_LogCommandAccess(IPAddr *client, CLG_Command_Type type, time_t now);
extern int CLG_GetClientIndex(IPAddr *client);
extern int CLG_LogNTPAccess(IPAddr *client, struct timespec *now);
extern int CLG_LogCommandAccess(IPAddr *client, struct timespec *now);
extern int CLG_LimitNTPResponseRate(int index);
extern int CLG_LimitCommandResponseRate(int index);
extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts);
extern int CLG_GetNtpMinPoll(void);
/* And some reporting functions, for use by chronyc. */
/* TBD */
typedef enum {
CLG_SUCCESS, /* All is well */
CLG_EMPTYSUBNET, /* No hosts logged in requested subnet */
CLG_BADSUBNET, /* Subnet requested is not 0, 8, 16 or 24 bits */
CLG_INACTIVE, /* Facility not active */
CLG_INDEXTOOLARGE /* Node index is higher than number of nodes present */
} CLG_Status;
CLG_Status
CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report,
time_t now, unsigned long *n_indices);
extern int CLG_GetNumberOfIndices(void);
extern int CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timespec *now);
extern void CLG_GetServerStatsReport(RPT_ServerStatsReport *report);
#endif /* GOT_CLIENTLOG_H */

41
cmac.h Normal file
View File

@@ -0,0 +1,41 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2019
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header file for CMAC.
*/
#ifndef GOT_CMAC_H
#define GOT_CMAC_H
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 void CMC_DestroyInstance(CMC_Instance inst);
#endif

115
cmac_nettle.c Normal file
View File

@@ -0,0 +1,115 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2019
*
* 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.
*
**********************************************************************
=======================================================================
Support for AES128 and AES256 CMAC in Nettle.
*/
#include "config.h"
#include "sysincl.h"
#include <nettle/cmac.h>
#include "cmac.h"
#include "memory.h"
struct CMC_Instance_Record {
int key_length;
union {
struct cmac_aes128_ctx aes128;
struct cmac_aes256_ctx aes256;
} context;
};
/* ================================================== */
unsigned int
CMC_GetKeyLength(const char *cipher)
{
if (strcmp(cipher, "AES128") == 0)
return AES128_KEY_SIZE;
else if (strcmp(cipher, "AES256") == 0)
return AES256_KEY_SIZE;
return 0;
}
/* ================================================== */
CMC_Instance
CMC_CreateInstance(const char *cipher, const unsigned char *key, unsigned int length)
{
CMC_Instance inst;
if (length == 0 || length != CMC_GetKeyLength(cipher))
return NULL;
inst = MallocNew(struct CMC_Instance_Record);
inst->key_length = length;
switch (length) {
case AES128_KEY_SIZE:
cmac_aes128_set_key(&inst->context.aes128, key);
break;
case AES256_KEY_SIZE:
cmac_aes256_set_key(&inst->context.aes256, key);
break;
default:
assert(0);
}
return inst;
}
/* ================================================== */
unsigned int
CMC_Hash(CMC_Instance inst, const unsigned char *in, unsigned int in_len,
unsigned char *out, unsigned int out_len)
{
if (out_len > CMAC128_DIGEST_SIZE)
out_len = CMAC128_DIGEST_SIZE;
switch (inst->key_length) {
case AES128_KEY_SIZE:
cmac_aes128_update(&inst->context.aes128, in_len, in);
cmac_aes128_digest(&inst->context.aes128, out_len, out);
break;
case AES256_KEY_SIZE:
cmac_aes256_update(&inst->context.aes256, in_len, in);
cmac_aes256_digest(&inst->context.aes256, out_len, out);
break;
default:
assert(0);
}
return out_len;
}
/* ================================================== */
void
CMC_DestroyInstance(CMC_Instance inst)
{
Free(inst);
}

1377
cmdmon.c

File diff suppressed because it is too large Load Diff

View File

@@ -33,6 +33,7 @@ extern void CAM_Initialise(int family);
extern void CAM_Finalise(void);
extern void CAM_OpenUnixSocket(void);
extern int CAM_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all);
extern int CAM_CheckAccessRestriction(IPAddr *ip_addr);

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2013-2014
* Copyright (C) Miroslav Lichvar 2013-2014, 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -39,246 +39,173 @@
/* ================================================== */
CPS_Status
int
CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
{
char *hostname, *cmd;
int n, done;
CPS_Status result;
int n;
src->port = SRC_DEFAULT_PORT;
src->params.minpoll = SRC_DEFAULT_MINPOLL;
src->params.maxpoll = SRC_DEFAULT_MAXPOLL;
src->params.connectivity = SRC_ONLINE;
src->params.auto_offline = 0;
src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL;
src->params.burst = 0;
src->params.iburst = 0;
src->params.min_stratum = SRC_DEFAULT_MINSTRATUM;
src->params.poll_target = SRC_DEFAULT_POLLTARGET;
src->params.version = 0;
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
src->params.filter_length = 0;
src->params.interleaved = 0;
src->params.sel_options = 0;
src->params.nts = 0;
src->params.nts_port = SRC_DEFAULT_NTSPORT;
src->params.authkey = INACTIVE_AUTHKEY;
src->params.max_delay = SRC_DEFAULT_MAXDELAY;
src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
src->params.online = 1;
src->params.auto_offline = 0;
src->params.iburst = 0;
src->params.min_stratum = SRC_DEFAULT_MINSTRATUM;
src->params.poll_target = SRC_DEFAULT_POLLTARGET;
src->params.version = NTP_VERSION;
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
src->params.sel_option = SRC_SelectNormal;
result = CPS_Success;
src->params.min_delay = 0.0;
src->params.asymmetry = SRC_DEFAULT_ASYMMETRY;
src->params.offset = 0.0;
hostname = line;
line = CPS_SplitWord(line);
if (!*hostname) {
result = CPS_BadHost;
} else {
if (!*hostname)
return 0;
src->name = hostname;
/* Parse subfields */
done = 0;
do {
/* Parse options */
for (; *line; line += n) {
cmd = line;
line = CPS_SplitWord(line);
n = 0;
if (*cmd) {
if (!strcasecmp(cmd, "port")) {
if (sscanf(line, "%hu%n", &src->port, &n) != 1) {
result = CPS_BadPort;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "minpoll")) {
if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1) {
result = CPS_BadMinpoll;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxpoll")) {
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1) {
result = CPS_BadMaxpoll;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "presend")) {
if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1) {
result = CPS_BadPresend;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1) {
result = CPS_BadMaxdelaydevratio;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxdelayratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1) {
result = CPS_BadMaxdelayratio;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxdelay")) {
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1) {
result = CPS_BadMaxdelay;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "key")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
src->params.authkey == INACTIVE_AUTHKEY) {
result = CPS_BadKey;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "offline")) {
src->params.online = 0;
} else if (!strcasecmp(cmd, "auto_offline")) {
if (!strcasecmp(cmd, "auto_offline")) {
src->params.auto_offline = 1;
} else if (!strcasecmp(cmd, "burst")) {
src->params.burst = 1;
} else if (!strcasecmp(cmd, "iburst")) {
src->params.iburst = 1;
} else if (!strcasecmp(cmd, "minstratum")) {
if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1) {
result = CPS_BadMinstratum;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "polltarget")) {
if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1) {
result = CPS_BadPolltarget;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "offline")) {
src->params.connectivity = SRC_OFFLINE;
} else if (!strcasecmp(cmd, "noselect")) {
src->params.sel_option = SRC_SelectNoselect;
src->params.sel_options |= SRC_SELECT_NOSELECT;
} else if (!strcasecmp(cmd, "prefer")) {
src->params.sel_option = SRC_SelectPrefer;
} else if (!strcasecmp(cmd, "version")) {
if (sscanf(line, "%d%n", &src->params.version, &n) != 1) {
result = CPS_BadVersion;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxsources")) {
if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1) {
result = CPS_BadMaxsources;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "minsamples")) {
if (sscanf(line, "%d%n", &src->params.min_samples, &n) != 1) {
result = CPS_BadMinsamples;
done = 1;
} else {
line += n;
}
src->params.sel_options |= SRC_SELECT_PREFER;
} else if (!strcasecmp(cmd, "require")) {
src->params.sel_options |= SRC_SELECT_REQUIRE;
} else if (!strcasecmp(cmd, "trust")) {
src->params.sel_options |= SRC_SELECT_TRUST;
} else if (!strcasecmp(cmd, "key")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
src->params.authkey == INACTIVE_AUTHKEY)
return 0;
} else if (!strcasecmp(cmd, "asymmetry")) {
if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "filter")) {
if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxdelay")) {
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxdelayratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxpoll")) {
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxsamples")) {
if (sscanf(line, "%d%n", &src->params.max_samples, &n) != 1) {
result = CPS_BadMaxsamples;
done = 1;
if (sscanf(line, "%d%n", &src->params.max_samples, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxsources")) {
if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "mindelay")) {
if (sscanf(line, "%lf%n", &src->params.min_delay, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "minpoll")) {
if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "minsamples")) {
if (sscanf(line, "%d%n", &src->params.min_samples, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "minstratum")) {
if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "nts")) {
src->params.nts = 1;
} else if (!strcasecmp(cmd, "ntsport")) {
if (sscanf(line, "%d%n", &src->params.nts_port, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "offset")) {
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)
return 0;
} else if (!strcasecmp(cmd, "polltarget")) {
if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "presend")) {
if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "version")) {
if (sscanf(line, "%d%n", &src->params.version, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "xleave")) {
src->params.interleaved = 1;
} else {
line += n;
return 0;
}
}
} else {
result = CPS_BadOption;
done = 1;
}
} else {
done = 1;
}
} while (!done);
}
return result;
return 1;
}
/* ================================================== */
void
CPS_StatusToString(CPS_Status status, char *dest, int len)
int
CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
{
const char *s = NULL;
int n;
char *cmd;
if (len > 0)
dest[0] = '\0';
*stratum = 10;
*distance = 1.0;
*orphan = 0;
switch (status) {
case CPS_Success:
return;
case CPS_BadOption:
s = "server/peer/pool option";
break;
case CPS_BadHost:
s = "address";
break;
case CPS_BadPort:
s = "port";
break;
case CPS_BadMinpoll:
s = "minpoll";
break;
case CPS_BadMaxpoll:
s = "maxpoll";
break;
case CPS_BadPresend:
s = "presend";
break;
case CPS_BadMaxdelaydevratio:
s = "maxdelaydevratio";
break;
case CPS_BadMaxdelayratio:
s = "maxdelayratio";
break;
case CPS_BadMaxdelay:
s = "maxdelay";
break;
case CPS_BadKey:
s = "key";
break;
case CPS_BadMinstratum:
s = "minstratum";
break;
case CPS_BadPolltarget:
s = "polltarget";
break;
case CPS_BadVersion:
s = "version";
break;
case CPS_BadMaxsources:
s = "maxsources";
break;
case CPS_BadMinsamples:
s = "minsamples";
break;
case CPS_BadMaxsamples:
s = "maxsamples";
break;
while (*line) {
cmd = line;
line = CPS_SplitWord(line);
if (!strcasecmp(cmd, "stratum")) {
if (sscanf(line, "%d%n", stratum, &n) != 1 ||
*stratum >= NTP_MAX_STRATUM || *stratum <= 0)
return 0;
} else if (!strcasecmp(cmd, "orphan")) {
*orphan = 1;
n = 0;
} else if (!strcasecmp(cmd, "distance")) {
if (sscanf(line, "%lf%n", distance, &n) != 1)
return 0;
} else {
return 0;
}
snprintf(dest, len, "Invalid %s", s);
line += n;
}
return 1;
}
/* ================================================== */
@@ -341,7 +268,7 @@ CPS_SplitWord(char *line)
/* ================================================== */
int
CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key)
CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key)
{
char *s1, *s2, *s3, *s4;
@@ -358,10 +285,10 @@ CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key)
return 0;
if (*s3) {
*hash = s2;
*type = s2;
*key = s3;
} else {
*hash = "MD5";
*type = "MD5";
*key = s2;
}

View File

@@ -30,26 +30,6 @@
#include "srcparams.h"
#include "addressing.h"
typedef enum {
CPS_Success,
CPS_BadOption,
CPS_BadHost,
CPS_BadPort,
CPS_BadMinpoll,
CPS_BadMaxpoll,
CPS_BadPresend,
CPS_BadMaxdelaydevratio,
CPS_BadMaxdelayratio,
CPS_BadMaxdelay,
CPS_BadKey,
CPS_BadMinstratum,
CPS_BadPolltarget,
CPS_BadVersion,
CPS_BadMaxsources,
CPS_BadMinsamples,
CPS_BadMaxsamples,
} CPS_Status;
typedef struct {
char *name;
unsigned short port;
@@ -57,10 +37,10 @@ typedef struct {
} CPS_NTP_Source;
/* Parse a command to add an NTP server or peer */
extern CPS_Status CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
/* Get a string describing error status */
extern void CPS_StatusToString(CPS_Status status, char *dest, int len);
/* Parse a command to enable local reference */
extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance);
/* Remove extra white-space and comments */
extern void CPS_NormalizeLine(char *line);
@@ -69,6 +49,6 @@ extern void CPS_NormalizeLine(char *line);
extern char *CPS_SplitWord(char *line);
/* Parse a key from keyfile */
extern int CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key);
extern int CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key);
#endif /* GOT_CMDPARSE_H */

680
conf.c

File diff suppressed because it is too large Load Diff

52
conf.h
View File

@@ -31,7 +31,7 @@
#include "addressing.h"
#include "reference.h"
extern void CNF_Initialise(int restarted);
extern void CNF_Initialise(int restarted, int client_only);
extern void CNF_Finalise(void);
extern char *CNF_GetRtcDevice(void);
@@ -39,6 +39,8 @@ extern char *CNF_GetRtcDevice(void);
extern void CNF_ReadFile(const char *filename);
extern void CNF_ParseLine(const char *filename, int number, char *line);
extern void CNF_CreateDirs(uid_t uid, gid_t gid);
extern void CNF_AddInitSources(void);
extern void CNF_AddSources(void);
extern void CNF_AddBroadcasts(void);
@@ -50,7 +52,7 @@ extern char *CNF_GetDriftFile(void);
extern char *CNF_GetLogDir(void);
extern char *CNF_GetDumpDir(void);
extern int CNF_GetLogBanner(void);
extern int CNF_GetLogMeasurements(void);
extern int CNF_GetLogMeasurements(int *raw);
extern int CNF_GetLogStatistics(void);
extern int CNF_GetLogTracking(void);
extern int CNF_GetLogRtc(void);
@@ -58,16 +60,13 @@ extern int CNF_GetLogRefclocks(void);
extern int CNF_GetLogTempComp(void);
extern char *CNF_GetKeysFile(void);
extern char *CNF_GetRtcFile(void);
extern uint32_t CNF_GetCommandKey(void);
extern int CNF_GetGenerateCommandKey(void);
extern int CNF_GetDumpOnExit(void);
extern int CNF_GetManualEnabled(void);
extern int CNF_GetCommandPort(void);
extern int CNF_GetRtcOnUtc(void);
extern int CNF_GetRtcSync(void);
extern void CNF_GetMakeStep(int *limit, double *threshold);
extern void CNF_GetMaxChange(int *delay, int *ignore, double *offset);
extern void CNF_GetLogChange(int *enabled, double *threshold);
extern double CNF_GetLogChange(void);
extern void CNF_GetMailOnChange(int *enabled, double *threshold, char **user);
extern int CNF_GetNoClientLog(void);
extern unsigned long CNF_GetClientLogLimit(void);
@@ -75,6 +74,8 @@ 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_GetBindCommandPath(void);
extern char *CNF_GetNtpSigndSocket(void);
extern char *CNF_GetPidFile(void);
extern REF_LeapMode CNF_GetLeapSecMode(void);
extern char *CNF_GetLeapSecTimezone(void);
@@ -82,20 +83,25 @@ extern char *CNF_GetLeapSecTimezone(void);
/* Value returned in ppm, as read from file */
extern double CNF_GetMaxUpdateSkew(void);
extern double CNF_GetMaxClockError(void);
extern double CNF_GetMaxDrift(void);
extern double CNF_GetCorrectionTimeRatio(void);
extern double CNF_GetMaxSlewRate(void);
extern double CNF_GetMaxDistance(void);
extern double CNF_GetMaxJitter(void);
extern double CNF_GetReselectDistance(void);
extern double CNF_GetStratumWeight(void);
extern double CNF_GetCombineLimit(void);
extern int CNF_AllowLocalReference(int *stratum);
extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance);
extern void CNF_SetupAccessRestrictions(void);
extern int CNF_GetSchedPriority(void);
extern int CNF_GetLockMemory(void);
extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak);
extern int CNF_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);
@@ -112,4 +118,36 @@ extern char *CNF_GetHwclockFile(void);
extern int CNF_GetInitSources(void);
extern double CNF_GetInitStepThreshold(void);
typedef enum {
CNF_HWTS_RXFILTER_ANY,
CNF_HWTS_RXFILTER_NONE,
CNF_HWTS_RXFILTER_NTP,
CNF_HWTS_RXFILTER_ALL,
} CNF_HwTs_RxFilter;
typedef struct {
char *name;
int minpoll;
int min_samples;
int max_samples;
int nocrossts;
CNF_HwTs_RxFilter rxfilter;
double precision;
double tx_comp;
double rx_comp;
} CNF_HwTsInterface;
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
extern char *CNF_GetNtsCacheDir(void);
extern char *CNF_GetNtsServerCertFile(void);
extern char *CNF_GetNtsServerKeyFile(void);
extern int CNF_GetNtsServerPort(void);
extern int CNF_GetNtsServerProcesses(void);
extern int CNF_GetNtsServerConnections(void);
extern int CNF_GetNtsRefresh(void);
extern int CNF_GetNtsRotate(void);
extern char *CNF_GetNtsTrustedCertFile(void);
extern int CNF_GetNoSystemCert(void);
#endif /* GOT_CONF_H */

637
configure vendored

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,103 @@
Notes for installing chrony on macOS
Author: Bryan Christianson (bryan@whatroute.net)
------------------------------------------------
These files are for those admins/users who would prefer to install chrony
from the source distribution and are intended as guidelines rather than
being definitive. They can be edited with a plain text editor, such as
vi, emacs or your favourite IDE (Xcode)
It is assumed you are comfortable with installing software from the
terminal command line and know how to use sudo to acquire root access.
If you are not familiar with the macOS command line then
please consider using ChronyControl from http://whatroute.net/chronycontrol.html
ChronyControl provides a gui wrapper for installing these files and sets the
necessary permissions on each file.
Install the chrony software
---------------------------
You will need xcode and the commandline additions to build and install chrony.
These can be obtained from Apple's website via the App Store.
cd to the chrony directory
./configure
make
sudo make install
chrony is now installed in default locations (/usr/local/sbin/chronyd,
/usr/local/bin/chronyc)
Create a chrony.conf file - see the chrony website for details
The support files here assume the following directives are specified in the
chrony.conf file
keyfile /etc/chrony.d/chrony.keys
driftfile /var/db/chrony/chrony.drift
bindcmdaddress /var/db/chrony/chronyd.sock
logdir /var/log/chrony
dumpdir /var/db/chrony
Install this file as /etc/chrony.d/chrony.conf and create
the directories specified in the above directives if they don't exist.
You will need root permissions to create the directories.
Running chronyd
---------------
At this point chronyd *could* be run as a daemon. Apple discourage running
daemons and their preferred method uses the launchd facility. The
support files here provide a launchd configuration file for chronyd and also
a shell script and launchd configuration file to rotate the chronyd logs on a daily basis.
Support files
-------------
Dates and sizes may differ
-rw-r--r-- 1 yourname staff 2084 4 Aug 22:54 README.txt
-rwxr-xr-x 1 yourname staff 676 4 Aug 21:18 chronylogrotate.sh
-rw-r--r-- 1 yourname staff 543 18 Jul 20:10 org.tuxfamily.chronyc.plist
-rw-r--r-- 1 yourname staff 511 19 Jun 18:30 org.tuxfamily.chronyd.plist
If you have used chrony support directories other than those suggested, you
will need to edit each file and make the appropriate changes.
Installing the support files
----------------------------
1. chronylogrotate.sh
This is a simple shell script that deletes old log files. Unfortunately because
of the need to run chronyc, the standard macOS logrotation does not work with
chrony logs.
This script runs on a daily basis under control of launchd and should be
installed in the /usr/local/bin directory
sudo cp chronylogrotate.sh /usr/local/bin
sudo chmod +x /usr/local/bin/chronylogrotate.sh
sudo chown root:wheel /usr/local/bin/chronylogrotate.sh
2. org.tuxfamily.chronyc.plist
This file is the launchd plist that runs logrotation each day. You may
wish to edit this file to change the time of day at which the rotation
will run, currently 04:05 am
sudo cp org.tuxfamily.chronyc.plist /Library/LaunchDaemons
sudo chown root:wheel /Library/LaunchDaemons/org.tuxfamily.chronyc.plist
sudo chmod 0644 /Library/LaunchDaemons/org.tuxfamily.chronyc.plist
sudo launchctl load -w /Library/LaunchDaemons/org.tuxfamily.chronyc.plist
3. org.tuxfamily.chronyd.plist
This file is the launchd plist that runs chronyd when the Macintosh starts.
sudo cp org.tuxfamily.chronyd.plist /Library/LaunchDaemons
sudo chown root:wheel /Library/LaunchDaemons/org.tuxfamily.chronyd.plist
sudo chmod 0644 /Library/LaunchDaemons/org.tuxfamily.chronyd.plist
sudo launchctl load -w /Library/LaunchDaemons/org.tuxfamily.chronyd.plist

View File

@@ -0,0 +1,58 @@
#!/bin/sh
# chronyd/chronyc - Programs for keeping computer clocks accurate.
#
# **********************************************************************
# * Copyright (C) Bryan Christianson 2015
# *
# * 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.
# *
# **********************************************************************
LOGDIR=/var/log/chrony
rotate () {
prefix=$1
rm -f $prefix.log.10
for (( count=9; count>= 0; count-- ))
do
next=$(( $count+1 ))
if [ -f $prefix.log.$count ]; then
mv $prefix.log.$count $prefix.log.$next
fi
done
if [ -f $prefix.log ]; then
mv $prefix.log $prefix.log.0
fi
}
if [ ! -e "$LOGDIR" ]; then
logger -s "missing directory: $LOGDIR"
exit 1
fi
cd $LOGDIR
rotate measurements
rotate statistics
rotate tracking
#
# signal chronyd via chronyc
/usr/local/bin/chronyc cyclelogs > /dev/null
exit $?

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.tuxfamily.logrotate</string>
<key>KeepAlive</key>
<false/>
<key>ProgramArguments</key>
<array>
<string>/bin/sh</string>
<string>/usr/local/bin/chronylogrotate.sh</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>5</integer>
<key>Hour</key>
<integer>4</integer>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.tuxfamily.chronyd</string>
<key>Program</key>
<string>/usr/local/sbin/chronyd</string>
<key>ProgramArguments</key>
<array>
<string>chronyd</string>
<string>-n</string>
<string>-f</string>
<string>/private/etc/chrony.d/chrony.conf</string>
</array>
<key>KeepAlive</key>
<true/>
</dict>
</plist>

76
doc/Makefile.in Normal file
View File

@@ -0,0 +1,76 @@
ADOC = asciidoctor
ADOC_FLAGS =
SED = sed
HTML_TO_TXT = w3m -dump -T text/html
MAN_FILES = chrony.conf.man chronyc.man chronyd.man
TXT_FILES = faq.txt installation.txt
HTML_FILES = $(MAN_FILES:%.man=%.html) $(TXT_FILES:%.txt=%.html)
MAN_IN_FILES = $(MAN_FILES:%.man=%.man.in)
SYSCONFDIR = @SYSCONFDIR@
BINDIR = @BINDIR@
SBINDIR = @SBINDIR@
MANDIR = @MANDIR@
DOCDIR = @DOCDIR@
CHRONYRUNDIR = @CHRONYRUNDIR@
CHRONYVARDIR = @CHRONYVARDIR@
CHRONY_VERSION = @CHRONY_VERSION@
DEFAULT_USER = @DEFAULT_USER@
DEFAULT_HWCLOCK_FILE = @DEFAULT_HWCLOCK_FILE@
DEFAULT_PID_FILE = @DEFAULT_PID_FILE@
DEFAULT_RTC_DEVICE = @DEFAULT_RTC_DEVICE@
SED_COMMANDS = "s%\@SYSCONFDIR\@%$(SYSCONFDIR)%g;\
s%\@BINDIR\@%$(BINDIR)%g;\
s%\@SBINDIR\@%$(SBINDIR)%g;\
s%\@CHRONY_VERSION\@%$(CHRONY_VERSION)%g;\
s%\@DEFAULT_HWCLOCK_FILE\@%$(DEFAULT_HWCLOCK_FILE)%g;\
s%\@DEFAULT_PID_FILE\@%$(DEFAULT_PID_FILE)%g;\
s%\@DEFAULT_RTC_DEVICE\@%$(DEFAULT_RTC_DEVICE)%g;\
s%\@DEFAULT_USER\@%$(DEFAULT_USER)%g;\
s%\@CHRONYRUNDIR\@%$(CHRONYRUNDIR)%g;\
s%\@CHRONYVARDIR\@%$(CHRONYVARDIR)%g;"
man: $(MAN_FILES) $(MAN_IN_FILES)
html: $(HTML_FILES)
txt: $(TXT_FILES)
docs: man html
%.html: %.adoc
$(ADOC) $(ADOC_FLAGS) -b html -o - $< | $(SED) -e $(SED_COMMANDS) > $@
%.man.in: %.adoc
$(ADOC) $(ADOC_FLAGS) -b manpage -o $@ $<
%.man: %.man.in
$(SED) -e $(SED_COMMANDS) < $< > $@
%.txt: %.html
$(HTML_TO_TXT) < $< > $@
install: $(MAN_FILES)
[ -d $(DESTDIR)$(MANDIR)/man1 ] || mkdir -p $(DESTDIR)$(MANDIR)/man1
[ -d $(DESTDIR)$(MANDIR)/man5 ] || mkdir -p $(DESTDIR)$(MANDIR)/man5
[ -d $(DESTDIR)$(MANDIR)/man8 ] || mkdir -p $(DESTDIR)$(MANDIR)/man8
cp chronyc.man $(DESTDIR)$(MANDIR)/man1/chronyc.1
chmod 644 $(DESTDIR)$(MANDIR)/man1/chronyc.1
cp chronyd.man $(DESTDIR)$(MANDIR)/man8/chronyd.8
chmod 644 $(DESTDIR)$(MANDIR)/man8/chronyd.8
cp chrony.conf.man $(DESTDIR)$(MANDIR)/man5/chrony.conf.5
chmod 644 $(DESTDIR)$(MANDIR)/man5/chrony.conf.5
install-docs: $(HTML_FILES)
[ -d $(DESTDIR)$(DOCDIR) ] || mkdir -p $(DESTDIR)$(DOCDIR)
for f in $(HTML_FILES); do \
cp $$f $(DESTDIR)$(DOCDIR); \
chmod 644 $(DESTDIR)$(DOCDIR)/$$f; \
done
clean:
rm -f $(MAN_FILES) $(TXT_FILES) $(HTML_FILES)
rm -f $(MAN_IN_FILES)
distclean:
rm -f $(MAN_FILES) $(TXT_FILES) $(HTML_FILES)
rm -f Makefile

2545
doc/chrony.conf.adoc Normal file

File diff suppressed because it is too large Load Diff

1266
doc/chronyc.adoc Normal file

File diff suppressed because it is too large Load Diff

194
doc/chronyd.adoc Normal file
View File

@@ -0,0 +1,194 @@
// This file is part of chrony
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Miroslav Lichvar 2009-2017
//
// 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.
= chronyd(8)
:doctype: manpage
:man manual: System Administration
:man source: chrony @CHRONY_VERSION@
== NAME
chronyd - chrony daemon
== SYNOPSIS
*chronyd* [_OPTION_]... [_DIRECTIVE_]...
== DESCRIPTION
*chronyd* is a daemon for synchronisation of the system clock. It can
synchronise the clock with NTP servers, reference clocks (e.g. a GPS receiver),
and manual input using wristwatch and keyboard via *chronyc*. It can also
operate as an NTPv4 (RFC 5905) server and peer to provide a time service to
other computers in the network.
If no configuration directives are specified on the command line, *chronyd*
will read them from a configuration file. The compiled-in default location of
the file is _@SYSCONFDIR@/chrony.conf_.
Informational messages, warnings, and errors will be logged to syslog.
== OPTIONS
*-4*::
With this option hostnames will be resolved only to IPv4 addresses and only
IPv4 sockets will be created.
*-6*::
With this option hostnames will be resolved only to IPv6 addresses and only
IPv6 sockets will be created.
*-f* _file_::
This option can be used to specify an alternate location for the configuration
file. The default value is _@SYSCONFDIR@/chrony.conf_.
*-n*::
When run in this mode, the program will not detach itself from the terminal.
*-d*::
When run in this mode, the program will not detach itself from the terminal,
and all messages will be written to the terminal instead of syslog. If
*chronyd* was compiled with enabled support for debugging, this option can be
used twice to enable debug messages.
*-l* _file_::
This option enables writing of log messages to a file instead of syslog or the
terminal.
*-L* _level_::
This option specifies the minimum severity level of messages to be written to
the log file, syslog, or terminal. The following levels can be specified:
0 (informational), 1 (warning), 2 (non-fatal error), and 3 (fatal error). The
default value is 0.
*-q*::
When run in this mode, *chronyd* will set the system clock once and exit. It
will not detach from the terminal.
*-Q*::
This option is similar to the *-q* option, except it only prints the offset
without making any corrections of the clock and it allows *chronyd* to be
started without root privileges.
*-r*::
This option will try to reload and then delete files containing sample
histories for each of the servers and reference clocks being used. The
files are expected to be in the directory specified by the
<<chrony.conf.adoc#dumpdir,*dumpdir*>>
directive in the configuration file. This option is useful if you want to stop
and restart *chronyd* briefly for any reason, e.g. to install a new version.
However, it should be used only on systems where the kernel can maintain clock
compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD,
Solaris, and macOS 10.13 or later).
*-R*::
When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
directive and the <<chrony.conf.adoc#makestep,*makestep*>> directive used with
a positive limit will be ignored. This option is useful when restarting
*chronyd* and can be used in conjunction with the *-r* option.
*-s*::
This option will set the system clock from the computer's real-time clock (RTC)
or to the last modification time of the file specified by the
<<chrony.conf.adoc#driftfile,*driftfile*>> directive. Real-time clocks are
supported only on Linux.
+
If used in conjunction with the *-r* flag, *chronyd* will attempt to preserve
the old samples after setting the system clock from the RTC. This can be used
to allow *chronyd* to perform long term averaging of the gain or loss rate
across system reboots, and is useful for systems with intermittent access to
network that are shut down when not in use. For this to work well, it relies
on *chronyd* having been able to determine accurate statistics for the
difference between the RTC and system clock last time the computer was on.
+
If the last modification time of the drift file is later than both the current
time and the RTC time, the system time will be set to it to restore the time
when *chronyd* was previously stopped. This is useful on computers that have no
RTC or the RTC is broken (e.g. it has no battery).
*-t* _timeout_::
This option sets a timeout (in seconds) after which *chronyd* will exit. If the
clock is not synchronised, it will exit with a non-zero status. This is useful
with the *-q* or *-Q* option to shorten the maximum time waiting for
measurements, or with the *-r* option to limit the time when *chronyd* is
running, but still allow it to adjust the frequency of the system clock.
*-u* _user_::
This option sets the name of the system user to which *chronyd* will switch
after start in order to drop root privileges. It overrides the
<<chrony.conf.adoc#user,*user*>> directive. The default value is
_@DEFAULT_USER@_.
+
On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
The child process retains root privileges, but can only perform a very limited
range of privileged system calls on behalf of the parent.
*-F* _level_::
This option configures a system call filter when *chronyd* is compiled with
support for the Linux secure computing (seccomp) facility. In level 1 the
process is killed when a forbidden system call is made, in level -1 the SIGSYS
signal is thrown instead and in level 0 the filter is disabled. The default
value is 0.
+
It's recommended to enable the filter only when it's known to work on the
version of the system where *chrony* is installed as the filter needs to allow
also system calls made from libraries that *chronyd* is using (e.g. libc) and
different versions or implementations of the libraries may make different
system calls. If the filter is missing some system call, *chronyd* could be
killed even in normal operation.
*-P* _priority_::
On Linux, this option will select the SCHED_FIFO real-time scheduler at the
specified priority (which must be between 0 and 100). On macOS, this option
must have either a value of 0 to disable the thread time
constraint policy or 1 for the policy to be enabled. Other systems do not
support this option. The default value is 0.
*-m*::
This option will lock *chronyd* into RAM so that it will never be paged out.
This mode is only supported on Linux.
*-x*::
This option disables the control of the system clock. *chronyd* will not try to
make any adjustments of the clock. It will assume the clock is free running and
still track its offset and frequency relative to the estimated true time. This
option allows *chronyd* to run without the capability to adjust or set the
system clock (e.g. in some containers) in order to operate as an NTP server. It
is not recommended to run *chronyd* (with or without *-x*) when another process
is controlling the system clock.
*-v*::
With this option *chronyd* will print version number to the terminal and exit.
== FILES
_@SYSCONFDIR@/chrony.conf_
== SEE ALSO
<<chronyc.adoc#,*chronyc(1)*>>, <<chrony.conf.adoc#,*chrony.conf(5)*>>
== BUGS
For instructions on how to report bugs, please visit
https://chrony.tuxfamily.org/.
== AUTHORS
chrony was written by Richard Curnow, Miroslav Lichvar, and others.

569
doc/faq.adoc Normal file
View File

@@ -0,0 +1,569 @@
// This file is part of chrony
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Miroslav Lichvar 2014-2016
//
// 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.
= Frequently Asked Questions
:toc:
:numbered:
== `chrony` compared to other programs
=== How does `chrony` compare to `ntpd`?
`chronyd` was designed to work well in a wide range of conditions and it can
usually synchronise the system clock faster and with better time accuracy. It
doesn't implement some of the less useful NTP modes like broadcast client or
multicast server/client.
If your computer is connected to the Internet only for few minutes at a time,
the network connection is often congested, you turn your computer off or
suspend it frequently, the clock is not very stable (e.g. there are rapid
changes in the temperature or it's a virtual machine), or you want to use NTP
on an isolated network with no hardware reference clocks in sight, `chrony`
will probably work much better for you.
For a more detailed comparison of features and performance, see the
https://chrony.tuxfamily.org/comparison.html[comparison page] on the `chrony`
website.
== Configuration issues
=== What is the minimum recommended configuration for an NTP client?
First, the client needs to know which NTP servers it should ask for the current
time. They are specified by the `server` or `pool` directive. The `pool`
directive can be used for names that resolve to multiple addresses. For good
reliability the client should have at least three servers. The `iburst` option
speeds up the initial synchronisation.
To stabilise the initial synchronisation on the next start, the estimated drift
of the system clock is saved to a file specified by the `driftfile` directive.
If the system clock can be far from the true time after boot for any reason,
`chronyd` should be allowed to correct it quickly by stepping instead of
slewing, which would take a very long time. The `makestep` directive does
that.
In order to keep the real-time clock (RTC) close to the true time, so the
system time is reasonably close to the true time when it's initialised on the
next boot from the RTC, the `rtcsync` directive enables a mode in which the
system time is periodically copied to the RTC. It is supported on Linux and
macOS.
If you want to use public NTP servers from the
http://www.pool.ntp.org/[pool.ntp.org] project, the minimal _chrony.conf_ file
could be:
----
pool pool.ntp.org iburst
driftfile /var/lib/chrony/drift
makestep 1 3
rtcsync
----
=== How do I make an NTP server from an NTP client?
You need to add an `allow` directive to the _chrony.conf_ file in order to open
the NTP port and allow `chronyd` to reply to client requests. `allow` with no
specified subnet allows access from all IPv4 and IPv6 addresses.
=== I have several computers on a LAN. Should be all clients of an external server?
The best configuration is usually to make one computer the server, with
the others as clients of it. Add a `local` directive to the server's
_chrony.conf_ file. This configuration will be better because
* the load on the external connection is less
* the load on the external NTP server(s) is less
* if your external connection goes down, the computers on the LAN
will maintain a common time with each other.
=== Must I specify servers by IP address if DNS is not available on chronyd start?
No. Starting from version 1.25, `chronyd` will keep trying to resolve
the names specified by the `server`, `pool`, and `peer` directives in an
increasing interval until it succeeds. The `online` command can be issued from
`chronyc` to force `chronyd` to try to resolve the names immediately.
=== How can I make `chronyd` more secure?
If you don't need to serve time to NTP clients or peers, you can add `port 0`
to the _chrony.conf_ file to completely disable the NTP server functionality
and prevent NTP requests from reaching `chronyd`. Starting from version 2.0,
the NTP server port is open only when client access is allowed by the `allow`
directive or command, an NTP peer is configured, or the `broadcast` directive
is used.
If you don't need to use `chronyc` remotely, you can add the following
directives to the configuration file to bind the command sockets to the
loopback interface. This is done by default since version 2.0.
----
bindcmdaddress 127.0.0.1
bindcmdaddress ::1
----
If you don't need to use `chronyc` at all or you need to run `chronyc` only
under the root or _chrony_ user (which can access `chronyd` through a Unix
domain socket since version 2.2), you can disable the internet command sockets
completely by adding `cmdport 0` to the configuration file.
You can specify an unprivileged user with the `-u` option, or the `user`
directive in the _chrony.conf_ file, to which `chronyd` will switch after start
in order to drop root privileges. The configure script has a `--with-user`
option, which sets the default user. On Linux, `chronyd` needs to be compiled
with support for the `libcap` library. On other systems, `chronyd` forks into
two processes. The child process retains root privileges, but can only perform
a very limited range of privileged system calls on behalf of the parent.
Also, if `chronyd` is compiled with support for the Linux secure computing
(seccomp) facility, you can enable a system call filter with the `-F` option.
It will significantly reduce the kernel attack surface and possibly prevent
kernel exploits from the `chronyd` process if it's compromised. It's
recommended to enable the filter only when it's known to work on the version of
the system where `chrony` is installed as the filter needs to allow also system
calls made from libraries that `chronyd` is using (e.g. libc) and different
versions or implementations of the libraries may make different system calls.
If the filter is missing some system call, `chronyd` could be killed even in
normal operation.
=== How can I improve the accuracy of the system clock with NTP sources?
Select NTP servers that are well synchronised, stable and close to your
network. It's better to use more than one server, three or four is usually
recommended as the minimum, so `chronyd` can detect servers that serve false
time and combine measurements from multiple sources.
If you have a network card with hardware timestamping supported on Linux, it
can be enabled by the *hwtimestamp* directive in the _chrony.conf_ file. It
should make local receive and transmit timestamps of NTP packets much more
accurate.
There are also useful options which can be set in the `server` directive, they
are `minpoll`, `maxpoll`, `polltarget`, `maxdelay`, `maxdelayratio`,
`maxdelaydevratio`, and `xleave`.
The first three options set the minimum and maximum allowed polling interval,
and how should be the actual interval adjusted in the specified range. Their
default values are 6 (64 seconds) for `minpoll`, 10 (1024 seconds) for
`maxpoll` and 8 (samples) for `polltarget`. The default values should be used
for general servers on the Internet. With your own NTP servers, or if you have
permission to poll some servers more frequently, setting these options for
shorter polling intervals may significantly improve the accuracy of the system
clock.
The optimal polling interval depends mainly on two factors, stability of the
network latency and stability of the system clock (which mainly depends on the
temperature sensitivity of the crystal oscillator and the maximum rate of the
temperature change).
Generally, if the `sourcestats` command usually reports a small number of
samples retained for a source (e.g. fewer than 16), a shorter polling interval
should be considered. If the number of samples is usually at the maximum of 64,
a longer polling interval may work better.
An example of the directive for an NTP server on the Internet that you are
allowed to poll frequently could be
----
server foo.example.net minpoll 4 maxpoll 6 polltarget 16
----
An example using shorter polling intervals with a server located in the same
LAN could be
----
server ntp.local minpoll 2 maxpoll 4 polltarget 30
----
The maxdelay options are useful to ignore measurements with an unusally large
delay (e.g. due to congestion in the network) and improve the stability of the
synchronisation. The `maxdelaydevratio` option could be added to the example
with local NTP server
----
server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
----
If your server supports the interleaved mode (e.g. it is running `chronyd`),
the `xleave` option should be added to the `server` directive in order to allow
the server to send the client more accurate transmit timestamps (kernel or
preferably hardware). For example:
----
server ntp.local minpoll 2 maxpoll 4 xleave
----
When combined with local hardware timestamping, good network switches, and even
shorter polling intervals, a sub-microsecond accuracy and stability of a few
tens of nanoseconds may be possible. For example:
----
server ntp.local minpoll 0 maxpoll 0 xleave
hwtimestamp eth0
----
For best stability, the CPU should be running at a constant frequency (i.e.
disabled power saving and performance boosting). Energy-Efficient Ethernet
(EEE) should be disabled in the network. The switches should be configured to
prioritize NTP packets, especially if the network is expected to be heavily
loaded.
If it is acceptable for NTP clients in the network to send requests at an
excessive rate, a sub-second polling interval may be specified. A median filter
can be enabled in order to update the clock at a reduced rate with more stable
measurements. For example:
----
server ntp.local minpoll -6 maxpoll -6 filter 15 xleave
hwtimestamp eth0 minpoll -6
----
=== Does `chronyd` have an ntpdate mode?
Yes. With the `-q` option `chronyd` will set the system clock once and exit.
With the `-Q` option it will print the measured offset without setting the
clock. If you don't want to use a configuration file, NTP servers can be
specified on the command line. For example:
----
# chronyd -q 'pool pool.ntp.org iburst'
----
The command above would normally take about 5 seconds if the servers were
well synchronised and responding to all requests. If not synchronised or
responding, it would take about 10 seconds for `chronyd` to give up and exit
with a non-zero status. A faster configuration is possible. A single server can
be used instead of four servers, the number of measurements can be reduced with
the `maxsamples` option, and a timeout can be specified with the `-t` option.
The following command would take only up to about 1 second.
----
# chronyd -q -t 1 'server pool.ntp.org iburst maxsamples 1'
----
=== Can `chronyd` be configured to control the clock like `ntpd`?
It is not possible to perfectly emulate `ntpd`, but there are some options that
can configure `chronyd` to behave more like `ntpd`.
In the following example the `minsamples` directive slows down the response to
changes in the frequency and offset of the clock. The `maxslewrate` and
`corrtimeratio` directives reduce the maximum frequency error due to an offset
correction and the `maxdrift` directive reduces the maximum assumed frequency
error of the clock. The `makestep` directive enables a step threshold and the
`maxchange` directive enables a panic threshold. The `maxclockerror` directive
increases the minimum dispersion rate.
----
minsamples 32
maxslewrate 500
corrtimeratio 100
maxdrift 500
makestep 0.128 -1
maxchange 1000 1 1
maxclockerror 15
----
Note that increasing `minsamples` may cause the offsets in the `tracking` and
`sourcestats` reports/logs to be significantly smaller than the actual offsets
and be unsuitable for monitoring.
=== What happened to the `commandkey` and `generatecommandkey` directives?
They were removed in version 2.2. Authentication is no longer supported in the
command protocol. Commands that required authentication are now allowed only
through a Unix domain socket, which is accessible only by the root and _chrony_
users. If you need to configure `chronyd` remotely or locally without the root
password, please consider using ssh and/or sudo to run `chronyc` under the root
or _chrony_ user on the host where `chronyd` is running.
== Computer is not synchronising
This is the most common problem. There are a number of reasons, see the
following questions.
=== Behind a firewall?
Check the `Reach` value printed by the ``chronyc``'s `sources` command. If it's
zero, it means `chronyd` did not get any valid responses from the NTP server
you are trying to use. If there is a firewall between you and the server, the
packets may be blocked. Try using a tool like `wireshark` or `tcpdump` to see
if you're getting any responses from the server.
When `chronyd` is receiving responses from the servers, the output of the
`sources` command issued few minutes after `chronyd` start might look like
this:
----
210 Number of sources = 3
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^* foo.example.net 2 6 377 34 +484us[ -157us] +/- 30ms
^- bar.example.net 2 6 377 34 +33ms[ +32ms] +/- 47ms
^+ baz.example.net 3 6 377 35 -1397us[-2033us] +/- 60ms
----
=== Are NTP servers specified with the `offline` option?
Check that you're using ``chronyc``'s `online` and `offline` commands
appropriately. The `activity` command prints the number of sources that are
currently online and offline. For example:
----
200 OK
3 sources online
0 sources offline
0 sources doing burst (return to online)
0 sources doing burst (return to offline)
0 sources with unknown address
----
=== Is `chronyd` allowed to step the system clock?
By default, `chronyd` adjusts the clock gradually by slowing it down or
speeding it up. If the clock is too far from the true time, it will take
a long time to correct the error. The `System time` value printed by the
``chronyc``'s `tracking` command is the remaining correction that needs to be
applied to the system clock.
The `makestep` directive can be used to allow `chronyd` to step the clock. For
example, if _chrony.conf_ had
----
makestep 1 3
----
the clock would be stepped in the first three updates if its offset was larger
than one second. Normally, it's recommended to allow the step only in the first
few updates, but in some cases (e.g. a computer without an RTC or virtual
machine which can be suspended and resumed with an incorrect time) it may be
necessary to allow the step on any clock update. The example above would change
to
----
makestep 1 -1
----
=== Using a Windows NTP server?
A common issue with Windows NTP servers is that they report a very large root
dispersion (e.g. three seconds or more), which causes `chronyd` to ignore the
server for being too inaccurate. The `sources` command may show a valid
measurement, but the server is not selected for synchronisation. You can check
the root dispersion of the server with the ``chronyc``'s `ntpdata` command.
The `maxdistance` value needs to be increased in _chrony.conf_ to enable
synchronisation to such a server. For example:
----
maxdistance 16.0
----
=== Using a PPS reference clock?
A pulse-per-second (PPS) reference clock requires a non-PPS time source to
determine which second of UTC corresponds to each pulse. If it is another
reference clock specified with the `lock` option in the `refclock` directive,
the offset between the two reference clocks must be smaller than 0.2 seconds in
order for the PPS reference clock to work. With NMEA reference clocks it is
common to have a larger offset. It needs to be corrected with the `offset`
option.
One approach to find out a good value of the `offset` option is to configure
the reference clocks with the `noselect` option and compare them to an NTP
server. For example, if the `sourcestats` command showed
----
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
==============================================================================
PPS0 0 0 0 +0.000 2000.000 +0ns 4000ms
NMEA 58 30 231 -96.494 38.406 +504ms 6080us
foo.example.net 7 3 200 -2.991 16.141 -107us 492us
----
the offset of the NMEA source would need to be increased by about 0.504
seconds. It does not have to be very accurate. As long as the offset of the
NMEA reference clock stays below 0.2 seconds, the PPS reference clock should be
able to determine the seconds corresponding to the pulses and allow the samples
to be used for synchronisation.
== Issues with `chronyc`
=== I keep getting the error `506 Cannot talk to daemon`
When accessing `chronyd` remotely, make sure that the _chrony.conf_ file (on
the computer where `chronyd` is running) has a `cmdallow` entry for the
computer you are running `chronyc` on and an appropriate `bindcmdaddress`
directive. This isn't necessary for localhost.
Perhaps `chronyd` is not running. Try using the `ps` command (e.g. on Linux,
`ps -auxw`) to see if it's running. Or try `netstat -a` and see if the ports
123/udp and 323/udp are listening. If `chronyd` is not running, you may have a
problem with the way you are trying to start it (e.g. at boot time).
Perhaps you have a firewall set up in a way that blocks packets on port
323/udp. You need to amend the firewall configuration in this case.
=== 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.
With older versions, 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`
authentication (`commandkey` directive).
=== Why does `chronyc tracking` always print an IPv4 address as reference ID?
The reference ID is a 32-bit value and in versions before 3.0 it was printed in
quad-dotted notation, even if the reference source did not actually have an
IPv4 address. For IPv4 addresses, the reference ID is equal to the address, but
for IPv6 addresses it is the first 32 bits of the MD5 sum of the address. For
reference clocks, the reference ID is the value specified with the `refid`
option in the `refclock` directive.
Since version 3.0, the reference ID is printed as a hexadecimal number to avoid
confusion with IPv4 addresses.
If you need to get the IP address of the current reference source, use the `-n`
option to disable resolving of IP addresses and read the second field (printed
in parentheses) on the `Reference ID` line.
=== Is the `chronyc` / `chronyd` protocol documented anywhere?
Only by the source code. See _cmdmon.c_ (`chronyd` side) and _client.c_
(`chronyc` side).
== Real-time clock issues
=== What is the real-time clock (RTC)?
This is the clock which keeps the time even when your computer is turned off.
It is used to initialise the system clock on boot. It normally doesn't drift
more than few seconds per day.
There are two approaches how `chronyd` can work with it. One is to use the
`rtcsync` directive, which tells `chronyd` to enable a kernel mode which sets
the RTC from the system clock every 11 minutes. `chronyd` itself won't touch
the RTC. If the computer is not turned off for a long time, the RTC should
still be close to the true time when the system clock will be initialised from
it on the next boot.
The other option is to use the `rtcfile` directive, which tells `chronyd` to
monitor the rate at which the RTC gains or loses time. When `chronyd` is
started with the `-s` option on the next boot, it will set the system time from
the RTC and also compensate for the drift it has measured previously. The
`rtcautotrim` directive can be used to keep the RTC close to the true time, but
it's not strictly necessary if its only purpose is to set the system clock when
`chronyd` is started on boot. See the documentation for details.
=== I want to use ``chronyd``'s RTC support. Must I disable `hwclock`?
The `hwclock` program is often set-up by default in the boot and shutdown
scripts with many Linux installations. With the kernel RTC synchronisation
(`rtcsync` directive), the RTC will be set also every 11 minutes as long as the
system clock is synchronised. If you want to use ``chronyd``'s RTC monitoring
(`rtcfile` directive), it's important to disable `hwclock` in the shutdown
procedure. If you don't, it will over-write the RTC with a new value, unknown
to `chronyd`. At the next reboot, `chronyd` started with the `-s` option will
compensate this (wrong) time with its estimate of how far the RTC has drifted
whilst the power was off, giving a meaningless initial system time.
There is no need to remove `hwclock` from the boot process, as long as `chronyd`
is started after it has run.
=== I just keep getting the `513 RTC driver not running` message
For the real-time clock support to work, you need the following three
things
* an RTC in your computer
* a Linux kernel with enabled RTC support
* an `rtcfile` directive in your _chrony.conf_ file
=== I get `Could not open /dev/rtc, Device or resource busy` in my syslog file
Some other program running on the system may be using the device.
=== What if my computer does not have an RTC or backup battery?
In this case you can still use the `-s` option to set the system clock to the
last modification time of the drift file, which should correspond to the system
time when `chronyd` was previously stopped. The initial system time will be
increasing across reboots and applications started after `chronyd` will not
observe backward steps.
== NTP-specific issues
=== Can `chronyd` be driven from broadcast/multicast NTP servers?
No, the broadcast/multicast client mode is not supported and there is currently
no plan to implement it. While the mode may be useful to simplify configuration
of clients in large networks, it is inherently less accurate and less secure
(even with authentication) than the ordinary client/server mode.
When configuring a large number of clients in a network, it is recommended to
use the `pool` directive with a DNS name which resolves to addresses of
multiple NTP servers. The clients will automatically replace the servers when
they become unreachable, or otherwise unsuitable for synchronisation, with new
servers from the pool.
Even with very modest hardware, an NTP server can serve time to hundreds of
thousands of clients using the ordinary client/server mode.
=== Can `chronyd` transmit broadcast NTP packets?
Yes, the `broadcast` directive can be used to enable the broadcast server mode
to serve time to clients in the network which support the broadcast client mode
(it's not supported in `chronyd`, see the previous question).
=== Can `chronyd` keep the system clock a fixed offset away from real time?
Yes. Starting from version 3.0, an offset can be specified by the `offset`
option for all time sources in the _chrony.conf_ file.
=== What happens if the network connection is dropped without using ``chronyc``'s `offline` command first?
`chronyd` will keep trying to access the sources that it thinks are online, and
it will take longer before new measurements are actually made and the clock is
corrected when the network is connected again. If the sources were set to
offline, `chronyd` would make new measurements immediately after issuing the
`online` command.
Unless the network connection lasts only few minutes (less than the maximum
polling interval), the delay is usually not a problem, and it may be acceptable
to keep all sources online all the time.
== Operating systems
=== Does `chrony` support Windows?
No. The `chronyc` program (the command-line client used for configuring
`chronyd` while it is running) has been successfully built and run under
Cygwin in the past. `chronyd` is not portable, because part of it is
very system-dependent. It needs adapting to work with Windows'
equivalent of the adjtimex() call, and it needs to be made to work as a
service.
=== Are there any plans to support Windows?
We have no plans to do this. Anyone is welcome to pick this work up and
contribute it back to the project.

237
doc/installation.adoc Normal file
View File

@@ -0,0 +1,237 @@
// This file is part of chrony
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Miroslav Lichvar 2009-2016
//
// 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.
= Installation
The software is distributed as source code which has to be compiled. The source
code is supplied in the form of a gzipped tar file, which unpacks to a
subdirectory identifying the name and version of the program.
A C compiler (e.g. `gcc` or `clang`) and GNU Make are needed to build `chrony`.
The following libraries with their development files, and programs, are needed
to enable optional features:
* pkg-config: detection of development libraries
* Nettle, NSS, or LibTomCrypt: secure hash functions (`SECHASH`)
* libcap: dropping root privileges on Linux (`DROPROOT`)
* libseccomp: system call filter on Linux (`SCFILTER`)
* GnuTLS and Nettle: Network Time Security (`NTS`)
* Editline: line editing in `chronyc` (`READLINE`)
* timepps.h header: PPS reference clock
* Asciidoctor: documentation in HTML format
* Bash: test suite
The following programs are needed when building `chrony` from the git
repository instead of a released tar file:
* Asciidoctor: manual pages
* Bison: parser for chronyc settime command
After unpacking the source code, change directory into it, and type
----
./configure
----
This is a shell script that automatically determines the system type. There is
an optional parameter `--prefix`, which indicates the directory tree where the
software should be installed. For example,
----
./configure --prefix=/opt/free
----
will install the `chronyd` daemon into `/opt/free/sbin` and the `chronyc`
control program into `/opt/free/bin`. The default value for the prefix is
`/usr/local`.
The `configure` script assumes you want to use `gcc` as your compiler. If you
want to use a different compiler, you can configure this way:
----
CC=cc ./configure --prefix=/opt/free
----
for Bourne-family shells, or
----
setenv CC cc
setenv CFLAGS -O
./configure --prefix=/opt/free
----
for C-family shells.
If the software cannot (yet) be built on your system, an error message will be
shown. Otherwise, `Makefile` will be generated.
On Linux, if development files for the libcap library are available, `chronyd`
will be built with support for dropping root privileges. On other systems no
extra library is needed. The default user which `chronyd` should run as can be
specified with the `--with-user` option of the `configure` script.
If development files for the POSIX threads library are available, `chronyd`
will be built with support for asynchronous resolving of hostnames specified in
the `server`, `peer`, and `pool` directives. This allows `chronyd` operating as
a server to respond to client requests when resolving a hostname. If you don't
want to enable the support, specify the `--disable-asyncdns` flag to
`configure`.
If development files for the https://www.lysator.liu.se/~nisse/nettle/[Nettle],
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS[NSS], or
http://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available,
`chronyd` will be built with support for other cryptographic hash functions
than MD5, which can be used for NTP authentication with a symmetric key. If you
don't want to enable the support, specify the `--disable-sechash` flag to
`configure`.
If development files for the editline or readline library are available,
`chronyc` will be built with line editing support. If you don't want this,
specify the `--disable-readline` flag to `configure`.
If a `timepps.h` header is available (e.g. from the
http://linuxpps.org[LinuxPPS project]), `chronyd` will be built with PPS API
reference clock driver. If the header is installed in a location that isn't
normally searched by the compiler, you can add it to the searched locations by
setting the `CPPFLAGS` variable to `-I/path/to/timepps`.
The `--help` option can be specified to `configure` to print all options
supported by the script.
Now type
----
make
----
to build the programs.
If you want to build the manual in HTML, type
----
make docs
----
Once the programs have been successfully compiled, they need to be installed in
their target locations. This step normally needs to be performed by the
superuser, and requires the following command to be entered.
----
make install
----
This will install the binaries and man pages.
To install the HTML version of the manual, enter the command
----
make install-docs
----
Now that the software is successfully installed, the next step is to set up a
configuration file. The default location of the file is _/etc/chrony.conf_.
Several examples of configuration with comments are included in the examples
directory. Suppose you want to use public NTP servers from the pool.ntp.org
project as your time reference. A minimal useful configuration file could be
----
pool pool.ntp.org iburst
makestep 1.0 3
rtcsync
----
Then, `chronyd` can be run. For security reasons, it's recommended to create an
unprivileged user for `chronyd` and specify it with the `-u` command-line
option or the `user` directive in the configuration file, or set the default
user with the `--with-user` configure option before building.
== Support for system call filtering
`chronyd` can be built with support for the Linux secure computing (seccomp)
facility. This requires development files for the
https://github.com/seccomp/libseccomp[libseccomp] library and the
`--enable-scfilter` option specified to `configure`. The `-F` option of
`chronyd` will enable a system call filter, which should significantly reduce
the kernel attack surface and possibly prevent kernel exploits from `chronyd`
if it is compromised.
== Support for line editing libraries
`chronyc` can be built with support for line editing, this allows you to use
the cursor keys to replay and edit old commands. Two libraries are supported
which provide such functionality, editline and GNU readline.
Please note that readline since version 6.0 is licensed under GPLv3+ which is
incompatible with chrony's license GPLv2. You should use editline instead if
you don't want to use older readline versions.
The `configure` script will automatically enable the line editing support if
one of the supported libraries is available. If they are both available, the
editline library will be used.
If you don't want to use it (in which case `chronyc` will use a minimal command
line interface), invoke `configure` like this:
----
./configure --disable-readline other-options...
----
If you have editline, readline or ncurses installed in locations that aren't
normally searched by the compiler and linker, you need to use extra options:
`--with-readline-includes=directory_name`::
This defines the name of the directory above the one where `readline.h` is.
`readline.h` is assumed to be in `editline` or `readline` subdirectory of the
named directory.
`--with-readline-library=directory_name`::
This defines the directory containing the `libedit.a` or `libedit.so` file,
or `libreadline.a` or `libreadline.so` file.
`--with-ncurses-library=directory_name`::
This defines the directory containing the `libncurses.a` or `libncurses.so`
file.
== Extra options for package builders
The `configure` and `make` procedures have some extra options that may be
useful if you are building a distribution package for `chrony`.
The `--mandir=DIR` option to `configure` specifies an installation directory
for the man pages. This overrides the `man` subdirectory of the argument to the
`--prefix` option.
----
./configure --prefix=/usr --mandir=/usr/share/man
----
to set both options together.
The final option is the `DESTDIR` option to the `make` command. For example,
you could use the commands
----
./configure --prefix=/usr --mandir=/usr/share/man
make all docs
make install DESTDIR=./tmp
cd tmp
tar cvf - . | gzip -9 > chrony.tar.gz
----
to build a package. When untarred within the root directory, this will install
the files to the intended final locations.

View File

@@ -1,5 +1,6 @@
[Unit]
Description=Wait for chrony to synchronize system clock
Documentation=man:chronyc(1)
After=chronyd.service
Requires=chronyd.service
Before=time-sync.target
@@ -9,7 +10,7 @@ Wants=time-sync.target
Type=oneshot
# Wait up to ~10 minutes for chronyd to synchronize and the remaining
# clock correction to be less than 0.1 seconds
ExecStart=/usr/bin/chronyc waitsync 60 0.1
ExecStart=/usr/bin/chronyc -h 127.0.0.1,::1 waitsync 600 0.1 0.0 1
RemainAfterExit=yes
StandardOutput=null

View File

@@ -4,9 +4,9 @@ pool pool.ntp.org iburst
# Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift
# In first three updates step the system clock instead of slew
# if the adjustment is larger than 10 seconds.
makestep 10 3
# Allow the system clock to be stepped in the first three updates
# if its offset is larger than 1 second.
makestep 1.0 3
# Enable kernel synchronization of the real-time clock (RTC).
rtcsync

View File

@@ -5,33 +5,31 @@ pool pool.ntp.org iburst
# Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift
# In first three updates step the system clock instead of slew
# if the adjustment is larger than 10 seconds.
makestep 10 3
# Allow the system clock to be stepped in the first three updates
# if its offset is larger than 1 second.
makestep 1.0 3
# Enable kernel synchronization of the real-time clock (RTC).
rtcsync
# Allow NTP client access from local network.
#allow 192.168/16
# Enable hardware timestamping on all interfaces that support it.
#hwtimestamp *
# Serve time even if not synchronized to any NTP server.
# Increase the minimum number of selectable sources required to adjust
# the system clock.
#minsources 2
# Allow NTP client access from local network.
#allow 192.168.0.0/16
# Serve time even if not synchronized to a time source.
#local stratum 10
# Specify file containing keys for NTP and command authentication.
keyfile /etc/chrony.keys
# Specify file containing keys for NTP authentication.
#keyfile /etc/chrony.keys
# Specify key number for command authentication.
commandkey 1
# Generate new command key on start if missing.
generatecommandkey
# Disable logging of client accesses.
noclientlog
# Send message to syslog when clock adjustment is larger than 0.5 seconds.
logchange 0.5
# Get TAI-UTC offset and leap seconds from the system tz database.
#leapsectz right/UTC
# Specify directory for log files.
logdir /var/log/chrony

View File

@@ -5,22 +5,6 @@
# want to enable. The more obscure options are not included. Refer
# to the documentation for these.
#
# Copyright 2002 Richard P. Curnow
#
# 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.
#
#
#######################################################################
### COMMENTS
# Any of the following lines are comments (you have a choice of
@@ -49,42 +33,30 @@
! pool pool.ntp.org iburst
# However, for dial-up use you probably want these instead. The word
# 'offline' means that the server is not visible at boot time. Use
# chronyc's 'online' command to tell chronyd that these servers have
# become visible after you go on-line.
! server foo.example.net offline
! server bar.example.net offline
! server baz.example.net offline
! pool pool.ntp.org offline
# You may want to specify NTP 'peers' instead. If you run a network
# with a lot of computers and want several computers running chrony to
# have the 'front-line' interface to the public NTP servers, you can
# 'peer' these machines together to increase robustness.
! peer foo.example.net
# There are other options to the 'server' and 'peer' directives that you
# might want to use. For example, you can ignore measurements whose
# round-trip-time is too large (indicating that the measurement is
# probably useless, because you don't know which way the measurement
# message got held up.) Consult the full documentation for details.
#######################################################################
### AVOIDING POTENTIALLY BOGUS CHANGES TO YOUR CLOCK
#
# To avoid changes being made to your computer's gain/loss compensation
# when the measurement history is too erratic, you might want to enable
# one of the following lines. The first seems good for dial-up (or
# other high-latency connections like slow leased lines), the second
# seems OK for a LAN environment.
# one of the following lines. The first seems good with servers on the
# Internet, the second seems OK for a LAN environment.
! maxupdateskew 100
! maxupdateskew 5
# If you want to increase the minimum number of selectable sources
# required to update the system clock in order to make the
# synchronisation more reliable, uncomment (and edit) the following
# line.
! minsources 2
# If your computer has a good stable clock (e.g. it is not a virtual
# machine), you might also want to reduce the maximum assumed drift
# (frequency error) of the clock (the value is specified in ppm).
! maxdrift 100
#######################################################################
### FILENAMES ETC
# Chrony likes to keep information about your computer's clock in files.
@@ -95,24 +67,10 @@
driftfile /var/lib/chrony/drift
# If you want to use the program called chronyc to configure aspects of
# chronyd's operation once it is running (e.g. tell it the Internet link
# has gone up or down), you need a password. This is stored in the
# following keys file. (You also need keys to support authenticated NTP
# exchanges between cooperating machines.) Again, this option is
# assumed by default.
# If you want to enable NTP authentication with symmetric keys, you will need
# to uncomment the following line and edit the file to set up the keys.
keyfile /etc/chrony.keys
# Tell chronyd which numbered key in the file is used as the password
# for chronyc. (You can pick any integer up to 2**32-1. '1' is just a
# default. Using another value will _NOT_ increase security.)
commandkey 1
# With this directive a random password will be generated automatically.
generatecommandkey
! keyfile /etc/chrony.keys
# chronyd can save the measurement history for the servers to files when
# it it exits. This is useful in 2 situations:
@@ -137,20 +95,26 @@ generatecommandkey
# still running and bail out. If you want to change the path to the PID
# file, uncomment this line and edit it. The default path is shown.
! pidfile /var/run/chronyd.pid
! pidfile /var/run/chrony/chronyd.pid
# If the system timezone database is kept up to date and includes the
# right/UTC timezone, chronyd can use it to determine the current
# TAI-UTC offset and when will the next leap second occur.
! leapsectz right/UTC
#######################################################################
### INITIAL CLOCK CORRECTION
# This option is useful to quickly correct the clock on start if it's
# off by a large amount. The value '10' means that if the error is less
# than 10 seconds, it will be gradually removed by speeding up or
# slowing down your computer's clock until it is correct. If the error
# is above 10 seconds, an immediate time jump will be applied to correct
# it. The value '1' means the step is allowed only on the first update
# of the clock. Some software can get upset if the system clock jumps
# off by a large amount. The value '1.0' means that if the error is less
# than 1 second, it will be gradually removed by speeding up or slowing
# down your computer's clock until it is correct. If the error is above
# 1 second, an immediate time jump will be applied to correct it. The
# value '3' means the step is allowed only in the first three updates of
# the clock. Some software can get upset if the system clock jumps
# (especially backwards), so be careful!
! makestep 10 1
! makestep 1.0 3
#######################################################################
### LOGGING
@@ -211,16 +175,22 @@ generatecommandkey
# machine accesses it. The information can be accessed by the 'clients'
# command of chronyc. You can disable this facility by uncommenting the
# following line. This will save a bit of memory if you have many
# clients.
# clients and it will also disable support for the interleaved mode.
! noclientlog
# The clientlog size is limited to 512KB by default. If you have many
# clients, especially in many different subnets, you might want to
# increase the limit.
# clients, you might want to increase the limit.
! clientloglimit 4194304
# By default, chronyd tries to respond to all valid NTP requests from
# allowed addresses. If you want to limit the response rate for NTP
# clients that are sending requests too frequently, uncomment and edit
# the following line.
! ratelimit interval 3 burst 8
#######################################################################
### REPORTING BIG CLOCK CHANGES
# Perhaps you want to know if chronyd suddenly detects any large error
@@ -247,6 +217,7 @@ generatecommandkey
# By default chronyd binds to the loopback interface. Uncomment the
# following lines to allow receiving command packets from remote hosts.
! bindcmdaddress 0.0.0.0
! bindcmdaddress ::
@@ -262,10 +233,20 @@ generatecommandkey
# syntax and meaning is the same as for 'allow' and 'deny', except that
# 'cmdallow' and 'cmddeny' control access to the chronyd's command port.
# NOTE, even if the host where you run chronyc is granted access, you
# still need a command key set up and you have to know the password to
# put into chronyc to allow you to modify chronyd's parameters. By
# default all you can do is view information about chronyd's operation.
# Rate limiting can be enabled also for command packets. (Note,
# commands from localhost are never limited.)
! cmdratelimit interval -4 burst 16
#######################################################################
### HARDWARE TIMESTAMPING
# On Linux, if the network interface controller and its driver support
# hardware timestamping, it can significantly improve the accuracy of
# synchronisation. It can be enabled on specified interfaces only, or it
# can be enabled on all interfaces that support it.
! hwtimestamp eth0
! hwtimestamp *
#######################################################################
### REAL TIME CLOCK
@@ -296,6 +277,12 @@ generatecommandkey
! rtcdevice /dev/misc/rtc
# Alternatively, if not using the -s option, this directive can be used
# to enable a mode in which the RTC is periodically set to the system
# time, with no tracking of its drift.
! rtcsync
#######################################################################
### REAL TIME SCHEDULER
# This directive tells chronyd to use the real-time FIFO scheduler with the

View File

@@ -1,29 +1,13 @@
#######################################################################
# This is an example chrony keys file. It enables authentication of NTP
# packets with symmetric keys when its location is specified by the keyfile
# directive in chrony.conf(5). It should be readable only by root and the
# user under which chronyd is running.
#
# This is an example chrony keys file. You should copy it to /etc/chrony.keys
# after editing it to set up the key(s) you want to use. It should be readable
# only by root or the user chronyd drops the root privileges to. In most
# situations, you will require a single key (the 'commandkey') so that you can
# supply a password to chronyc to enable you to modify chronyd's operation
# whilst it is running.
#
# Copyright 2002 Richard P. Curnow
#
######################################################################
# Don't use the example keys! It's recommended to generate random keys using
# the chronyc keygen command.
# Examples of valid keys:
#1 ALongAndRandomPassword
#2 MD5 HEX:B028F91EA5C38D06C2E140B26C7F41EC
#3 SHA1 HEX:1DC764E0791B11FA67EFC7ECBC4B0D73F68A070C
# The keys should be random for maximum security. If you wanted to use a key
# with ID 1 as your commandkey (i.e. chronyc password) you would put
# "commandkey 1" into chrony.conf. If no commandkey is present in the keys
# file and the generatecommandkey directive is specified in chrony.conf,
# a random commandkey will be generated and added to the keys file
# automatically on chronyd start.
# You might want to define more keys if you use the authentication facility
# in the network time protocol to authenticate request/response packets between
# trusted clients and servers.
#1 MD5 AVeryLongAndRandomPassword
#2 MD5 HEX:12114855C7931009B4049EF3EFC48A139C3F989F
#3 SHA1 HEX:B2159C05D6A219673A3B7E896B6DE07F6A440995

View File

@@ -3,6 +3,6 @@
nocreate
sharedscripts
postrotate
/usr/bin/chronyc -a cyclelogs > /dev/null 2>&1 || true
/usr/bin/chronyc cyclelogs > /dev/null 2>&1 || true
endscript
}

View File

@@ -1,17 +1,15 @@
#!/bin/sh
# This is a NetworkManager dispatcher script for chronyd to set its NTP sources
# online/offline when a default route is configured/removed on the system.
# This is a NetworkManager dispatcher / networkd-dispatcher script for
# chronyd to set its NTP sources online or offline when a network interface
# is configured or removed
export LC_ALL=C
if [ "$2" = "up" ]; then
/sbin/ip route list dev "$1" | grep -q '^default' &&
/usr/bin/chronyc -a online > /dev/null 2>&1
fi
# For NetworkManager consider only up/down events
[ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
if [ "$2" = "down" ]; then
/sbin/ip route list | grep -q '^default' ||
/usr/bin/chronyc -a offline > /dev/null 2>&1
fi
# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off
chronyc onoffline > /dev/null 2>&1
exit 0

View File

@@ -1,56 +0,0 @@
%global chrony_version @@VERSION@@
%if 0%(echo %{chrony_version} | grep -q pre && echo 1)
%global prerelease %(echo %{chrony_version} | sed 's/.*-//')
%endif
Summary: An NTP client/server
Name: chrony
Version: %(echo %{chrony_version} | sed 's/-.*//')
Release: %{!?prerelease:1}%{?prerelease:0.1.%{prerelease}}
Source: chrony-%{version}%{?prerelease:-%{prerelease}}.tar.gz
License: GPLv2
Group: Applications/Utilities
BuildRoot: %{_tmppath}/%{name}-%{version}-root-%(id -u -n)
Requires: info
%description
chrony is a client and server for the Network Time Protocol (NTP).
This program keeps your computer's clock accurate. It was specially
designed to support systems with intermittent Internet connections,
but it also works well in permanently connected environments. It can
also use hardware reference clocks, the system real-time clock, or
manual input as time references.
%prep
%setup -q -n %{name}-%{version}%{?prerelease:-%{prerelease}}
%build
./configure \
--prefix=%{_prefix} \
--bindir=%{_bindir} \
--sbindir=%{_sbindir} \
--infodir=%{_infodir} \
--mandir=%{_mandir}
make
make chrony.txt
make chrony.info
%install
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
rm -rf $RPM_BUILD_ROOT%{_docdir}
mkdir -p $RPM_BUILD_ROOT%{_infodir}
cp chrony.info* $RPM_BUILD_ROOT%{_infodir}
%files
%{_sbindir}/chronyd
%{_bindir}/chronyc
%{_infodir}/chrony.info*
%{_mandir}/man1/chronyc.1.gz
%{_mandir}/man5/chrony.conf.5.gz
%{_mandir}/man8/chronyd.8.gz
%doc README
%doc chrony.txt
%doc COPYING
%doc examples/chrony.conf.example*
%doc examples/chrony.keys.example

View File

@@ -1,13 +1,18 @@
[Unit]
Description=NTP client/server
Documentation=man:chronyd(8) man:chrony.conf(5)
After=ntpdate.service sntp.service ntpd.service
Conflicts=ntpd.service systemd-timesyncd.service
ConditionCapability=CAP_SYS_TIME
[Service]
Type=forking
PIDFile=/var/run/chronyd.pid
PIDFile=/run/chrony/chronyd.pid
EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd $OPTIONS
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=full
[Install]
WantedBy=multi-user.target

2870
getdate.c

File diff suppressed because it is too large Load Diff

View File

@@ -8,12 +8,7 @@
** This code is in the public domain and has no copyright.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
# ifdef HAVE_ALLOCA_H
# include <alloca.h>
# endif
#endif
#include "config.h"
/* Since the code of getdate.y is not included in the Emacs executable
itself, there is no need to #define static in this file. Even if

View File

@@ -29,6 +29,7 @@
#include "sysincl.h"
#include "hash.h"
#include "memory.h"
#include "util.h"
#include "md5.c"
@@ -49,18 +50,17 @@ 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)
{
if (out_len < 16)
return 0;
MD5Init(&ctx);
MD5Update(&ctx, in1, in1_len);
if (in2)
MD5Update(&ctx, in2, in2_len);
MD5Final(&ctx);
memcpy(out, ctx.digest, 16);
out_len = MIN(out_len, 16);
return 16;
memcpy(out, ctx.digest, out_len);
return out_len;
}
void

119
hash_nettle.c Normal file
View File

@@ -0,0 +1,119 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2018
*
* 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.
*
**********************************************************************
=======================================================================
Routines implementing crypto hashing using the nettle library.
*/
#include "config.h"
#include "sysincl.h"
#include <nettle/nettle-meta.h>
#include "hash.h"
#include "memory.h"
struct hash {
const char *name;
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 }
};
int
HSH_GetHashId(const char *name)
{
int id, nid;
for (id = 0; hashes[id].name; id++) {
if (!strcmp(name, hashes[id].name))
break;
}
if (!hashes[id].name)
return -1;
if (hashes[id].context)
return id;
for (nid = 0; nettle_hashes[nid]; nid++) {
if (!strcmp(hashes[id].int_name, nettle_hashes[nid]->name))
break;
}
if (!nettle_hashes[nid] || !nettle_hashes[nid]->context_size || !nettle_hashes[nid]->init)
return -1;
hashes[id].nettle_hash = nettle_hashes[nid];
hashes[id].context = Malloc(hashes[id].nettle_hash->context_size);
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)
{
const struct nettle_hash *hash;
void *context;
hash = hashes[id].nettle_hash;
context = hashes[id].context;
if (out_len > hash->digest_size)
out_len = hash->digest_size;
hash->init(context);
hash->update(context, in1_len, in1);
if (in2)
hash->update(context, in2_len, in2);
hash->digest(context, out_len, out);
return out_len;
}
void
HSH_Finalise(void)
{
int i;
for (i = 0; hashes[i].name; i++) {
if (hashes[i].context)
Free(hashes[i].context);
}
}

View File

@@ -25,12 +25,14 @@
*/
#include "config.h"
#include <nss.h>
#include <hasht.h>
#include <nsslowhash.h>
/* #include "config.h" */
#include "hash.h"
#include "util.h"
static NSSLOWInitContext *ictx;
@@ -77,13 +79,17 @@ 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)
{
unsigned int ret;
unsigned char buf[MAX_HASH_LENGTH];
unsigned int ret = 0;
NSSLOWHASH_Begin(hashes[id].context);
NSSLOWHASH_Update(hashes[id].context, in1, in1_len);
if (in2)
NSSLOWHASH_Update(hashes[id].context, in2, in2_len);
NSSLOWHASH_End(hashes[id].context, out, &ret, out_len);
NSSLOWHASH_End(hashes[id].context, buf, &ret, sizeof (buf));
ret = MIN(ret, out_len);
memcpy(out, buf, ret);
return ret;
}

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2012
* Copyright (C) Miroslav Lichvar 2012, 2018
*
* 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
@@ -29,6 +29,7 @@
#include "config.h"
#include "hash.h"
#include "util.h"
struct hash {
const char *name;
@@ -38,18 +39,6 @@ struct hash {
static const struct hash hashes[] = {
{ "MD5", "md5", &md5_desc },
#ifdef LTC_RIPEMD128
{ "RMD128", "rmd128", &rmd128_desc },
#endif
#ifdef LTC_RIPEMD160
{ "RMD160", "rmd160", &rmd160_desc },
#endif
#ifdef LTC_RIPEMD256
{ "RMD256", "rmd256", &rmd256_desc },
#endif
#ifdef LTC_RIPEMD320
{ "RMD320", "rmd320", &rmd320_desc },
#endif
#ifdef LTC_SHA1
{ "SHA1", "sha1", &sha1_desc },
#endif
@@ -62,6 +51,12 @@ static const struct hash hashes[] = {
#ifdef LTC_SHA512
{ "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 },
#endif
#ifdef LTC_TIGER
{ "TIGER", "tiger", &tiger_desc },
#endif
@@ -99,19 +94,24 @@ 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)
{
unsigned char buf[MAX_HASH_LENGTH];
unsigned long len;
int r;
len = out_len;
len = sizeof (buf);
if (in2)
r = hash_memory_multi(id, out, &len,
in1, (unsigned long)in1_len, in2, (unsigned long)in2_len, NULL, 0);
r = hash_memory_multi(id, buf, &len,
in1, (unsigned long)in1_len,
in2, (unsigned long)in2_len, NULL, 0);
else
r = hash_memory(id, in1, in1_len, out, &len);
r = hash_memory(id, in1, in1_len, buf, &len);
if (r != CRYPT_OK)
return 0;
len = MIN(len, out_len);
memcpy(out, buf, len);
return len;
}

227
hwclock.c Normal file
View File

@@ -0,0 +1,227 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016-2018
*
* 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.
*
**********************************************************************
=======================================================================
Tracking of hardware clocks (e.g. RTC, PHC)
*/
#include "config.h"
#include "sysincl.h"
#include "array.h"
#include "hwclock.h"
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "regress.h"
#include "util.h"
/* Minimum and maximum number of samples per clock */
#define MIN_SAMPLES 2
#define MAX_SAMPLES 64
/* Maximum acceptable frequency offset of the clock */
#define MAX_FREQ_OFFSET (2.0 / 3.0)
struct HCL_Instance_Record {
/* HW and local reference timestamp */
struct timespec hw_ref;
struct timespec local_ref;
/* Samples stored as intervals (uncorrected for frequency error)
relative to local_ref and hw_ref */
double *x_data;
double *y_data;
/* Minimum, maximum and current number of samples */
int min_samples;
int max_samples;
int n_samples;
/* Maximum error of the last sample */
double last_err;
/* Minimum interval between samples */
double min_separation;
/* Flag indicating the offset and frequency values are valid */
int valid_coefs;
/* Estimated offset and frequency of HW clock relative to local clock */
double offset;
double frequency;
};
/* ================================================== */
static void
handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
HCL_Instance clock;
double delta;
clock = anything;
if (clock->n_samples)
UTI_AdjustTimespec(&clock->local_ref, cooked, &clock->local_ref, &delta, dfreq, doffset);
if (clock->valid_coefs)
clock->frequency /= 1.0 - dfreq;
}
/* ================================================== */
HCL_Instance
HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
{
HCL_Instance clock;
min_samples = CLAMP(MIN_SAMPLES, min_samples, MAX_SAMPLES);
max_samples = CLAMP(MIN_SAMPLES, max_samples, MAX_SAMPLES);
max_samples = MAX(min_samples, max_samples);
clock = MallocNew(struct HCL_Instance_Record);
clock->x_data = MallocArray(double, max_samples);
clock->y_data = MallocArray(double, max_samples);
clock->x_data[max_samples - 1] = 0.0;
clock->y_data[max_samples - 1] = 0.0;
clock->min_samples = min_samples;
clock->max_samples = max_samples;
clock->n_samples = 0;
clock->valid_coefs = 0;
clock->min_separation = min_separation;
LCL_AddParameterChangeHandler(handle_slew, clock);
return clock;
}
/* ================================================== */
void HCL_DestroyInstance(HCL_Instance clock)
{
LCL_RemoveParameterChangeHandler(handle_slew, clock);
Free(clock->y_data);
Free(clock->x_data);
Free(clock);
}
/* ================================================== */
int
HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now)
{
if (!clock->n_samples ||
fabs(UTI_DiffTimespecsToDouble(now, &clock->local_ref)) >= clock->min_separation)
return 1;
return 0;
}
/* ================================================== */
void
HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
struct timespec *local_ts, double err)
{
double hw_delta, local_delta, local_freq, raw_freq;
int i, n_runs, best_start;
local_freq = 1.0 - LCL_ReadAbsoluteFrequency() / 1.0e6;
/* Shift old samples */
if (clock->n_samples) {
if (clock->n_samples >= clock->max_samples)
clock->n_samples--;
hw_delta = UTI_DiffTimespecsToDouble(hw_ts, &clock->hw_ref);
local_delta = UTI_DiffTimespecsToDouble(local_ts, &clock->local_ref) / local_freq;
if (hw_delta <= 0.0 || local_delta < clock->min_separation / 2.0) {
clock->n_samples = 0;
DEBUG_LOG("HW clock reset interval=%f", local_delta);
}
for (i = clock->max_samples - clock->n_samples; i < clock->max_samples; i++) {
clock->y_data[i - 1] = clock->y_data[i] - hw_delta;
clock->x_data[i - 1] = clock->x_data[i] - local_delta;
}
}
clock->n_samples++;
clock->hw_ref = *hw_ts;
clock->local_ref = *local_ts;
clock->last_err = err;
/* Get new coefficients */
clock->valid_coefs =
RGR_FindBestRobustRegression(clock->x_data + clock->max_samples - clock->n_samples,
clock->y_data + clock->max_samples - clock->n_samples,
clock->n_samples, 1.0e-10, &clock->offset, &raw_freq,
&n_runs, &best_start);
if (!clock->valid_coefs) {
DEBUG_LOG("HW clock needs more samples");
return;
}
clock->frequency = raw_freq / local_freq;
/* Drop unneeded samples */
if (clock->n_samples > clock->min_samples)
clock->n_samples -= MIN(best_start, clock->n_samples - clock->min_samples);
/* If the fit doesn't cross the error interval of the last sample,
or the frequency is not sane, drop all samples and start again */
if (fabs(clock->offset) > err ||
fabs(clock->frequency - 1.0) > MAX_FREQ_OFFSET) {
DEBUG_LOG("HW clock reset");
clock->n_samples = 0;
clock->valid_coefs = 0;
}
DEBUG_LOG("HW clock samples=%d offset=%e freq=%e raw_freq=%e err=%e ref_diff=%e",
clock->n_samples, clock->offset, clock->frequency - 1.0, raw_freq - 1.0, err,
UTI_DiffTimespecsToDouble(&clock->hw_ref, &clock->local_ref));
}
/* ================================================== */
int
HCL_CookTime(HCL_Instance clock, struct timespec *raw, struct timespec *cooked, double *err)
{
double offset, elapsed;
if (!clock->valid_coefs)
return 0;
elapsed = UTI_DiffTimespecsToDouble(raw, &clock->hw_ref);
offset = elapsed / clock->frequency - clock->offset;
UTI_AddDoubleToTimespec(&clock->local_ref, offset, cooked);
/* Fow now, just return the error of the last sample */
if (err)
*err = clock->last_err;
return 1;
}

49
hwclock.h Normal file
View File

@@ -0,0 +1,49 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header for tracking of hardware clocks */
#ifndef GOT_HWCLOCK_H
#define GOT_HWCLOCK_H
typedef struct HCL_Instance_Record *HCL_Instance;
/* Create a new HW clock instance */
extern HCL_Instance HCL_CreateInstance(int min_samples, int max_samples,
double min_separation);
/* Destroy a HW clock instance */
extern void HCL_DestroyInstance(HCL_Instance clock);
/* Check if a new sample should be accumulated at this time */
extern int HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now);
/* Accumulate a new sample */
extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
struct timespec *local_ts, double err);
/* Convert raw hardware time to cooked local time */
extern int HCL_CookTime(HCL_Instance clock, struct timespec *raw, struct timespec *cooked,
double *err);
#endif

297
keys.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2012-2014
* Copyright (C) Miroslav Lichvar 2012-2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -32,6 +32,7 @@
#include "array.h"
#include "keys.h"
#include "cmac.h"
#include "cmdparse.h"
#include "conf.h"
#include "memory.h"
@@ -39,93 +40,57 @@
#include "local.h"
#include "logging.h"
/* Consider 80 bits as the absolute minimum for a secure key */
#define MIN_SECURE_KEY_LENGTH 10
typedef enum {
NTP_MAC,
CMAC,
} KeyClass;
typedef struct {
uint32_t id;
char *val;
int len;
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;
static int command_key_valid;
static uint32_t command_key_id;
static int cache_valid;
static uint32_t cache_key_id;
static int cache_key_pos;
/* ================================================== */
static int
generate_key(uint32_t key_id)
{
#ifdef FEAT_SECHASH
unsigned char key[20];
const char *hashname = "SHA1";
#else
unsigned char key[16];
const char *hashname = "MD5";
#endif
const char *key_file, *rand_dev = "/dev/urandom";
FILE *f;
struct stat st;
int i;
key_file = CNF_GetKeysFile();
if (!key_file)
return 0;
f = fopen(rand_dev, "r");
if (!f || fread(key, sizeof (key), 1, f) != 1) {
if (f)
fclose(f);
LOG_FATAL(LOGF_Keys, "Could not read %s", rand_dev);
return 0;
}
fclose(f);
f = fopen(key_file, "a");
if (!f) {
LOG_FATAL(LOGF_Keys, "Could not open keyfile %s for writing", key_file);
return 0;
}
/* Make sure the keyfile is not world-readable */
if (stat(key_file, &st) || chmod(key_file, st.st_mode & 0770)) {
fclose(f);
LOG_FATAL(LOGF_Keys, "Could not change permissions of keyfile %s", key_file);
return 0;
}
fprintf(f, "\n%"PRIu32" %s HEX:", key_id, hashname);
for (i = 0; i < sizeof (key); i++)
fprintf(f, "%02hhX", key[i]);
fprintf(f, "\n");
fclose(f);
/* Erase the key from stack */
memset(key, 0, sizeof (key));
LOG(LOGS_INFO, LOGF_Keys, "Generated key %"PRIu32, key_id);
return 1;
}
/* ================================================== */
static void
free_keys(void)
{
unsigned int i;
Key *key;
for (i = 0; i < ARR_GetSize(keys); i++)
Free(((Key *)ARR_GetElement(keys, i))->val);
for (i = 0; i < ARR_GetSize(keys); i++) {
key = ARR_GetElement(keys, i);
switch (key->class) {
case NTP_MAC:
Free(key->data.ntp_mac.value);
break;
case CMAC:
CMC_DestroyInstance(key->data.cmac);
break;
default:
assert(0);
}
}
ARR_SetSize(keys, 0);
command_key_valid = 0;
cache_valid = 0;
}
@@ -135,14 +100,8 @@ void
KEY_Initialise(void)
{
keys = ARR_CreateInstance(sizeof (Key));
command_key_valid = 0;
cache_valid = 0;
KEY_Reload();
if (CNF_GetGenerateCommandKey() && !KEY_KeyKnown(KEY_GetCommandKey())) {
if (generate_key(KEY_GetCommandKey()))
KEY_Reload();
}
}
/* ================================================== */
@@ -168,29 +127,49 @@ static int
determine_hash_delay(uint32_t key_id)
{
NTP_Packet pkt;
struct timeval before, after;
unsigned long usecs, min_usecs=0;
int i;
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_NORMAL_PACKET_LENGTH,
(unsigned char *)&pkt.auth_data, sizeof (pkt.auth_data));
KEY_GenerateAuth(key_id, (unsigned char *)&pkt, NTP_HEADER_LENGTH,
(unsigned char *)&pkt + NTP_HEADER_LENGTH,
sizeof (pkt) - NTP_HEADER_LENGTH);
LCL_ReadRawTime(&after);
usecs = (after.tv_sec - before.tv_sec) * 1000000 + (after.tv_usec - before.tv_usec);
diff = UTI_DiffTimespecsToDouble(&after, &before);
if (i == 0 || usecs < min_usecs) {
min_usecs = usecs;
}
if (i == 0 || min_diff > diff)
min_diff = diff;
}
/* Add on a bit extra to allow for copying, conversions etc */
min_usecs += min_usecs >> 4;
nsecs = 1.0e9 * min_diff;
DEBUG_LOG(LOGF_Keys, "authentication delay for key %"PRIu32": %ld useconds", key_id, min_usecs);
DEBUG_LOG("authentication delay for key %"PRIu32": %d nsecs", key_id, nsecs);
return min_usecs;
return nsecs;
}
/* ================================================== */
/* Decode key encoded in ASCII or HEX */
static int
decode_key(char *key)
{
int len = strlen(key);
if (!strncmp(key, "ASCII:", 6)) {
memmove(key, key + 6, len - 6);
return len - 6;
} else if (!strncmp(key, "HEX:", 4)) {
return UTI_HexToBytes(key + 4, key, len);
} else {
/* assume ASCII */
return len;
}
}
/* ================================================== */
@@ -218,11 +197,11 @@ compare_keys_by_id(const void *a, const void *b)
void
KEY_Reload(void)
{
unsigned int i, line_number;
unsigned int i, line_number, key_length, cmac_key_length;
FILE *in;
uint32_t key_id;
char line[2048], *keyval, *key_file;
const char *hashname;
char line[2048], *key_file, *key_value;
const char *key_type;
int hash_id;
Key key;
free_keys();
@@ -233,9 +212,9 @@ KEY_Reload(void)
if (!key_file)
return;
in = fopen(key_file, "r");
in = UTI_OpenFile(NULL, key_file, NULL, 'r', 0);
if (!in) {
LOG(LOGS_WARN, LOGF_Keys, "Could not open keyfile %s", key_file);
LOG(LOGS_WARN, "Could not open keyfile %s", key_file);
return;
}
@@ -246,26 +225,43 @@ KEY_Reload(void)
if (!*line)
continue;
if (!CPS_ParseKey(line, &key_id, &hashname, &keyval)) {
LOG(LOGS_WARN, LOGF_Keys, "Could not parse key at line %d in file %s", line_number, key_file);
memset(&key, 0, sizeof (key));
if (!CPS_ParseKey(line, &key.id, &key_type, &key_value)) {
LOG(LOGS_WARN, "Could not parse key at line %u in file %s", line_number, key_file);
continue;
}
key.hash_id = HSH_GetHashId(hashname);
if (key.hash_id < 0) {
LOG(LOGS_WARN, LOGF_Keys, "Unknown hash function in key %"PRIu32, key_id);
key_length = decode_key(key_value);
if (key_length == 0) {
LOG(LOGS_WARN, "Could not decode key %"PRIu32, key.id);
continue;
}
key.len = UTI_DecodePasswordFromText(keyval);
if (!key.len) {
LOG(LOGS_WARN, LOGF_Keys, "Could not decode password in key %"PRIu32, key_id);
hash_id = HSH_GetHashId(key_type);
cmac_key_length = CMC_GetKeyLength(key_type);
if (hash_id >= 0) {
key.class = NTP_MAC;
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) {
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);
assert(key.data.cmac);
} else {
LOG(LOGS_WARN, "Unknown hash function or cipher in key %"PRIu32, key.id);
continue;
}
key.id = key_id;
key.val = MallocArray(char, key.len);
memcpy(key.val, keyval, key.len);
ARR_AppendElement(keys, &key);
}
@@ -279,7 +275,7 @@ KEY_Reload(void)
/* Check for duplicates */
for (i = 1; i < ARR_GetSize(keys); i++) {
if (get_key(i - 1)->id == get_key(i)->id)
LOG(LOGS_WARN, LOGF_Keys, "Detected duplicate key %"PRIu32, get_key(i - 1)->id);
LOG(LOGS_WARN, "Detected duplicate key %"PRIu32, get_key(i - 1)->id);
}
/* Erase any passwords from stack */
@@ -334,18 +330,6 @@ get_key_by_id(uint32_t key_id)
/* ================================================== */
uint32_t
KEY_GetCommandKey(void)
{
if (!command_key_valid) {
command_key_id = CNF_GetCommandKey();
}
return command_key_id;
}
/* ================================================== */
int
KEY_KeyKnown(uint32_t key_id)
{
@@ -369,6 +353,81 @@ KEY_GetAuthDelay(uint32_t key_id)
/* ================================================== */
int
KEY_GetAuthLength(uint32_t key_id)
{
unsigned char buf[MAX_HASH_LENGTH];
Key *key;
key = get_key_by_id(key_id);
if (!key)
return 0;
switch (key->class) {
case NTP_MAC:
return HSH_Hash(key->data.ntp_mac.hash_id, buf, 0, buf, 0, buf, sizeof (buf));
case CMAC:
return CMC_Hash(key->data.cmac, buf, 0, buf, sizeof (buf));
default:
assert(0);
return 0;
}
}
/* ================================================== */
int
KEY_CheckKeyLength(uint32_t key_id)
{
Key *key;
key = get_key_by_id(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;
}
}
/* ================================================== */
static int
generate_auth(Key *key, const unsigned char *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);
case CMAC:
return CMC_Hash(key->data.cmac, data, data_len, auth, auth_len);
default:
return 0;
}
}
/* ================================================== */
static int
check_auth(Key *key, const unsigned char *data, int data_len,
const unsigned char *auth, int auth_len, int trunc_len)
{
unsigned char buf[MAX_HASH_LENGTH];
int hash_len;
hash_len = generate_auth(key, data, data_len, buf, sizeof (buf));
return MIN(hash_len, trunc_len) == auth_len && !memcmp(buf, auth, auth_len);
}
/* ================================================== */
int
KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
unsigned char *auth, int auth_len)
@@ -380,15 +439,14 @@ KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
if (!key)
return 0;
return UTI_GenerateNTPAuth(key->hash_id, (unsigned char *)key->val,
key->len, data, data_len, auth, auth_len);
return generate_auth(key, data, data_len, auth, auth_len);
}
/* ================================================== */
int
KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len,
const unsigned char *auth, int auth_len)
const unsigned char *auth, int auth_len, int trunc_len)
{
Key *key;
@@ -397,6 +455,5 @@ KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len,
if (!key)
return 0;
return UTI_CheckNTPAuth(key->hash_id, (unsigned char *)key->val,
key->len, data, data_len, auth, auth_len);
return check_auth(key, data, data_len, auth, auth_len, trunc_len);
}

8
keys.h
View File

@@ -34,15 +34,15 @@ extern void KEY_Finalise(void);
extern void KEY_Reload(void);
extern uint32_t KEY_GetCommandKey(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_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, const unsigned char *auth, int auth_len);
extern int KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len,
const unsigned char *auth, int auth_len, int trunc_len);
#endif /* GOT_KEYS_H */

121
local.c
View File

@@ -42,13 +42,12 @@
/* ================================================== */
/* Maximum allowed frequency offset in ppm, the time must not stop
or run backwards */
#define MAX_FREQ 500000.0
/* Variable to store the current frequency, in ppm */
static double current_freq_ppm;
/* Maximum allowed frequency, in ppm */
static double max_freq_ppm;
/* Temperature compensation, in ppm */
static double temp_comp_ppm;
@@ -107,40 +106,47 @@ static double max_clock_error;
under 1s of busy waiting. */
#define NITERS 100
#define NSEC_PER_SEC 1000000000
static void
calculate_sys_precision(void)
{
struct timeval tv, old_tv;
int dusec, best_dusec;
int iters;
struct timespec ts, old_ts;
int iters, diff, best;
gettimeofday(&old_tv, NULL);
best_dusec = 1000000; /* Assume we must be better than a second */
LCL_ReadRawTime(&old_ts);
/* Assume we must be better than a second */
best = NSEC_PER_SEC;
iters = 0;
do {
gettimeofday(&tv, NULL);
dusec = 1000000*(tv.tv_sec - old_tv.tv_sec) + (tv.tv_usec - old_tv.tv_usec);
old_tv = tv;
if (dusec > 0) {
if (dusec < best_dusec) {
best_dusec = dusec;
}
LCL_ReadRawTime(&ts);
diff = NSEC_PER_SEC * (ts.tv_sec - old_ts.tv_sec) + (ts.tv_nsec - old_ts.tv_nsec);
old_ts = ts;
if (diff > 0) {
if (diff < best)
best = diff;
iters++;
}
} while (iters < NITERS);
assert(best_dusec > 0);
assert(best > 0);
precision_quantum = best_dusec * 1.0e-6;
precision_quantum = 1.0e-9 * best;
/* Get rounded log2 value of the measured precision */
precision_log = 0;
while (best_dusec < 707107) {
while (best < 707106781) {
precision_log--;
best_dusec *= 2;
best *= 2;
}
DEBUG_LOG(LOGF_Local, "Clock precision %.9f (%d)", precision_quantum, precision_log);
assert(precision_log >= -30);
DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log);
}
/* ================================================== */
@@ -166,6 +172,11 @@ LCL_Initialise(void)
calculate_sys_precision();
/* This is the maximum allowed frequency offset in ppm, the time must
never stop or run backwards */
max_freq_ppm = CNF_GetMaxDrift();
max_freq_ppm = CLAMP(0.0, max_freq_ppm, 500000.0);
max_clock_error = CNF_GetMaxClockError() * 1e-6;
}
@@ -274,7 +285,7 @@ LCL_IsFirstParameterChangeHandler(LCL_ParameterChangeHandler handler)
/* ================================================== */
static void
invoke_parameter_change_handlers(struct timeval *raw, struct timeval *cooked,
invoke_parameter_change_handlers(struct timespec *raw, struct timespec *cooked,
double dfreq, double doffset,
LCL_ChangeType change_type)
{
@@ -341,23 +352,29 @@ void LCL_RemoveDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void
}
/* ================================================== */
/* At the moment, this is just gettimeofday(), because
I can't think of a Unix system where it would not be */
void
LCL_ReadRawTime(struct timeval *result)
LCL_ReadRawTime(struct timespec *ts)
{
if (gettimeofday(result, NULL) < 0) {
LOG_FATAL(LOGF_Local, "gettimeofday() failed");
}
#if HAVE_CLOCK_GETTIME
if (clock_gettime(CLOCK_REALTIME, ts) < 0)
LOG_FATAL("clock_gettime() failed : %s", strerror(errno));
#else
struct timeval tv;
if (gettimeofday(&tv, NULL) < 0)
LOG_FATAL("gettimeofday() failed : %s", strerror(errno));
UTI_TimevalToTimespec(&tv, ts);
#endif
}
/* ================================================== */
void
LCL_ReadCookedTime(struct timeval *result, double *err)
LCL_ReadCookedTime(struct timespec *result, double *err)
{
struct timeval raw;
struct timespec raw;
LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, result, err);
@@ -366,18 +383,18 @@ LCL_ReadCookedTime(struct timeval *result, double *err)
/* ================================================== */
void
LCL_CookTime(struct timeval *raw, struct timeval *cooked, double *err)
LCL_CookTime(struct timespec *raw, struct timespec *cooked, double *err)
{
double correction;
LCL_GetOffsetCorrection(raw, &correction, err);
UTI_AddDoubleToTimeval(raw, correction, cooked);
UTI_AddDoubleToTimespec(raw, correction, cooked);
}
/* ================================================== */
void
LCL_GetOffsetCorrection(struct timeval *raw, double *correction, double *err)
LCL_GetOffsetCorrection(struct timespec *raw, double *correction, double *err)
{
/* Call system specific driver to get correction */
(*drv_offset_convert)(raw, correction, err);
@@ -406,24 +423,24 @@ LCL_ReadAbsoluteFrequency(void)
static double
clamp_freq(double freq)
{
if (freq <= MAX_FREQ && freq >= -MAX_FREQ)
if (freq <= max_freq_ppm && freq >= -max_freq_ppm)
return freq;
LOG(LOGS_WARN, LOGF_Local, "Frequency %.1f ppm exceeds allowed maximum", freq);
LOG(LOGS_WARN, "Frequency %.1f ppm exceeds allowed maximum", freq);
return freq >= MAX_FREQ ? MAX_FREQ : -MAX_FREQ;
return CLAMP(-max_freq_ppm, freq, max_freq_ppm);
}
/* ================================================== */
static int
check_offset(struct timeval *now, double offset)
check_offset(struct timespec *now, double offset)
{
/* Check if the time will be still sane with accumulated offset */
if (UTI_IsTimeOffsetSane(now, -offset))
return 1;
LOG(LOGS_WARN, LOGF_Local, "Adjustment of %.1f seconds is invalid", -offset);
LOG(LOGS_WARN, "Adjustment of %.1f seconds is invalid", -offset);
return 0;
}
@@ -435,7 +452,7 @@ check_offset(struct timeval *now, double offset)
void
LCL_SetAbsoluteFrequency(double afreq_ppm)
{
struct timeval raw, cooked;
struct timespec raw, cooked;
double dfreq;
afreq_ppm = clamp_freq(afreq_ppm);
@@ -466,7 +483,7 @@ LCL_SetAbsoluteFrequency(double afreq_ppm)
void
LCL_AccumulateDeltaFrequency(double dfreq)
{
struct timeval raw, cooked;
struct timespec raw, cooked;
double old_freq_ppm;
old_freq_ppm = current_freq_ppm;
@@ -495,7 +512,7 @@ LCL_AccumulateDeltaFrequency(double dfreq)
void
LCL_AccumulateOffset(double offset, double corr_rate)
{
struct timeval raw, cooked;
struct timespec raw, cooked;
/* In this case, the cooked time to be passed to the notify clients
has to be the cooked time BEFORE the change was made */
@@ -517,7 +534,7 @@ LCL_AccumulateOffset(double offset, double corr_rate)
int
LCL_ApplyStepOffset(double offset)
{
struct timeval raw, cooked;
struct timespec raw, cooked;
/* In this case, the cooked time to be passed to the notify clients
has to be the cooked time BEFORE the change was made */
@@ -529,7 +546,7 @@ LCL_ApplyStepOffset(double offset)
return 0;
if (!(*drv_apply_step_offset)(offset)) {
LOG(LOGS_ERR, LOGF_Local, "Could not step clock");
LOG(LOGS_ERR, "Could not step system clock");
return 0;
}
@@ -545,7 +562,7 @@ LCL_ApplyStepOffset(double offset)
/* ================================================== */
void
LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
double offset, double dispersion)
{
/* Dispatch to all handlers */
@@ -559,7 +576,7 @@ LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
void
LCL_NotifyLeap(int leap)
{
struct timeval raw, cooked;
struct timespec raw, cooked;
LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, &cooked, NULL);
@@ -576,7 +593,7 @@ LCL_NotifyLeap(int leap)
void
LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
{
struct timeval raw, cooked;
struct timespec raw, cooked;
double old_freq_ppm;
LCL_ReadRawTime(&raw);
@@ -596,7 +613,7 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
current_freq_ppm = clamp_freq(current_freq_ppm);
DEBUG_LOG(LOGF_Local, "old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
DEBUG_LOG("old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
old_freq_ppm, current_freq_ppm, doffset);
/* Call the system-specific driver for setting the frequency */
@@ -643,7 +660,7 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
current_freq_ppm = (*drv_read_freq)();
DEBUG_LOG(LOGF_Local, "Local freq=%.3fppm", current_freq_ppm);
DEBUG_LOG("Local freq=%.3fppm", current_freq_ppm);
}
/* ================================================== */
@@ -653,7 +670,7 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
int
LCL_MakeStep(void)
{
struct timeval raw;
struct timespec raw;
double correction;
LCL_ReadRawTime(&raw);
@@ -667,7 +684,7 @@ LCL_MakeStep(void)
if (!LCL_ApplyStepOffset(-correction))
return 0;
LOG(LOGS_WARN, LOGF_Local, "System clock was stepped by %.6f seconds", correction);
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", correction);
return 1;
}
@@ -683,10 +700,10 @@ LCL_CanSystemLeap(void)
/* ================================================== */
void
LCL_SetSystemLeap(int leap)
LCL_SetSystemLeap(int leap, int tai_offset)
{
if (drv_set_leap) {
(drv_set_leap)(leap);
(drv_set_leap)(leap, tai_offset);
}
}

24
local.h
View File

@@ -31,9 +31,8 @@
#include "sysincl.h"
/* Read the system clock. This is analogous to gettimeofday(),
but with the timezone information ignored */
extern void LCL_ReadRawTime(struct timeval *);
/* Read the system clock */
extern void LCL_ReadRawTime(struct timespec *ts);
/* Read the system clock, corrected according to all accumulated
drifts and uncompensated offsets.
@@ -44,15 +43,15 @@ extern void LCL_ReadRawTime(struct timeval *);
adjtime()-like interface to correct offsets, and to adjust the
frequency), we must correct the raw time to get this value */
extern void LCL_ReadCookedTime(struct timeval *t, double *err);
extern void LCL_ReadCookedTime(struct timespec *ts, double *err);
/* Convert raw time to cooked. */
extern void LCL_CookTime(struct timeval *raw, struct timeval *cooked, double *err);
extern void LCL_CookTime(struct timespec *raw, struct timespec *cooked, double *err);
/* Read the current offset between the system clock and true time
(i.e. 'cooked' - 'raw') (in seconds). */
extern void LCL_GetOffsetCorrection(struct timeval *raw, double *correction, double *err);
extern void LCL_GetOffsetCorrection(struct timespec *raw, double *correction, double *err);
/* Type of routines that may be invoked as callbacks when there is a
change to the frequency or offset.
@@ -79,7 +78,7 @@ typedef enum {
} LCL_ChangeType;
typedef void (*LCL_ParameterChangeHandler)
(struct timeval *raw, struct timeval *cooked,
(struct timespec *raw, struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -163,7 +162,7 @@ extern int LCL_ApplyStepOffset(double offset);
/* Routine to invoke notify handlers on an unexpected time jump
in system clock */
extern void LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
extern void LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
double offset, double dispersion);
/* Routine to invoke notify handlers on leap second when the system clock
@@ -202,10 +201,11 @@ extern int LCL_MakeStep(void);
does something */
extern int LCL_CanSystemLeap(void);
/* Routine to set the system clock to correct itself for a leap second if
supported. Leap second will be inserted at the end of the day if the
argument is positive, deleted if negative, and zero resets the setting. */
extern void LCL_SetSystemLeap(int leap);
/* Routine to set the system clock to correct itself for a leap second and also
set its TAI-UTC offset. If supported, leap second will be inserted at the
end of the day if the argument is positive, deleted if negative, and zero
resets the setting. */
extern void LCL_SetSystemLeap(int leap, int tai_offset);
/* Routine to set a frequency correction (in ppm) that should be applied
to local clock to compensate for temperature changes. A positive

View File

@@ -52,10 +52,10 @@ typedef int (*lcl_ApplyStepOffsetDriver)(double offset);
/* System driver to convert a raw time to an adjusted (cooked) time.
The number of seconds returned in 'corr' have to be added to the
raw time to get the corrected time */
typedef void (*lcl_OffsetCorrectionDriver)(struct timeval *raw, double *corr, double *err);
typedef void (*lcl_OffsetCorrectionDriver)(struct timespec *raw, double *corr, double *err);
/* System driver to schedule leap second */
typedef void (*lcl_SetLeapDriver)(int leap);
/* System driver to schedule leap seconds and set TAI-UTC offset */
typedef void (*lcl_SetLeapDriver)(int leap, int tai_offset);
/* System driver to set the synchronisation status */
typedef void (*lcl_SetSyncStatusDriver)(int synchronised, double est_error, double max_error);

149
logging.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2014
* Copyright (C) Miroslav Lichvar 2011-2014, 2018
*
* 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
@@ -29,30 +29,24 @@
#include "sysincl.h"
#include <syslog.h>
#include "conf.h"
#include "logging.h"
#include "mkdirpp.h"
#include "util.h"
/* This is used by DEBUG_LOG macro */
int log_debug_enabled = 0;
LOG_Severity log_min_severity = LOGS_INFO;
/* ================================================== */
/* Flag indicating we have initialised */
static int initialised = 0;
static FILE *file_log = NULL;
static int system_log = 0;
static int parent_fd = 0;
#define DEBUG_LEVEL_PRINT_FUNCTION 2
#define DEBUG_LEVEL_PRINT_DEBUG 2
static int debug_level = 0;
#ifdef WINNT
static FILE *logfile;
#endif
struct LogFile {
const char *name;
const char *banner;
@@ -74,10 +68,7 @@ void
LOG_Initialise(void)
{
initialised = 1;
#ifdef WINNT
logfile = fopen("./chronyd.err", "a");
#endif
LOG_OpenFileLog(NULL);
}
/* ================================================== */
@@ -86,15 +77,11 @@ LOG_Initialise(void)
void
LOG_Finalise(void)
{
#ifdef WINNT
if (logfile) {
fclose(logfile);
}
#else
if (system_log) {
if (system_log)
closelog();
}
#endif
if (file_log)
fclose(file_log);
LOG_CycleLogFiles();
@@ -105,11 +92,6 @@ LOG_Finalise(void)
static void log_message(int fatal, LOG_Severity severity, const char *message)
{
#ifdef WINNT
if (logfile) {
fprintf(logfile, fatal ? "Fatal error : %s\n" : "%s\n", message);
}
#else
if (system_log) {
int priority;
switch (severity) {
@@ -132,35 +114,37 @@ static void log_message(int fatal, LOG_Severity severity, const char *message)
assert(0);
}
syslog(priority, fatal ? "Fatal error : %s" : "%s", message);
} else {
fprintf(stderr, fatal ? "Fatal error : %s\n" : "%s\n", message);
} else if (file_log) {
fprintf(file_log, fatal ? "Fatal error : %s\n" : "%s\n", message);
}
#endif
}
/* ================================================== */
void LOG_Message(LOG_Severity severity, LOG_Facility facility,
int line_number, const char *filename,
const char *function_name, const char *format, ...)
void LOG_Message(LOG_Severity severity,
#if DEBUG > 0
int line_number, const char *filename, const char *function_name,
#endif
const char *format, ...)
{
char buf[2048];
va_list other_args;
time_t t;
struct tm stm;
struct tm *tm;
#ifdef WINNT
#else
if (!system_log) {
if (!system_log && file_log && severity >= log_min_severity) {
/* Don't clutter up syslog with timestamps and internal debugging info */
time(&t);
stm = *gmtime(&t);
strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &stm);
fprintf(stderr, "%s ", buf);
if (debug_level >= DEBUG_LEVEL_PRINT_FUNCTION)
fprintf(stderr, "%s:%d:(%s) ", filename, line_number, function_name);
tm = gmtime(&t);
if (tm) {
strftime(buf, sizeof (buf), "%Y-%m-%dT%H:%M:%SZ", tm);
fprintf(file_log, "%s ", buf);
}
#if DEBUG > 0
if (log_min_severity <= LOGS_DEBUG)
fprintf(file_log, "%s:%d:(%s) ", filename, line_number, function_name);
#endif
}
va_start(other_args, format);
vsnprintf(buf, sizeof(buf), format, other_args);
@@ -171,48 +155,67 @@ void LOG_Message(LOG_Severity severity, LOG_Facility facility,
case LOGS_INFO:
case LOGS_WARN:
case LOGS_ERR:
if (severity >= log_min_severity)
log_message(0, severity, buf);
break;
case LOGS_FATAL:
if (severity >= log_min_severity)
log_message(1, severity, buf);
/* With syslog, send the message also to the grandparent
process or write it to stderr if not detached */
if (system_log) {
/* Send the message also to the foreground process if it is
still running, or stderr if it is still open */
if (parent_fd > 0) {
if (write(parent_fd, buf, strlen(buf) + 1) < 0)
; /* Not much we can do here */
} else if (parent_fd == 0) {
} else if (system_log && parent_fd == 0) {
system_log = 0;
log_message(1, severity, buf);
}
}
exit(1);
break;
default:
assert(0);
}
}
/* ================================================== */
void
LOG_OpenFileLog(const char *log_file)
{
FILE *f;
if (log_file) {
f = UTI_OpenFile(NULL, log_file, NULL, 'A', 0640);
} else {
f = stderr;
}
/* Enable line buffering */
setvbuf(f, NULL, _IOLBF, BUFSIZ);
if (file_log && file_log != stderr)
fclose(file_log);
file_log = f;
}
/* ================================================== */
void
LOG_OpenSystemLog(void)
{
#ifdef WINNT
#else
system_log = 1;
openlog("chronyd", LOG_PID, LOG_DAEMON);
#endif
}
/* ================================================== */
void LOG_SetDebugLevel(int level)
void LOG_SetMinSeverity(LOG_Severity severity)
{
debug_level = level;
if (level >= DEBUG_LEVEL_PRINT_DEBUG) {
log_debug_enabled = 1;
}
/* Don't print any debug messages in a non-debug build */
log_min_severity = CLAMP(DEBUG > 0 ? LOGS_DEBUG : LOGS_INFO, severity, LOGS_FATAL);
}
/* ================================================== */
@@ -221,6 +224,8 @@ void
LOG_SetParentFd(int fd)
{
parent_fd = fd;
if (file_log == stderr)
file_log = NULL;
}
/* ================================================== */
@@ -260,18 +265,20 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
return;
if (!logfiles[id].file) {
char filename[512];
char *logdir = CNF_GetLogDir();
if (snprintf(filename, sizeof(filename), "%s/%s.log",
CNF_GetLogDir(), logfiles[id].name) >= sizeof(filename) ||
!(logfiles[id].file = fopen(filename, "a"))) {
LOG(LOGS_WARN, LOGF_Refclock, "Couldn't open logfile %s for update", filename);
if (logdir[0] == '\0') {
LOG(LOGS_WARN, "logdir not specified");
logfiles[id].name = NULL;
return;
}
/* Close on exec */
UTI_FdSetCloexec(fileno(logfiles[id].file));
logfiles[id].file = UTI_OpenFile(logdir, logfiles[id].name, ".log", 'a', 0644);
if (!logfiles[id].file) {
/* Disable the log */
logfiles[id].name = NULL;
return;
}
}
banner = CNF_GetLogBanner();
@@ -279,7 +286,7 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
char bannerline[256];
int i, bannerlen;
bannerlen = strlen(logfiles[id].banner);
bannerlen = MIN(strlen(logfiles[id].banner), sizeof (bannerline) - 1);
for (i = 0; i < bannerlen; i++)
bannerline[i] = '=';
@@ -300,20 +307,6 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
/* ================================================== */
void
LOG_CreateLogFileDir(void)
{
const char *logdir;
logdir = CNF_GetLogDir();
if (!mkdir_and_parents(logdir)) {
LOG(LOGS_ERR, LOGF_Logging, "Could not create directory %s", logdir);
}
}
/* ================================================== */
void
LOG_CycleLogFiles(void)
{

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2002
* Copyright (C) Miroslav Lichvar 2013-2014
* Copyright (C) Miroslav Lichvar 2013-2015
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -31,9 +31,6 @@
#include "sysincl.h"
/* Flag indicating whether debug messages are logged */
extern int log_debug_enabled;
/* Line logging macros. If the compiler is GNU C, we take advantage of
being able to get the function name also. */
@@ -45,64 +42,39 @@ extern int log_debug_enabled;
#define FORMAT_ATTRIBUTE_PRINTF(str, first)
#endif
#define DEBUG_LOG(facility, ...) \
#if DEBUG > 0
#define LOG_MESSAGE(severity, ...) \
LOG_Message(severity, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__)
#else
#define LOG_MESSAGE(severity, ...) \
LOG_Message(severity, __VA_ARGS__)
#endif
#define DEBUG_LOG(...) \
do { \
if (DEBUG && log_debug_enabled) \
LOG_Message(LOGS_DEBUG, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__); \
if (DEBUG && log_min_severity == LOGS_DEBUG) \
LOG_MESSAGE(LOGS_DEBUG, __VA_ARGS__); \
} while (0)
#define LOG(severity, facility, ...) LOG_Message(severity, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__)
#define LOG_FATAL(facility, ...) \
#define LOG_FATAL(...) \
do { \
LOG_Message(LOGS_FATAL, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__); \
LOG_MESSAGE(LOGS_FATAL, __VA_ARGS__); \
exit(1); \
} while (0)
#define LOG(severity, ...) LOG_MESSAGE(severity, __VA_ARGS__)
/* Definition of severity */
typedef enum {
LOGS_INFO,
LOGS_DEBUG = -1,
LOGS_INFO = 0,
LOGS_WARN,
LOGS_ERR,
LOGS_FATAL,
LOGS_DEBUG
} LOG_Severity;
/* Definition of facility. Each message is tagged with who generated
it, so that the user can customise what level of reporting he gets
for each area of the software */
typedef enum {
LOGF_Reference,
LOGF_NtpIO,
LOGF_NtpCore,
LOGF_NtpSources,
LOGF_Scheduler,
LOGF_SourceStats,
LOGF_Sources,
LOGF_Local,
LOGF_Util,
LOGF_Main,
LOGF_Memory,
LOGF_ClientLog,
LOGF_Configure,
LOGF_CmdMon,
LOGF_Acquire,
LOGF_Manual,
LOGF_Keys,
LOGF_Logging,
LOGF_Nameserv,
LOGF_Rtc,
LOGF_Regress,
LOGF_Sys,
LOGF_SysGeneric,
LOGF_SysLinux,
LOGF_SysNetBSD,
LOGF_SysSolaris,
LOGF_SysSunOS,
LOGF_SysWinnt,
LOGF_TempComp,
LOGF_RtcLinux,
LOGF_Refclock,
LOGF_Smooth,
} LOG_Facility;
/* Minimum severity of messages to be logged */
extern LOG_Severity log_min_severity;
/* Init function */
extern void LOG_Initialise(void);
@@ -111,22 +83,27 @@ extern void LOG_Initialise(void);
extern void LOG_Finalise(void);
/* Line logging function */
FORMAT_ATTRIBUTE_PRINTF(6, 7)
extern void LOG_Message(LOG_Severity severity, LOG_Facility facility,
int line_number, const char *filename,
#if DEBUG > 0
FORMAT_ATTRIBUTE_PRINTF(5, 6)
extern void LOG_Message(LOG_Severity severity, int line_number, const char *filename,
const char *function_name, const char *format, ...);
#else
FORMAT_ATTRIBUTE_PRINTF(2, 3)
extern void LOG_Message(LOG_Severity severity, const char *format, ...);
#endif
/* Set debug level:
0, 1 - only non-debug messages are logged
2 - debug messages are logged too, all messages are prefixed with
filename, line, and function name
*/
extern void LOG_SetDebugLevel(int level);
/* Set the minimum severity of a message to be logged or printed to terminal.
If the severity is LOGS_DEBUG and DEBUG is enabled, all messages will be
prefixed with the filename, line number, and function name. */
extern void LOG_SetMinSeverity(LOG_Severity severity);
/* Log messages to a file instead of stderr, or stderr again if NULL */
extern void LOG_OpenFileLog(const char *log_file);
/* Log messages to syslog instead of stderr */
extern void LOG_OpenSystemLog(void);
/* Send fatal message also to the foreground process */
/* Stop using stderr and send fatal message to the foreground process */
extern void LOG_SetParentFd(int fd);
/* Close the pipe to the foreground process so it can exit */
@@ -141,7 +118,6 @@ extern LOG_FileID LOG_FileOpen(const char *name, const char *banner);
FORMAT_ATTRIBUTE_PRINTF(2, 3)
extern void LOG_FileWrite(LOG_FileID id, const char *format, ...);
extern void LOG_CreateLogFileDir(void);
extern void LOG_CycleLogFiles(void);
#endif /* GOT_LOGGING_H */

392
main.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009
* Copyright (C) Miroslav Lichvar 2012-2014
* Copyright (C) Miroslav Lichvar 2012-2018
*
* 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
@@ -35,8 +35,13 @@
#include "local.h"
#include "sys.h"
#include "ntp_io.h"
#include "ntp_signd.h"
#include "ntp_sources.h"
#include "ntp_core.h"
#include "nts_ke_client.h"
#include "nts_ke_server.h"
#include "nts_ntp_server.h"
#include "socket.h"
#include "sources.h"
#include "sourcestats.h"
#include "reference.h"
@@ -49,8 +54,10 @@
#include "refclock.h"
#include "clientlog.h"
#include "nameserv.h"
#include "privops.h"
#include "smooth.h"
#include "tempcomp.h"
#include "util.h"
/* ================================================== */
@@ -67,12 +74,28 @@ static REF_Mode ref_mode = REF_ModeNormal;
/* ================================================== */
static void
do_platform_checks(void)
{
/* Require at least 32-bit integers, two's complement representation and
the usual implementation of conversion of unsigned integers */
assert(sizeof (int) >= 4);
assert(-1 == ~0);
assert((int32_t)4294967295U == (int32_t)-1);
}
/* ================================================== */
static void
delete_pidfile(void)
{
const char *pidfile = CNF_GetPidFile();
/* Don't care if this fails, there's not a lot we can do */
unlink(pidfile);
if (!pidfile[0])
return;
if (!UTI_RemoveFile(NULL, pidfile, NULL))
;
}
/* ================================================== */
@@ -82,7 +105,7 @@ MAI_CleanupAndExit(void)
{
if (!initialised) exit(exit_status);
if (CNF_GetDumpOnExit()) {
if (CNF_GetDumpDir()[0] != '\0') {
SRC_DumpSources();
}
@@ -93,11 +116,16 @@ MAI_CleanupAndExit(void)
TMC_Finalise();
MNL_Finalise();
CLG_Finalise();
NKC_Finalise();
NKS_Finalise();
NNS_Finalise();
NSD_Finalise();
NSR_Finalise();
NCR_Finalise();
CAM_Finalise();
NIO_Finalise();
SST_Finalise();
NCR_Finalise();
NIO_Finalise();
CAM_Finalise();
SCK_Finalise();
KEY_Finalise();
RCL_Finalise();
SRC_Finalise();
@@ -106,13 +134,13 @@ MAI_CleanupAndExit(void)
SYS_Finalise();
SCH_Finalise();
LCL_Finalise();
PRV_Finalise();
delete_pidfile();
CNF_Finalise();
LOG_Finalise();
HSH_Finalise();
LOG_Finalise();
exit(exit_status);
}
@@ -128,6 +156,16 @@ signal_cleanup(int x)
/* ================================================== */
static void
quit_timeout(void *arg)
{
/* Return with non-zero status if the clock is not synchronised */
exit_status = REF_GetOurStratum() >= NTP_MAX_STRATUM;
SCH_QuitProgram();
}
/* ================================================== */
static void
ntp_source_resolving_end(void)
{
@@ -141,6 +179,7 @@ ntp_source_resolving_end(void)
SRC_ReloadSources();
}
SRC_RemoveDumpFiles();
RTC_StartMeasurements();
RCL_StartRefclocks();
NSR_StartSources();
@@ -214,84 +253,70 @@ post_init_rtc_hook(void *anything)
}
/* ================================================== */
/* Return 1 if the process exists on the system. */
static int
does_process_exist(int pid)
{
int status;
status = getsid(pid);
if (status >= 0) {
return 1;
} else {
return 0;
}
}
/* ================================================== */
static int
maybe_another_chronyd_running(int *other_pid)
static void
check_pidfile(void)
{
const char *pidfile = CNF_GetPidFile();
FILE *in;
int pid, count;
*other_pid = 0;
if (!pidfile[0])
return;
in = fopen(pidfile, "r");
if (!in) return 0;
in = UTI_OpenFile(NULL, pidfile, NULL, 'r', 0);
if (!in)
return;
count = fscanf(in, "%d", &pid);
fclose(in);
if (count != 1) return 0;
if (count != 1)
return;
*other_pid = pid;
return does_process_exist(pid);
if (getsid(pid) < 0)
return;
LOG_FATAL("Another chronyd may already be running (pid=%d), check %s",
pid, pidfile);
}
/* ================================================== */
static void
write_lockfile(void)
write_pidfile(void)
{
const char *pidfile = CNF_GetPidFile();
FILE *out;
out = fopen(pidfile, "w");
if (!out) {
LOG_FATAL(LOGF_Main, "could not open lockfile %s for writing", pidfile);
} else {
fprintf(out, "%d\n", getpid());
if (!pidfile[0])
return;
out = UTI_OpenFile(NULL, pidfile, NULL, 'W', 0644);
fprintf(out, "%d\n", (int)getpid());
fclose(out);
}
}
/* ================================================== */
#define DEV_NULL "/dev/null"
static void
go_daemon(void)
{
#ifdef WINNT
#else
int pid, fd, pipefd[2];
/* Create pipe which will the daemon use to notify the grandparent
when it's initialised or send an error message */
if (pipe(pipefd)) {
LOG_FATAL(LOGF_Logging, "Could not detach, pipe failed : %s", strerror(errno));
LOG_FATAL("pipe() failed : %s", strerror(errno));
}
/* Does this preserve existing signal handlers? */
pid = fork();
if (pid < 0) {
LOG_FATAL(LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno));
LOG_FATAL("fork() failed : %s", strerror(errno));
} else if (pid > 0) {
/* In the 'grandparent' */
char message[1024];
@@ -302,7 +327,8 @@ go_daemon(void)
if (r) {
if (r > 0) {
/* Print the error message from the child */
fprintf(stderr, "%.1024s\n", message);
message[sizeof (message) - 1] = '\0';
fprintf(stderr, "%s\n", message);
}
exit(1);
} else
@@ -316,7 +342,7 @@ go_daemon(void)
pid = fork();
if (pid < 0) {
LOG_FATAL(LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno));
LOG_FATAL("fork() failed : %s", strerror(errno));
} else if (pid > 0) {
exit(0); /* In the 'parent' */
} else {
@@ -324,7 +350,7 @@ go_daemon(void)
/* Change current directory to / */
if (chdir("/") < 0) {
LOG_FATAL(LOGF_Logging, "Could not chdir to / : %s", strerror(errno));
LOG_FATAL("chdir() failed : %s", strerror(errno));
}
/* Don't keep stdin/out/err from before. But don't close
@@ -335,10 +361,44 @@ go_daemon(void)
}
LOG_SetParentFd(pipefd[1]);
}
}
#endif
/* Open /dev/null as new stdin/out/err */
errno = 0;
if (open(DEV_NULL, O_RDONLY) != STDIN_FILENO ||
open(DEV_NULL, O_WRONLY) != STDOUT_FILENO ||
open(DEV_NULL, O_RDWR) != STDERR_FILENO)
LOG_FATAL("Could not open %s : %s", DEV_NULL, strerror(errno));
}
}
}
/* ================================================== */
static void
print_help(const char *progname)
{
printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n",
progname);
}
/* ================================================== */
static void
print_version(void)
{
printf("chronyd (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYD_FEATURES);
}
/* ================================================== */
static int
parse_int_arg(const char *arg)
{
int i;
if (sscanf(arg, "%d", &i) != 1)
LOG_FATAL("Invalid argument %s", arg);
return i;
}
/* ================================================== */
@@ -348,129 +408,166 @@ int main
{
const char *conf_file = DEFAULT_CONF_FILE;
const char *progname = argv[0];
char *user = NULL;
int debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
int do_init_rtc = 0, restarted = 0;
int other_pid;
int lock_memory = 0, sched_priority = 0;
int system_log = 1;
char *user = NULL, *log_file = NULL;
struct passwd *pw;
int opt, debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
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;
do_platform_checks();
LOG_Initialise();
/* Parse command line options */
while (++argv, (--argc)>0) {
if (!strcmp("-f", *argv)) {
++argv, --argc;
conf_file = *argv;
} else if (!strcmp("-P", *argv)) {
++argv, --argc;
if (argc == 0 || sscanf(*argv, "%d", &sched_priority) != 1) {
LOG_FATAL(LOGF_Main, "Bad scheduler priority");
}
} else if (!strcmp("-m", *argv)) {
lock_memory = 1;
} else if (!strcmp("-r", *argv)) {
reload = 1;
} else if (!strcmp("-R", *argv)) {
restarted = 1;
} else if (!strcmp("-u", *argv)) {
++argv, --argc;
if (argc == 0) {
LOG_FATAL(LOGF_Main, "Missing user name");
} else {
user = *argv;
}
} else if (!strcmp("-s", *argv)) {
do_init_rtc = 1;
} else if (!strcmp("-v", *argv) || !strcmp("--version",*argv)) {
/* This write to the terminal is OK, it comes before we turn into a daemon */
printf("chronyd (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYD_FEATURES);
/* Parse (undocumented) long command-line options */
for (optind = 1; optind < argc; optind++) {
if (!strcmp("--help", argv[optind])) {
print_help(progname);
return 0;
} else if (!strcmp("-n", *argv)) {
nofork = 1;
} else if (!strcmp("-d", *argv)) {
} else if (!strcmp("--version", argv[optind])) {
print_version();
return 0;
}
}
optind = 1;
/* Parse short command-line options */
while ((opt = getopt(argc, argv, "46df:F:hl:L:mnP:qQrRst:u:vx")) != -1) {
switch (opt) {
case '4':
case '6':
address_family = opt == '4' ? IPADDR_INET4 : IPADDR_INET6;
break;
case 'd':
debug++;
nofork = 1;
system_log = 0;
} else if (!strcmp("-q", *argv)) {
break;
case 'f':
conf_file = optarg;
break;
case 'F':
scfilter_level = parse_int_arg(optarg);
break;
case 'l':
log_file = optarg;
break;
case 'L':
log_severity = parse_int_arg(optarg);
break;
case 'm':
lock_memory = 1;
break;
case 'n':
nofork = 1;
break;
case 'P':
sched_priority = parse_int_arg(optarg);
break;
case 'q':
ref_mode = REF_ModeUpdateOnce;
nofork = 1;
client_only = 0;
system_log = 0;
} else if (!strcmp("-Q", *argv)) {
break;
case 'Q':
ref_mode = REF_ModePrintOnce;
nofork = 1;
client_only = 1;
clock_control = 0;
system_log = 0;
} else if (!strcmp("-4", *argv)) {
address_family = IPADDR_INET4;
} else if (!strcmp("-6", *argv)) {
address_family = IPADDR_INET6;
} else if (!strcmp("-h", *argv) || !strcmp("--help", *argv)) {
printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-f FILE|COMMAND...]\n",
progname);
return 0;
} else if (*argv[0] == '-') {
LOG_FATAL(LOGF_Main, "Unrecognized command line option [%s]", *argv);
} else {
/* Process remaining arguments and configuration lines */
config_args = argc;
break;
case 'r':
reload = 1;
break;
case 'R':
restarted = 1;
break;
case 's':
do_init_rtc = 1;
break;
case 't':
timeout = parse_int_arg(optarg);
break;
case 'u':
user = optarg;
break;
case 'v':
print_version();
return 0;
case 'x':
clock_control = 0;
break;
default:
print_help(progname);
return opt != 'h';
}
}
if (getuid() != 0) {
/* This write to the terminal is OK, it comes before we turn into a daemon */
fprintf(stderr,"Not superuser\n");
return 1;
}
if (getuid() && !client_only)
LOG_FATAL("Not superuser");
/* Turn into a daemon */
if (!nofork) {
go_daemon();
}
if (system_log) {
if (log_file) {
LOG_OpenFileLog(log_file);
} else if (system_log) {
LOG_OpenSystemLog();
}
LOG_SetDebugLevel(debug);
LOG_SetMinSeverity(debug >= 2 ? LOGS_DEBUG : log_severity);
LOG(LOGS_INFO, LOGF_Main, "chronyd version %s starting (%s)",
CHRONY_VERSION, CHRONYD_FEATURES);
LOG(LOGS_INFO, "chronyd version %s starting (%s)", CHRONY_VERSION, CHRONYD_FEATURES);
DNS_SetAddressFamily(address_family);
CNF_Initialise(restarted);
CNF_Initialise(restarted, client_only);
/* Parse the config file or the remaining command line arguments */
config_args = argc - optind;
if (!config_args) {
CNF_ReadFile(conf_file);
} else {
do {
CNF_ParseLine(NULL, config_args - argc + 1, *argv);
} while (++argv, --argc);
for (; optind < argc; optind++)
CNF_ParseLine(NULL, config_args + optind - argc + 1, argv[optind]);
}
/* Check whether another chronyd may already be running. Do this after
* forking, so that message logging goes to the right place (i.e. syslog), in
* case this chronyd is being run from a boot script. */
if (maybe_another_chronyd_running(&other_pid)) {
LOG_FATAL(LOGF_Main, "Another chronyd may already be running (pid=%d), check lockfile (%s)",
other_pid, CNF_GetPidFile());
}
/* Check whether another chronyd may already be running */
check_pidfile();
/* Write our lockfile to prevent other chronyds running. This has *GOT* to
* be done *AFTER* the daemon-creation fork() */
write_lockfile();
if (!user)
user = CNF_GetUser();
pw = getpwnam(user);
if (!pw)
LOG_FATAL("Could not get user/group ID of %s", user);
/* Create directories for sockets, log files, and dump files */
CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
/* Write our pidfile to prevent other instances from running */
write_pidfile();
PRV_Initialise();
LCL_Initialise();
SCH_Initialise();
SYS_Initialise();
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);
NCR_Initialise();
CNF_SetupAccessRestrictions();
/* Command-line switch must have priority */
if (!sched_priority) {
@@ -484,21 +581,17 @@ int main
SYS_LockMemory();
}
if (!user) {
user = CNF_GetUser();
}
if (user && strcmp(user, "root")) {
SYS_DropRoot(user);
}
LOG_CreateLogFileDir();
/* Drop root privileges if the specified user has a non-zero UID */
if (!geteuid() && (pw->pw_uid || pw->pw_gid))
SYS_DropRoot(pw->pw_uid, pw->pw_gid);
REF_Initialise();
SST_Initialise();
NIO_Initialise(address_family);
CAM_Initialise(address_family);
NCR_Initialise();
NSR_Initialise();
NSD_Initialise();
NNS_Initialise();
NKS_Initialise(scfilter_level);
NKC_Initialise();
CLG_Initialise();
MNL_Initialise();
TMC_Initialise();
@@ -507,7 +600,12 @@ int main
/* From now on, it is safe to do finalisation on exit */
initialised = 1;
CNF_SetupAccessRestrictions();
UTI_SetQuitSignalsHandler(signal_cleanup, 1);
CAM_OpenUnixSocket();
if (scfilter_level)
SYS_EnableSystemCallFilter(scfilter_level, SYS_MAIN_PROCESS);
if (ref_mode == REF_ModeNormal && CNF_GetInitSources() > 0) {
ref_mode = REF_ModeInitStepSlew;
@@ -516,24 +614,20 @@ int main
REF_SetModeEndHandler(reference_mode_end);
REF_SetMode(ref_mode);
if (timeout >= 0)
SCH_AddTimeoutByDelay(timeout, quit_timeout, NULL);
if (do_init_rtc) {
RTC_TimeInit(post_init_rtc_hook, NULL);
} else {
post_init_rtc_hook(NULL);
}
signal(SIGINT, signal_cleanup);
signal(SIGTERM, signal_cleanup);
#if !defined(WINNT)
signal(SIGQUIT, signal_cleanup);
signal(SIGHUP, signal_cleanup);
#endif /* WINNT */
/* The program normally runs under control of the main loop in
the scheduler. */
SCH_MainLoop();
LOG(LOGS_INFO, LOGF_Main, "chronyd exiting");
LOG(LOGS_INFO, "chronyd exiting");
MAI_CleanupAndExit();

View File

@@ -1,6 +1,6 @@
#!/bin/sh
LANG=C
LANG=C.UTF-8
export LANG
if [ $# -ne 1 ]; then
@@ -11,7 +11,6 @@ fi
version=$1
tag=$version
subdir=chrony-${version}
mandate=$(date +'%B %Y')
umask 022
@@ -37,39 +36,13 @@ cd RELEASES/$subdir || exit 1
echo $version > version.txt
sed -i -e "s%@@VERSION@@%${version}%" examples/chrony.spec
./configure && make -C doc man txt || exit 1
for m in chronyc.1.in chrony.conf.5.in chronyd.8.in; do
sed -e "s%@VERSION@%${version}%;s%@MAN_DATE@%${mandate}%" \
< $m > ${m}_
mv -f ${m}_ $m
done
iconv -f utf-8 -t ascii//TRANSLIT < doc/installation.txt > INSTALL
iconv -f utf-8 -t ascii//TRANSLIT < doc/faq.txt > FAQ
./configure && make chrony.txt || exit 1
mv chrony.txt chrony.txt_
make distclean
mv chrony.txt_ chrony.txt
awk '/^[1-9] Installation$/{p=1}
/^[1-9]\.. Support for line editing/{exit}; p' chrony.txt | \
tail -n +4 > INSTALL
if [ $(wc -l < INSTALL) -gt 100 -o $(wc -l < INSTALL) -lt 85 ]; then
echo "INSTALL generated incorrectly?"
exit 3
fi
awk '/^[1-9] Frequently asked questions$/{p=1}
/^Appendix A GNU General Public License$/{exit}; p' chrony.txt | \
tail -n +4 | sed 's/^[1-9]\.\([1-9]\)/\1/' | sed 's/^----/--/' | \
sed 's/^====/==/' > FAQ
if [ $(wc -l < FAQ) -gt 400 -o $(wc -l < FAQ) -lt 200 ]; then
echo "FAQ generated incorrectly?"
exit 3
fi
rm -f config.h config.log make_release .gitignore
rm -f make_release .gitignore
cd ..
tar cv --owner root --group root $subdir | gzip -9 > ${subdir}.tar.gz

View File

@@ -47,7 +47,7 @@ static int enabled = 0;
/* More recent samples at highest indices */
typedef struct {
struct timeval when; /* This is our 'cooked' time */
struct timespec when; /* This is our 'cooked' time */
double orig_offset; /*+ Not modified by slew samples */
double offset; /*+ if we are fast of the supplied reference */
double residual; /*+ regression residual (sign convention given by
@@ -64,8 +64,8 @@ static int n_samples;
/* ================================================== */
static void
slew_samples(struct timeval *raw,
struct timeval *cooked,
slew_samples(struct timespec *raw,
struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -97,7 +97,8 @@ MNL_Finalise(void)
/* ================================================== */
static void
estimate_and_set_system(struct timeval *now, int offset_provided, double offset, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
estimate_and_set_system(struct timespec *now, int offset_provided, double offset,
double *reg_offset, double *dfreq_ppm, double *new_afreq_ppm)
{
double agos[MAX_SAMPLES], offsets[MAX_SAMPLES];
double b0, b1;
@@ -108,32 +109,26 @@ estimate_and_set_system(struct timeval *now, int offset_provided, double offset,
int found_freq;
double slew_by;
b0 = offset_provided ? offset : 0.0;
b1 = freq = 0.0;
found_freq = 0;
if (n_samples > 1) {
for (i=0; i<n_samples; i++) {
UTI_DiffTimevalsToDouble(&agos[i], &samples[n_samples-1].when, &samples[i].when);
agos[i] = UTI_DiffTimespecsToDouble(&samples[n_samples - 1].when, &samples[i].when);
offsets[i] = samples[i].offset;
}
RGR_FindBestRobustRegression(agos, offsets, n_samples,
1.0e-8, /* 0.01ppm easily good enough for this! */
&b0, &b1, &n_runs, &best_start);
if (RGR_FindBestRobustRegression(agos, offsets, n_samples, 1.0e-8,
&b0, &b1, &n_runs, &best_start)) {
/* Ignore b0 from regression; treat offset as being the most
recently entered value. (If the administrator knows he's put
an outlier in, he will rerun the settime operation.) However,
the frequency estimate comes from the regression. */
freq = -b1;
found_freq = 1;
} else {
if (offset_provided) {
b0 = offset;
} else {
b0 = 0.0;
}
b1 = freq = 0.0;
found_freq = 0;
} else {
agos[0] = 0.0;
offsets[0] = b0;
}
@@ -145,21 +140,20 @@ estimate_and_set_system(struct timeval *now, int offset_provided, double offset,
}
if (found_freq) {
LOG(LOGS_INFO, LOGF_Manual,
"Making a frequency change of %.3f ppm and a slew of %.6f",
LOG(LOGS_INFO, "Making a frequency change of %.3f ppm and a slew of %.6f",
1.0e6 * freq, slew_by);
REF_SetManualReference(now,
slew_by,
freq, skew);
} else {
LOG(LOGS_INFO, LOGF_Manual, "Making a slew of %.6f", slew_by);
LOG(LOGS_INFO, "Making a slew of %.6f", slew_by);
REF_SetManualReference(now,
slew_by,
0.0, skew);
}
if (offset_cs) *offset_cs = (long)(0.5 + 100.0 * b0);
if (reg_offset) *reg_offset = b0;
if (dfreq_ppm) *dfreq_ppm = 1.0e6 * freq;
if (new_afreq_ppm) *new_afreq_ppm = LCL_ReadAbsoluteFrequency();
@@ -173,9 +167,9 @@ estimate_and_set_system(struct timeval *now, int offset_provided, double offset,
/* ================================================== */
int
MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
MNL_AcceptTimestamp(struct timespec *ts, double *reg_offset, double *dfreq_ppm, double *new_afreq_ppm)
{
struct timeval now;
struct timespec now;
double offset, diff;
int i;
@@ -189,12 +183,12 @@ MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, doub
return 0;
if (n_samples) {
UTI_DiffTimevalsToDouble(&diff, &now, &samples[n_samples - 1].when);
diff = UTI_DiffTimespecsToDouble(&now, &samples[n_samples - 1].when);
if (diff < MIN_SAMPLE_SEPARATION)
return 0;
}
UTI_DiffTimevalsToDouble(&offset, &now, ts);
offset = UTI_DiffTimespecsToDouble(&now, ts);
/* Check if buffer full up */
if (n_samples == MAX_SAMPLES) {
@@ -210,7 +204,7 @@ MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, doub
samples[n_samples].orig_offset = offset;
++n_samples;
estimate_and_set_system(&now, 1, offset, offset_cs, dfreq_ppm, new_afreq_ppm);
estimate_and_set_system(&now, 1, offset, reg_offset, dfreq_ppm, new_afreq_ppm);
return 1;
@@ -224,8 +218,8 @@ MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, doub
/* ================================================== */
static void
slew_samples(struct timeval *raw,
struct timeval *cooked,
slew_samples(struct timespec *raw,
struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -239,7 +233,7 @@ slew_samples(struct timeval *raw,
}
for (i=0; i<n_samples; i++) {
UTI_AdjustTimeval(&samples[i].when, cooked, &samples[i].when, &delta_time,
UTI_AdjustTimespec(&samples[i].when, cooked, &samples[i].when, &delta_time,
dfreq, doffset);
samples[i].offset += delta_time;
}
@@ -309,7 +303,7 @@ int
MNL_DeleteSample(int index)
{
int i;
struct timeval now;
struct timespec now;
if ((index < 0) || (index >= n_samples)) {
return 0;

View File

@@ -33,7 +33,7 @@
extern void MNL_Initialise(void);
extern void MNL_Finalise(void);
extern int MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm);
extern int MNL_AcceptTimestamp(struct timespec *ts, double *reg_offset, double *dfreq_ppm, double *new_afreq_ppm);
extern void MNL_Enable(void);
extern void MNL_Disable(void);

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2014
* Copyright (C) Miroslav Lichvar 2014, 2017
*
* 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
@@ -37,7 +37,7 @@ Malloc(size_t size)
r = malloc(size);
if (!r && size)
LOG_FATAL(LOGF_Memory, "Could not allocate memory");
LOG_FATAL("Could not allocate memory");
return r;
}
@@ -49,11 +49,37 @@ Realloc(void *ptr, size_t size)
r = realloc(ptr, size);
if (!r && size)
LOG_FATAL(LOGF_Memory, "Could not allocate memory");
LOG_FATAL("Could not allocate memory");
return r;
}
static size_t
get_array_size(size_t nmemb, size_t size)
{
size_t array_size;
array_size = nmemb * size;
/* Check for overflow */
if (nmemb > 0 && array_size / nmemb != size)
LOG_FATAL("Could not allocate memory");
return array_size;
}
void *
Malloc2(size_t nmemb, size_t size)
{
return Malloc(get_array_size(nmemb, size));
}
void *
Realloc2(void *ptr, size_t nmemb, size_t size)
{
return Realloc(ptr, get_array_size(nmemb, size));
}
char *
Strdup(const char *s)
{
@@ -61,7 +87,7 @@ Strdup(const char *s)
r = strdup(s);
if (!r)
LOG_FATAL(LOGF_Memory, "Could not allocate memory");
LOG_FATAL("Could not allocate memory");
return r;
}

View File

@@ -27,15 +27,19 @@
#ifndef GOT_MEMORY_H
#define GOT_MEMORY_H
#include "sysincl.h"
/* Wrappers checking for errors */
extern void *Malloc(size_t size);
extern void *Realloc(void *ptr, size_t size);
extern void *Malloc2(size_t nmemb, size_t size);
extern void *Realloc2(void *ptr, size_t nmemb, size_t size);
extern char *Strdup(const char *s);
/* Convenient macros */
#define MallocNew(T) ((T *) Malloc(sizeof(T)))
#define MallocArray(T, n) ((T *) Malloc((n) * sizeof(T)))
#define ReallocArray(T,n,x) ((T *) Realloc((void *)(x), (n)*sizeof(T)))
#define MallocArray(T, n) ((T *) Malloc2(n, sizeof(T)))
#define ReallocArray(T, n, x) ((T *) Realloc2((void *)(x), n, sizeof(T)))
#define Free(x) free(x)
#endif /* GOT_MEMORY_H */

135
mkdirpp.c
View File

@@ -1,135 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2002
*
* 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.
*
**********************************************************************
=======================================================================
A function for creating a directory and any parent directories that
don't exist.
*/
#include "config.h"
#include "sysincl.h"
#include "memory.h"
#include "mkdirpp.h"
static int
do_dir(char *p)
{
int status;
struct stat buf;
#if defined(TEST)
fprintf(stderr, "do_dir(%s)\n", p);
#endif
/* See if directory exists */
status = stat(p, &buf);
if (status < 0) {
if (errno == ENOENT) {
/* Try to create directory */
status = mkdir(p, 0755);
return status;
} else {
return status;
}
}
if (!S_ISDIR(buf.st_mode)) {
return -1;
}
return 0;
}
/* ================================================== */
/* Return 0 if the directory couldn't be created, 1 if it could (or
already existed) */
int
mkdir_and_parents(const char *path)
{
char *p;
int len;
int i, j, k, last;
len = strlen(path);
p = (char *)Malloc(1 + len);
i = k = 0;
while (1) {
p[i++] = path[k++];
if (path[k] == '/' || !path[k]) {
p[i] = 0;
if (do_dir(p) < 0) {
Free(p);
return 0;
}
if (!path[k]) {
/* End of the string */
break;
}
/* check whether its a trailing / or group of / */
last = 1;
j = k+1;
while (path[j]) {
if (path[j] != '/') {
k = j - 1; /* Pick up a / into p[] thru the assignment at the top of the loop */
last = 0;
break;
}
j++;
}
if (last) {
break;
}
}
if (!path[k]) break;
}
Free(p);
return 1;
}
/* ================================================== */
#if defined(TEST)
int main(int argc, char **argv) {
if (argc > 1) {
/* Invert sense of result */
return mkdir_and_parents(argv[1]) ? 0 : 1;
} else {
return 1;
}
}
#endif

View File

@@ -30,7 +30,11 @@
#include "sysincl.h"
#include <netdb.h>
#include <resolv.h>
#include "nameserv.h"
#include "socket.h"
#include "util.h"
/* ================================================== */
@@ -50,9 +54,23 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
struct addrinfo hints, *res, *ai;
int i, result;
max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
memset(&hints, 0, sizeof (hints));
switch (address_family) {
case IPADDR_INET4:
hints.ai_family = AF_INET;
break;
#ifdef FEAT_IPV6
case IPADDR_INET6:
hints.ai_family = AF_INET6;
break;
#endif
default:
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
}
hints.ai_socktype = SOCK_DGRAM;
result = getaddrinfo(name, NULL, &hints, &res);
@@ -99,6 +117,8 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET4)
return DNS_Failure;
max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
host = gethostbyname(name);
if (host == NULL) {
@@ -137,10 +157,14 @@ DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len)
#ifdef FEAT_IPV6
struct sockaddr_in6 in6;
IPSockAddr ip_saddr;
socklen_t slen;
char hbuf[NI_MAXHOST];
slen = UTI_IPAndPortToSockaddr(ip_addr, 0, (struct sockaddr *)&in6);
ip_saddr.ip_addr = *ip_addr;
ip_saddr.port = 0;
slen = SCK_IPSockAddrToSockaddr(&ip_saddr, (struct sockaddr *)&in6, sizeof (in6));
if (!getnameinfo((struct sockaddr *)&in6, slen, hbuf, sizeof (hbuf), NULL, 0, 0))
result = hbuf;
#else

View File

@@ -39,6 +39,9 @@ typedef enum {
/* Resolve names only to selected address family */
extern void DNS_SetAddressFamily(int family);
/* Maximum number of addresses returned by DNS_Name2IPAddress */
#define DNS_MAX_ADDRESSES 16
extern DNS_Status DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs);
extern int DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len);

View File

@@ -31,20 +31,19 @@
#include "nameserv_async.h"
#include "logging.h"
#include "memory.h"
#include "privops.h"
#include "sched.h"
#include "util.h"
#ifdef USE_PTHREAD_ASYNCDNS
#include <pthread.h>
#define MAX_ADDRESSES 16
/* ================================================== */
struct DNS_Async_Instance {
const char *name;
DNS_Status status;
IPAddr addresses[MAX_ADDRESSES];
IPAddr addresses[DNS_MAX_ADDRESSES];
DNS_NameResolveHandler handler;
void *arg;
@@ -52,7 +51,7 @@ struct DNS_Async_Instance {
int pipe[2];
};
static int resolving_threads = 0;
static pthread_mutex_t privops_lock = PTHREAD_MUTEX_INITIALIZER;
/* ================================================== */
@@ -61,7 +60,9 @@ start_resolving(void *anything)
{
struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything;
inst->status = DNS_Name2IPAddress(inst->name, inst->addresses, MAX_ADDRESSES);
pthread_mutex_lock(&privops_lock);
inst->status = PRV_Name2IPAddress(inst->name, inst->addresses, DNS_MAX_ADDRESSES);
pthread_mutex_unlock(&privops_lock);
/* Notify the main thread that the result is ready */
if (write(inst->pipe[1], "", 1) < 0)
@@ -73,22 +74,20 @@ start_resolving(void *anything)
/* ================================================== */
static void
end_resolving(void *anything)
end_resolving(int fd, int event, void *anything)
{
struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything;
int i;
if (pthread_join(inst->thread, NULL)) {
LOG_FATAL(LOGF_Nameserv, "pthread_join() failed");
LOG_FATAL("pthread_join() failed");
}
resolving_threads--;
SCH_RemoveInputFileHandler(inst->pipe[0]);
SCH_RemoveFileHandler(inst->pipe[0]);
close(inst->pipe[0]);
close(inst->pipe[1]);
for (i = 0; inst->status == DNS_Success && i < MAX_ADDRESSES &&
for (i = 0; inst->status == DNS_Success && i < DNS_MAX_ADDRESSES &&
inst->addresses[i].family != IPADDR_UNSPEC; i++)
;
@@ -111,17 +110,17 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
inst->status = DNS_Failure;
if (pipe(inst->pipe)) {
LOG_FATAL(LOGF_Nameserv, "pipe() failed");
LOG_FATAL("pipe() failed");
}
resolving_threads++;
assert(resolving_threads <= 1);
UTI_FdSetCloexec(inst->pipe[0]);
UTI_FdSetCloexec(inst->pipe[1]);
if (pthread_create(&inst->thread, NULL, start_resolving, inst)) {
LOG_FATAL(LOGF_Nameserv, "pthread_create() failed");
LOG_FATAL("pthread_create() failed");
}
SCH_AddInputFileHandler(inst->pipe[0], end_resolving, inst);
SCH_AddFileHandler(inst->pipe[0], SCH_FILE_INPUT, end_resolving, inst);
}
/* ================================================== */

View File

@@ -34,8 +34,7 @@
typedef void (*DNS_NameResolveHandler)(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *anything);
/* Request resolving of a name to IP address. The handler will be
called when the result is available, but it may be also called
directly from this function call. */
called when the result is available. */
extern void DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *anything);
#endif

89
ntp.h
View File

@@ -38,23 +38,33 @@ typedef struct {
typedef uint32_t NTP_int32;
/* The UDP port number used by NTP */
#define NTP_PORT 123
/* The NTP protocol version that we support */
#define NTP_VERSION 4
/* Maximum stratum number (infinity) */
#define NTP_MAX_STRATUM 16
/* The minimum valid length of an extension field */
#define NTP_MIN_EXTENSION_LENGTH 16
/* The maximum assumed length of all extension fields in received
packets (RFC 5905 doesn't specify a limit on length or number of
extension fields in one packet) */
#define NTP_MAX_EXTENSIONS_LENGTH 1024
/* Invalid stratum number */
#define NTP_INVALID_STRATUM 0
/* The minimum and maximum supported length of MAC */
#define NTP_MIN_MAC_LENGTH 16
#define NTP_MAX_MAC_LENGTH MAX_HASH_LENGTH
#define NTP_MIN_MAC_LENGTH (4 + 16)
#define NTP_MAX_MAC_LENGTH (4 + MAX_HASH_LENGTH)
/* The minimum valid length of an extension field */
#define NTP_MIN_EF_LENGTH 16
/* The maximum assumed length of all extension fields in an NTP packet,
including a MAC (RFC 5905 doesn't specify a limit on length or number of
extension fields in one packet) */
#define NTP_MAX_EXTENSIONS_LENGTH (1024 + NTP_MAX_MAC_LENGTH)
/* The maximum length of MAC in NTPv4 packets which allows deterministic
parsing of extension fields (RFC 7822) */
#define NTP_MAX_V4_MAC_LENGTH (4 + 20)
/* Type definition for leap bits */
typedef enum {
@@ -86,21 +96,10 @@ typedef struct {
NTP_int64 receive_ts;
NTP_int64 transmit_ts;
/* Optional extension fields, we don't send packets with them yet */
/* uint8_t extensions[] */
/* Optional message authentication code (MAC) */
NTP_int32 auth_keyid;
uint8_t auth_data[NTP_MAX_MAC_LENGTH];
uint8_t extensions[NTP_MAX_EXTENSIONS_LENGTH];
} NTP_Packet;
#define NTP_NORMAL_PACKET_LENGTH (int)offsetof(NTP_Packet, auth_keyid)
/* The buffer used to hold a datagram read from the network */
typedef struct {
NTP_Packet ntp_pkt;
uint8_t extensions[NTP_MAX_EXTENSIONS_LENGTH];
} NTP_Receive_Buffer;
#define NTP_HEADER_LENGTH (int)offsetof(NTP_Packet, extensions)
/* Macros to work with the lvm field */
#define NTP_LVM_TO_LEAP(lvm) (((lvm) >> 6) & 0x3)
@@ -109,4 +108,50 @@ typedef struct {
#define NTP_LVM(leap, version, mode) \
((((leap) << 6) & 0xc0) | (((version) << 3) & 0x38) | ((mode) & 0x07))
/* Special NTP reference IDs */
#define NTP_REFID_UNSYNC 0x0UL
#define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */
#define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */
/* Enumeration for authentication modes of NTP packets */
typedef enum {
NTP_AUTH_NONE = 0, /* No authentication */
NTP_AUTH_SYMMETRIC, /* MAC using symmetric key (RFC 1305, RFC 5905) */
NTP_AUTH_MSSNTP, /* MS-SNTP authenticator field */
NTP_AUTH_MSSNTP_EXT, /* MS-SNTP extended authenticator field */
NTP_AUTH_NTS, /* Network Time Security (RFC ????) */
} NTP_AuthMode;
/* Structure describing an NTP packet */
typedef struct {
int length;
int version;
NTP_Mode mode;
int ext_fields;
struct {
NTP_AuthMode mode;
struct {
int start;
int length;
uint32_t key_id;
} mac;
} auth;
} NTP_PacketInfo;
/* Structure used to save NTP measurements. time is the local time at which
the sample is to be considered to have been made and offset is the offset at
the time (positive indicates that the local clock is slow relative to the
source). root_delay/root_dispersion include peer_delay/peer_dispersion. */
typedef struct {
struct timespec time;
double offset;
double peer_delay;
double peer_dispersion;
double root_delay;
double root_dispersion;
int stratum;
} NTP_Sample;
#endif /* GOT_NTP_H */

486
ntp_auth.c Normal file
View File

@@ -0,0 +1,486 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2019
*
* 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.
*
**********************************************************************
=======================================================================
NTP authentication
*/
#include "config.h"
#include "sysincl.h"
#include "keys.h"
#include "logging.h"
#include "memory.h"
#include "ntp_auth.h"
#include "ntp_ext.h"
#include "ntp_signd.h"
#include "nts_ntp.h"
#include "nts_ntp_client.h"
#include "nts_ntp_server.h"
#include "srcparams.h"
#include "util.h"
/* Structure to hold authentication configuration and state */
struct NAU_Instance_Record {
NTP_AuthMode mode; /* Authentication mode of NTP packets */
uint32_t key_id; /* Identifier of a symmetric key */
NNC_Instance nts; /* Client NTS state */
};
/* ================================================== */
static int
generate_symmetric_auth(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info)
{
int auth_len, max_auth_len;
/* 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);
auth_len = KEY_GenerateAuth(key_id, (unsigned char *)packet, info->length,
(unsigned char *)packet + info->length + 4, max_auth_len);
if (!auth_len) {
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;
return 1;
}
/* ================================================== */
static int
check_symmetric_auth(NTP_Packet *packet, NTP_PacketInfo *info)
{
int trunc_len;
if (info->auth.mac.length < NTP_MIN_MAC_LENGTH)
return 0;
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,
(unsigned char *)packet + info->auth.mac.start + 4,
info->auth.mac.length - 4, trunc_len - 4))
return 0;
return 1;
}
/* ================================================== */
static void
adjust_timestamp(NTP_AuthMode mode, uint32_t key_id, struct timespec *ts)
{
switch (mode) {
case NTP_AUTH_SYMMETRIC:
ts->tv_nsec += KEY_GetAuthDelay(key_id);
UTI_NormaliseTimespec(ts);
break;
case NTP_AUTH_MSSNTP:
ts->tv_nsec += NSD_GetAuthDelay(key_id);
UTI_NormaliseTimespec(ts);
default:
break;
}
}
/* ================================================== */
static int
is_zero_data(unsigned char *data, int length)
{
int i;
for (i = 0; i < length; i++)
if (data[i])
return 0;
return 1;
}
/* ================================================== */
static NAU_Instance
create_instance(NTP_AuthMode mode)
{
NAU_Instance instance;
instance = MallocNew(struct NAU_Instance_Record);
instance->mode = mode;
instance->key_id = INACTIVE_AUTHKEY;
instance->nts = NULL;
assert(sizeof (instance->key_id) == 4);
return instance;
}
/* ================================================== */
NAU_Instance
NAU_CreateNoneInstance(void)
{
return create_instance(NTP_AUTH_NONE);
}
/* ================================================== */
NAU_Instance
NAU_CreateSymmetricInstance(uint32_t key_id)
{
NAU_Instance instance = create_instance(NTP_AUTH_SYMMETRIC);
instance->key_id = key_id;
if (!KEY_KeyKnown(key_id))
LOG(LOGS_WARN, "Key %"PRIu32" is %s", key_id, "missing");
else if (!KEY_CheckKeyLength(key_id))
LOG(LOGS_WARN, "Key %"PRIu32" is %s", key_id, "too short");
return instance;
}
/* ================================================== */
NAU_Instance
NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name, const IPSockAddr *ntp_address)
{
NAU_Instance instance = create_instance(NTP_AUTH_NTS);
instance->nts = NNC_CreateInstance(nts_address, name, ntp_address);
return instance;
}
/* ================================================== */
void
NAU_DestroyInstance(NAU_Instance instance)
{
if (instance->nts)
NNC_DestroyInstance(instance->nts);
Free(instance);
}
/* ================================================== */
int
NAU_IsAuthEnabled(NAU_Instance instance)
{
return instance->mode != NTP_AUTH_NONE;
}
/* ================================================== */
int
NAU_GetSuggestedNtpVersion(NAU_Instance instance)
{
/* If the MAC in NTPv4 packets would be truncated, prefer NTPv3 for
compatibility with older chronyd servers */
if (instance->mode == NTP_AUTH_SYMMETRIC &&
KEY_GetAuthLength(instance->key_id) + sizeof (instance->key_id) > NTP_MAX_V4_MAC_LENGTH)
return 3;
return NTP_VERSION;
}
/* ================================================== */
int
NAU_PrepareRequestAuth(NAU_Instance instance)
{
switch (instance->mode) {
case NTP_AUTH_NTS:
if (!NNC_PrepareForAuth(instance->nts))
return 0;
break;
default:
break;
}
return 1;
}
/* ================================================== */
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)
{
switch (instance->mode) {
case NTP_AUTH_NONE:
break;
case NTP_AUTH_SYMMETRIC:
if (!generate_symmetric_auth(instance->key_id, request, info))
return 0;
break;
case NTP_AUTH_NTS:
if (!NNC_GenerateRequestAuth(instance->nts, request, info))
return 0;
break;
default:
assert(0);
}
return 1;
}
/* ================================================== */
int
NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info)
{
int parsed, remainder, ef_length, ef_type;
unsigned char *data;
data = (void *)packet;
parsed = NTP_HEADER_LENGTH;
remainder = info->length - parsed;
info->ext_fields = 0;
/* Check if this is a plain NTP packet with no extension fields or MAC */
if (remainder <= 0)
return 1;
/* In NTPv3 and older packets don't have extension fields. Anything after
the header is assumed to be a MAC. */
if (info->version <= 3) {
info->auth.mode = NTP_AUTH_SYMMETRIC;
info->auth.mac.start = parsed;
info->auth.mac.length = remainder;
info->auth.mac.key_id = ntohl(*(uint32_t *)(data + parsed));
/* Check if it is an MS-SNTP authenticator field or extended authenticator
field with zeroes as digest */
if (info->version == 3 && info->auth.mac.key_id) {
if (remainder == 20 && is_zero_data(data + parsed + 4, remainder - 4))
info->auth.mode = NTP_AUTH_MSSNTP;
else if (remainder == 72 && is_zero_data(data + parsed + 8, remainder - 8))
info->auth.mode = NTP_AUTH_MSSNTP_EXT;
}
return 1;
}
/* Check for a crypto NAK */
if (remainder == 4 && ntohl(*(uint32_t *)(data + parsed)) == 0) {
info->auth.mode = NTP_AUTH_SYMMETRIC;
info->auth.mac.start = parsed;
info->auth.mac.length = remainder;
info->auth.mac.key_id = 0;
return 1;
}
/* Parse the rest of the NTPv4 packet */
while (remainder > 0) {
/* Check if the remaining data is a MAC */
if (remainder >= NTP_MIN_MAC_LENGTH && remainder <= NTP_MAX_V4_MAC_LENGTH)
break;
/* The NTPv4-specific limit for MAC length enables deterministic parsing of
packets with extension fields (RFC 7822), but we support longer MACs in
packets with no extension fields for compatibility with older chrony
clients. Check if the longer MAC would authenticate the packet before
trying to parse the data as an extension field. */
if (parsed == NTP_HEADER_LENGTH &&
remainder > NTP_MAX_V4_MAC_LENGTH && remainder <= NTP_MAX_MAC_LENGTH &&
KEY_CheckAuth(ntohl(*(uint32_t *)(data + parsed)), data, parsed,
(void *)(data + parsed + 4), remainder - 4, NTP_MAX_MAC_LENGTH - 4))
break;
/* Check if this is a valid NTPv4 extension field and skip it */
if (!NEF_ParseField(packet, info->length, parsed, &ef_length, &ef_type, NULL, NULL)) {
/* Invalid MAC or format error */
DEBUG_LOG("Invalid format or MAC");
return 0;
}
assert(ef_length > 0);
switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
case NTP_EF_NTS_COOKIE:
case NTP_EF_NTS_COOKIE_PLACEHOLDER:
case NTP_EF_NTS_AUTH_AND_EEF:
info->auth.mode = NTP_AUTH_NTS;
break;
default:
DEBUG_LOG("Unknown extension field type=%x", (unsigned int)ef_type);
}
info->ext_fields++;
parsed += ef_length;
remainder = info->length - parsed;
}
if (remainder == 0) {
/* No MAC */
return 1;
} else if (remainder >= NTP_MIN_MAC_LENGTH) {
/* This is not 100% reliable as a MAC could fail to authenticate and could
pass as an extension field, leaving reminder smaller than the minimum MAC
length */
info->auth.mode = NTP_AUTH_SYMMETRIC;
info->auth.mac.start = parsed;
info->auth.mac.length = remainder;
info->auth.mac.key_id = ntohl(*(uint32_t *)(data + parsed));
return 1;
}
DEBUG_LOG("Invalid format");
return 0;
}
/* ================================================== */
int
NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod)
{
*kod = 0;
switch (info->auth.mode) {
case NTP_AUTH_NONE:
break;
case NTP_AUTH_SYMMETRIC:
if (!check_symmetric_auth(request, info))
return 0;
break;
case NTP_AUTH_MSSNTP:
/* MS-SNTP requests are not authenticated */
break;
case NTP_AUTH_NTS:
if (!NNS_CheckRequestAuth(request, info, kod))
return 0;
break;
default:
return 0;
}
return 1;
}
/* ================================================== */
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,
NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
uint32_t kod)
{
switch (request_info->auth.mode) {
case NTP_AUTH_NONE:
break;
case NTP_AUTH_SYMMETRIC:
if (!generate_symmetric_auth(request_info->auth.mac.key_id, response, response_info))
return 0;
break;
case NTP_AUTH_MSSNTP:
/* Sign the packet asynchronously by ntp_signd */
if (!NSD_SignAndSendPacket(request_info->auth.mac.key_id, response, response_info,
remote_addr, local_addr))
return 0;
/* Don't send the original packet */
return 0;
case NTP_AUTH_NTS:
if (!NNS_GenerateResponseAuth(request, request_info, response, response_info, kod))
return 0;
break;
default:
DEBUG_LOG("Could not authenticate response auth_mode=%d", (int)request_info->auth.mode);
return 0;
}
return 1;
}
/* ================================================== */
int
NAU_CheckResponseAuth(NAU_Instance instance, NTP_Packet *response, NTP_PacketInfo *info)
{
/* The authentication must match the expected mode */
if (info->auth.mode != instance->mode)
return 0;
switch (info->auth.mode) {
case NTP_AUTH_NONE:
break;
case NTP_AUTH_SYMMETRIC:
/* Check if it is authenticated with the specified key */
if (info->auth.mac.key_id != instance->key_id)
return 0;
/* and that the MAC is valid */
if (!check_symmetric_auth(response, info))
return 0;
break;
case NTP_AUTH_NTS:
if (!NNC_CheckResponseAuth(instance->nts, response, info))
return 0;
break;
default:
return 0;
}
return 1;
}
/* ================================================== */
void
NAU_ChangeAddress(NAU_Instance instance, IPAddr *address)
{
switch (instance->mode) {
case NTP_AUTH_NONE:
case NTP_AUTH_SYMMETRIC:
break;
case NTP_AUTH_NTS:
NNC_ChangeAddress(instance->nts, address);
break;
default:
assert(0);
}
}

89
ntp_auth.h Normal file
View File

@@ -0,0 +1,89 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2019
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header file for NTP authentication
*/
#ifndef GOT_NTP_AUTH_H
#define GOT_NTP_AUTH_H
#include "addressing.h"
#include "ntp.h"
typedef struct NAU_Instance_Record *NAU_Instance;
/* Create an authenticator instance in a specific mode */
extern NAU_Instance NAU_CreateNoneInstance(void);
extern NAU_Instance NAU_CreateSymmetricInstance(uint32_t key_id);
extern NAU_Instance NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name,
const IPSockAddr *ntp_address);
/* Destroy an instance */
extern void NAU_DestroyInstance(NAU_Instance instance);
/* Check if an instance is not in the None mode */
extern int NAU_IsAuthEnabled(NAU_Instance instance);
/* Get NTP version recommended for better compatibility */
extern int NAU_GetSuggestedNtpVersion(NAU_Instance instance);
/* Perform operations necessary for NAU_GenerateRequestAuth() */
extern int NAU_PrepareRequestAuth(NAU_Instance instance);
/* Adjust a transmit timestamp for an estimated minimum time it takes to call
NAU_GenerateRequestAuth() */
extern void NAU_AdjustRequestTimestamp(NAU_Instance instance, struct timespec *ts);
/* Extend a request with data required by the authentication mode */
extern int NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request,
NTP_PacketInfo *info);
/* Parse a request or response to detect the authentication mode */
extern int NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info);
/* Verify that a request is authentic. If it is not authentic and a non-zero
kod code is returned, a KoD response should be sent back. */
extern int NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod);
/* Adjust a transmit timestamp for an estimated minimum time it takes to call
NAU_GenerateResponseAuth() */
extern void NAU_AdjustResponseTimestamp(NTP_Packet *request, NTP_PacketInfo *info,
struct timespec *ts);
/* Extend a response with data required by the authentication mode. This
function can be called only if the previous call of NAU_CheckRequestAuth()
was on the same request. */
extern int NAU_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *request_info,
NTP_Packet *response, NTP_PacketInfo *response_info,
NTP_Remote_Address *remote_addr,
NTP_Local_Address *local_addr,
uint32_t kod);
/* Verify that a response is authentic */
extern int NAU_CheckResponseAuth(NAU_Instance instance, NTP_Packet *response,
NTP_PacketInfo *info);
/* Change an authentication-specific address (e.g. after replacing a source) */
extern void NAU_ChangeAddress(NAU_Instance instance, IPAddr *address);
#endif

1842
ntp_core.c

File diff suppressed because it is too large Load Diff

View File

@@ -38,6 +38,18 @@ typedef enum {
NTP_SERVER, NTP_PEER
} NTP_Source_Type;
typedef enum {
NTP_TS_DAEMON = 0,
NTP_TS_KERNEL,
NTP_TS_HARDWARE
} NTP_Timestamp_Source;
typedef struct {
struct timespec ts;
double err;
NTP_Timestamp_Source source;
} NTP_Local_Timestamp;
/* This is a private data type used for storing the instance record for
each source that we are chiming with */
typedef struct NCR_Instance_Record *NCR_Instance;
@@ -47,7 +59,8 @@ extern void NCR_Initialise(void);
extern void NCR_Finalise(void);
/* Get a new instance for a server or peer */
extern NCR_Instance NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params);
extern NCR_Instance NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, const char *name);
/* Destroy an instance */
extern void NCR_DestroyInstance(NCR_Instance instance);
@@ -58,26 +71,39 @@ extern void NCR_StartInstance(NCR_Instance instance);
/* Reset an instance */
extern void NCR_ResetInstance(NCR_Instance inst);
/* Reset polling interval of an instance */
extern void NCR_ResetPoll(NCR_Instance instance);
/* Change the remote address of an instance */
extern void NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr);
extern void NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr,
int ntp_only);
/* This routine is called when a new packet arrives off the network,
and it relates to a source we have an ongoing protocol exchange with */
extern int NCR_ProcessKnown(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance data, NTP_Local_Address *local_addr, int length);
extern int NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length);
/* This routine is called when a new packet arrives off the network,
and we do not recognize its source */
extern void NCR_ProcessUnknown(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
extern void NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length);
/* This routine is called when a packet is sent to a source we have
an ongoing protocol exchange with */
extern void NCR_ProcessTxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length);
/* This routine is called when a packet is sent to a destination we
do not recognize */
extern void NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length);
/* Slew receive and transmit times in instance records */
extern void NCR_SlewTimes(NCR_Instance inst, struct timeval *when, double dfreq, double doffset);
extern void NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double doffset);
/* Take a particular source online (i.e. start sampling it) */
extern void NCR_TakeSourceOnline(NCR_Instance inst);
/* Take a particular source offline (i.e. stop sampling it, without
marking it unreachable in the source selection stuff) */
extern void NCR_TakeSourceOffline(NCR_Instance inst);
/* Take a particular source online (i.e. start sampling it) or offline
(i.e. stop sampling it) */
extern void NCR_SetConnectivity(NCR_Instance inst, SRC_Connectivity connectivity);
extern void NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll);
@@ -95,7 +121,8 @@ 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 timeval *now);
extern void NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timespec *now);
extern void NCR_GetNTPReport(NCR_Instance inst, RPT_NTPReport *report);
extern int NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all);
extern int NCR_CheckAccessRestriction(IPAddr *ip_addr);
@@ -105,6 +132,8 @@ extern void NCR_IncrementActivityCounters(NCR_Instance inst, int *online, int *o
extern NTP_Remote_Address *NCR_GetRemoteAddress(NCR_Instance instance);
extern uint32_t NCR_GetLocalRefid(NCR_Instance inst);
extern int NCR_IsSyncPeer(NCR_Instance instance);
extern void NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval);

192
ntp_ext.c Normal file
View File

@@ -0,0 +1,192 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2019
*
* 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.
*
**********************************************************************
=======================================================================
Functions for adding and parsing NTPv4 extension fields
*/
#include "config.h"
#include "sysincl.h"
#include "ntp_ext.h"
struct ExtFieldHeader {
uint16_t type;
uint16_t length;
};
/* ================================================== */
static int
format_field(unsigned char *buffer, int buffer_length, int start,
int type, int body_length, int *length, void **body)
{
struct ExtFieldHeader *header;
if (buffer_length < 0 || start < 0 || buffer_length <= start ||
buffer_length - start < sizeof (*header) || start % 4 != 0)
return 0;
header = (struct ExtFieldHeader *)(buffer + start);
if (body_length < 0 || sizeof (*header) + body_length > 0xffff ||
start + sizeof (*header) + body_length > buffer_length || body_length % 4 != 0)
return 0;
header->type = htons(type);
header->length = htons(sizeof (*header) + body_length);
*length = sizeof (*header) + body_length;
*body = header + 1;
return 1;
}
/* ================================================== */
int
NEF_SetField(unsigned char *buffer, int buffer_length, int start,
int type, void *body, int body_length, int *length)
{
void *ef_body;
if (!format_field(buffer, buffer_length, start, type, body_length, length, &ef_body))
return 0;
memcpy(ef_body, body, body_length);
return 1;
}
/* ================================================== */
int
NEF_AddBlankField(NTP_Packet *packet, NTP_PacketInfo *info, int type, int body_length, void **body)
{
int ef_length, length = info->length;
if (length < NTP_HEADER_LENGTH || length >= sizeof (*packet) || length % 4 != 0)
return 0;
/* Only NTPv4 packets can have extension fields */
if (info->version != 4)
return 0;
if (!format_field((unsigned char *)packet, sizeof (*packet), length,
type, body_length, &ef_length, body))
return 0;
if (ef_length < NTP_MIN_EF_LENGTH)
return 0;
info->length += ef_length;
info->ext_fields++;
return 1;
}
/* ================================================== */
int
NEF_AddField(NTP_Packet *packet, NTP_PacketInfo *info,
int type, void *body, int body_length)
{
void *ef_body;
if (!NEF_AddBlankField(packet, info, type, body_length, &ef_body))
return 0;
memcpy(ef_body, body, body_length);
return 1;
}
/* ================================================== */
int
NEF_ParseSingleField(unsigned char *buffer, int buffer_length, int start,
int *length, int *type, void **body, int *body_length)
{
struct ExtFieldHeader *header;
int ef_length;
if (buffer_length < 0 || start < 0 || buffer_length <= start ||
buffer_length - start < sizeof (*header))
return 0;
header = (struct ExtFieldHeader *)(buffer + start);
assert(sizeof (*header) == 4);
ef_length = ntohs(header->length);
if (ef_length < (int)(sizeof (*header)) || start + ef_length > buffer_length ||
ef_length % 4 != 0)
return 0;
if (length)
*length = ef_length;
if (type)
*type = ntohs(header->type);
if (body)
*body = header + 1;
if (body_length)
*body_length = ef_length - sizeof (*header);
return 1;
}
/* ================================================== */
int
NEF_ParseField(NTP_Packet *packet, int packet_length, int start,
int *length, int *type, void **body, int *body_length)
{
int ef_length;
if (packet_length <= NTP_HEADER_LENGTH || packet_length > sizeof (*packet) ||
packet_length <= start || packet_length % 4 != 0 ||
start < NTP_HEADER_LENGTH || start % 4 != 0)
return 0;
/* Only NTPv4 packets have extension fields */
if (NTP_LVM_TO_VERSION(packet->lvm) != 4)
return 0;
/* Check if the remaining data is a MAC. RFC 7822 specifies the maximum
length of a MAC in NTPv4 packets in order to enable deterministic
parsing. */
if (packet_length - start <= NTP_MAX_V4_MAC_LENGTH)
return 0;
if (!NEF_ParseSingleField((unsigned char *)packet, packet_length, start,
&ef_length, type, body, body_length))
return 0;
if (ef_length < NTP_MIN_EF_LENGTH)
return 0;
if (length)
*length = ef_length;
return 1;
}

43
ntp_ext.h Normal file
View File

@@ -0,0 +1,43 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2019
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header file for NTP extension fields
*/
#ifndef GOT_NTP_EXT_H
#define GOT_NTP_EXT_H
#include "ntp.h"
extern int NEF_SetField(unsigned char *buffer, int buffer_length, int start,
int type, void *body, int body_length, int *length);
extern int NEF_AddBlankField(NTP_Packet *packet, NTP_PacketInfo *info, int type,
int body_length, void **body);
extern int NEF_AddField(NTP_Packet *packet, NTP_PacketInfo *info,
int type, void *body, int body_length);
extern int NEF_ParseSingleField(unsigned char *buffer, int buffer_length, int start,
int *length, int *type, void **body, int *body_length);
extern int NEF_ParseField(NTP_Packet *packet, int packet_length, int start,
int *length, int *type, void **body, int *body_length);
#endif

646
ntp_io.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Timo Teras 2009
* Copyright (C) Miroslav Lichvar 2009, 2013-2015
* Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018
*
* 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
@@ -34,34 +34,28 @@
#include "ntp_core.h"
#include "ntp_sources.h"
#include "sched.h"
#include "socket.h"
#include "local.h"
#include "logging.h"
#include "conf.h"
#include "privops.h"
#include "util.h"
#ifdef HAVE_LINUX_TIMESTAMPING
#include "ntp_io_linux.h"
#endif
#define INVALID_SOCK_FD -1
union sockaddr_in46 {
struct sockaddr_in in4;
#ifdef FEAT_IPV6
struct sockaddr_in6 in6;
#endif
struct sockaddr u;
};
/* The server/peer and client sockets for IPv4 and IPv6 */
static int server_sock_fd4;
static int client_sock_fd4;
#ifdef FEAT_IPV6
static int server_sock_fd6;
static int client_sock_fd4;
static int client_sock_fd6;
#endif
/* Reference counters for server sockets to keep them open only when needed */
static int server_sock_ref4;
#ifdef FEAT_IPV6
static int server_sock_ref6;
#endif
/* Flag indicating we create a new connected client socket for each
server instead of sharing client_sock_fd4 and client_sock_fd6 */
@@ -73,161 +67,61 @@ static int separate_client_sockets;
disabled */
static int permanent_server_sockets;
/* Flag indicating the server IPv4 socket is bound to an address */
static int bound_server_sock_fd4;
/* Flag indicating that we have been initialised */
static int initialised=0;
/* ================================================== */
/* Forward prototypes */
static void read_from_socket(void *anything);
static void read_from_socket(int sock_fd, int event, void *anything);
/* ================================================== */
static int
prepare_socket(int family, int port_number, int client_only)
open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr)
{
union sockaddr_in46 my_addr;
socklen_t my_addr_len;
int sock_fd;
IPAddr bind_address;
int on_off = 1;
int sock_fd, sock_flags, events = SCH_FILE_INPUT;
IPSockAddr local_addr;
/* Open Internet domain UDP socket for NTP message transmissions */
if (!SCK_IsFamilySupported(family))
return INVALID_SOCK_FD;
sock_fd = socket(family, SOCK_DGRAM, 0);
if (!client_only)
CNF_GetBindAddress(family, &local_addr.ip_addr);
else
CNF_GetBindAcquisitionAddress(family, &local_addr.ip_addr);
if (local_addr.ip_addr.family != family)
SCK_GetAnyLocalIPAddress(family, &local_addr.ip_addr);
local_addr.port = local_port;
sock_flags = SCK_FLAG_RX_DEST_ADDR | SCK_FLAG_PRIV_BIND;
if (!client_only)
sock_flags |= SCK_FLAG_BROADCAST;
sock_fd = SCK_OpenUdpSocket(remote_addr, &local_addr, sock_flags);
if (sock_fd < 0) {
if (!client_only) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not open %s NTP socket : %s",
family == AF_INET ? "IPv4" : "IPv6", strerror(errno));
} else {
DEBUG_LOG(LOGF_NtpIO, "Could not open %s NTP socket : %s",
family == AF_INET ? "IPv4" : "IPv6", strerror(errno));
}
if (!client_only)
LOG(LOGS_ERR, "Could not open NTP socket on %s", UTI_IPSockAddrToString(&local_addr));
return INVALID_SOCK_FD;
}
/* Close on exec */
UTI_FdSetCloexec(sock_fd);
if (!client_only && family == IPADDR_INET4 && local_addr.port > 0)
bound_server_sock_fd4 = local_addr.ip_addr.addr.in4 != INADDR_ANY;
/* Prepare local address */
memset(&my_addr, 0, sizeof (my_addr));
my_addr_len = 0;
switch (family) {
case AF_INET:
if (!client_only)
CNF_GetBindAddress(IPADDR_INET4, &bind_address);
else
CNF_GetBindAcquisitionAddress(IPADDR_INET4, &bind_address);
if (bind_address.family == IPADDR_INET4)
my_addr.in4.sin_addr.s_addr = htonl(bind_address.addr.in4);
else if (port_number)
my_addr.in4.sin_addr.s_addr = htonl(INADDR_ANY);
else
break;
my_addr.in4.sin_family = family;
my_addr.in4.sin_port = htons(port_number);
my_addr_len = sizeof (my_addr.in4);
break;
#ifdef FEAT_IPV6
case AF_INET6:
if (!client_only)
CNF_GetBindAddress(IPADDR_INET6, &bind_address);
else
CNF_GetBindAcquisitionAddress(IPADDR_INET6, &bind_address);
if (bind_address.family == IPADDR_INET6)
memcpy(my_addr.in6.sin6_addr.s6_addr, bind_address.addr.in6,
sizeof (my_addr.in6.sin6_addr.s6_addr));
else if (port_number)
my_addr.in6.sin6_addr = in6addr_any;
else
break;
my_addr.in6.sin6_family = family;
my_addr.in6.sin6_port = htons(port_number);
my_addr_len = sizeof (my_addr.in6);
break;
/* Enable kernel/HW timestamping of packets */
#ifdef HAVE_LINUX_TIMESTAMPING
if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, client_only, &events))
#endif
default:
assert(0);
}
if (!SCK_EnableKernelRxTimestamping(sock_fd))
;
/* Make the socket capable of re-using an old address if binding to a specific port */
if (port_number &&
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set reuseaddr socket options");
/* Don't quit - we might survive anyway */
}
/* Make the socket capable of sending broadcast pkts - needed for NTP broadcast mode */
if (!client_only &&
setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set broadcast socket options");
/* Don't quit - we might survive anyway */
}
#ifdef SO_TIMESTAMP
/* Enable receiving of timestamp control messages */
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set timestamp socket options");
/* Don't quit - we might survive anyway */
}
#endif
#ifdef IP_FREEBIND
/* Allow binding to address that doesn't exist yet */
if (my_addr_len > 0 &&
setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set free bind socket option");
}
#endif
if (family == AF_INET) {
#ifdef IP_PKTINFO
/* We want the local IP info on server sockets */
if (setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set packet info socket option");
/* Don't quit - we might survive anyway */
}
#endif
}
#ifdef FEAT_IPV6
else if (family == AF_INET6) {
#ifdef IPV6_V6ONLY
/* Receive IPv6 packets only */
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPV6_V6ONLY socket option");
}
#endif
#ifdef IPV6_RECVPKTINFO
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPv6 packet info socket option");
}
#elif defined(IPV6_PKTINFO)
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPv6 packet info socket option");
}
#endif
}
#endif
/* Bind the socket if a port or address was specified */
if (my_addr_len > 0 && bind(sock_fd, &my_addr.u, my_addr_len) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not bind %s NTP socket : %s",
family == AF_INET ? "IPv4" : "IPv6", strerror(errno));
close(sock_fd);
return INVALID_SOCK_FD;
}
/* Register handler for read events on the socket */
SCH_AddInputFileHandler(sock_fd, read_from_socket, (void *)(long)sock_fd);
/* Register handler for read and possibly exception events on the socket */
SCH_AddFileHandler(sock_fd, events, read_from_socket, NULL);
return sock_fd;
}
@@ -235,40 +129,9 @@ prepare_socket(int family, int port_number, int client_only)
/* ================================================== */
static int
prepare_separate_client_socket(int family)
open_separate_client_socket(IPSockAddr *remote_addr)
{
switch (family) {
case IPADDR_INET4:
return prepare_socket(AF_INET, 0, 1);
#ifdef FEAT_IPV6
case IPADDR_INET6:
return prepare_socket(AF_INET6, 0, 1);
#endif
default:
return INVALID_SOCK_FD;
}
}
/* ================================================== */
static int
connect_socket(int sock_fd, NTP_Remote_Address *remote_addr)
{
union sockaddr_in46 addr;
socklen_t addr_len;
addr_len = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port, &addr.u);
assert(addr_len);
if (connect(sock_fd, &addr.u, addr_len) < 0) {
DEBUG_LOG(LOGF_NtpIO, "Could not connect NTP socket to %s:%d : %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
strerror(errno));
return 0;
}
return 1;
return open_socket(remote_addr->ip_addr.family, 0, 1, remote_addr);
}
/* ================================================== */
@@ -279,11 +142,15 @@ close_socket(int sock_fd)
if (sock_fd == INVALID_SOCK_FD)
return;
SCH_RemoveInputFileHandler(sock_fd);
close(sock_fd);
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_NotifySocketClosing(sock_fd);
#endif
SCH_RemoveFileHandler(sock_fd);
SCK_CloseSocket(sock_fd);
}
/* ================================================== */
void
NIO_Initialise(int family)
{
@@ -292,6 +159,20 @@ NIO_Initialise(int family)
assert(!initialised);
initialised = 1;
#ifdef PRIVOPS_BINDSOCKET
SCK_SetPrivBind(PRV_BindSocket);
#endif
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_Initialise();
#else
if (1) {
CNF_HwTsInterface *conf_iface;
if (CNF_GetHwTsInterface(0, &conf_iface))
LOG_FATAL("HW timestamping not supported");
}
#endif
server_port = CNF_GetNTPPort();
client_port = CNF_GetAcquisitionPort();
@@ -304,48 +185,39 @@ NIO_Initialise(int family)
client_port == server_port);
server_sock_fd4 = INVALID_SOCK_FD;
client_sock_fd4 = INVALID_SOCK_FD;
server_sock_ref4 = 0;
#ifdef FEAT_IPV6
server_sock_fd6 = INVALID_SOCK_FD;
client_sock_fd4 = INVALID_SOCK_FD;
client_sock_fd6 = INVALID_SOCK_FD;
server_sock_ref4 = 0;
server_sock_ref6 = 0;
#endif
if (family == IPADDR_UNSPEC || family == IPADDR_INET4) {
if (permanent_server_sockets && server_port)
server_sock_fd4 = prepare_socket(AF_INET, server_port, 0);
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 = prepare_socket(AF_INET, client_port, 1);
client_sock_fd4 = open_socket(IPADDR_INET4, client_port, 1, NULL);
else
client_sock_fd4 = server_sock_fd4;
}
}
#ifdef FEAT_IPV6
if (family == IPADDR_UNSPEC || family == IPADDR_INET6) {
if (permanent_server_sockets && server_port)
server_sock_fd6 = prepare_socket(AF_INET6, server_port, 0);
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 = prepare_socket(AF_INET6, client_port, 1);
client_sock_fd6 = open_socket(IPADDR_INET6, client_port, 1, NULL);
else
client_sock_fd6 = server_sock_fd6;
}
}
#endif
if ((server_port && server_sock_fd4 == INVALID_SOCK_FD &&
permanent_server_sockets
#ifdef FEAT_IPV6
&& server_sock_fd6 == INVALID_SOCK_FD
#endif
) || (!separate_client_sockets && client_sock_fd4 == INVALID_SOCK_FD
#ifdef FEAT_IPV6
&& client_sock_fd6 == INVALID_SOCK_FD
#endif
)) {
LOG_FATAL(LOGF_NtpIO, "Could not open NTP sockets");
if ((server_port && permanent_server_sockets &&
server_sock_fd4 == INVALID_SOCK_FD && server_sock_fd6 == INVALID_SOCK_FD) ||
(!separate_client_sockets &&
client_sock_fd4 == INVALID_SOCK_FD && client_sock_fd6 == INVALID_SOCK_FD)) {
LOG_FATAL("Could not open NTP sockets");
}
}
@@ -358,12 +230,16 @@ NIO_Finalise(void)
close_socket(client_sock_fd4);
close_socket(server_sock_fd4);
server_sock_fd4 = client_sock_fd4 = INVALID_SOCK_FD;
#ifdef FEAT_IPV6
if (server_sock_fd6 != client_sock_fd6)
close_socket(client_sock_fd6);
close_socket(server_sock_fd6);
server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD;
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_Finalise();
#endif
initialised = 0;
}
@@ -373,25 +249,13 @@ int
NIO_OpenClientSocket(NTP_Remote_Address *remote_addr)
{
if (separate_client_sockets) {
int sock_fd = prepare_separate_client_socket(remote_addr->ip_addr.family);
if (sock_fd == INVALID_SOCK_FD)
return INVALID_SOCK_FD;
if (!connect_socket(sock_fd, remote_addr)) {
close_socket(sock_fd);
return INVALID_SOCK_FD;
}
return sock_fd;
return open_separate_client_socket(remote_addr);
} else {
switch (remote_addr->ip_addr.family) {
case IPADDR_INET4:
return client_sock_fd4;
#ifdef FEAT_IPV6
case IPADDR_INET6:
return client_sock_fd6;
#endif
default:
return INVALID_SOCK_FD;
}
@@ -408,20 +272,18 @@ NIO_OpenServerSocket(NTP_Remote_Address *remote_addr)
if (permanent_server_sockets)
return server_sock_fd4;
if (server_sock_fd4 == INVALID_SOCK_FD)
server_sock_fd4 = prepare_socket(AF_INET, CNF_GetNTPPort(), 0);
server_sock_fd4 = open_socket(IPADDR_INET4, CNF_GetNTPPort(), 0, NULL);
if (server_sock_fd4 != INVALID_SOCK_FD)
server_sock_ref4++;
return server_sock_fd4;
#ifdef FEAT_IPV6
case IPADDR_INET6:
if (permanent_server_sockets)
return server_sock_fd6;
if (server_sock_fd6 == INVALID_SOCK_FD)
server_sock_fd6 = prepare_socket(AF_INET6, CNF_GetNTPPort(), 0);
server_sock_fd6 = open_socket(IPADDR_INET6, CNF_GetNTPPort(), 0, NULL);
if (server_sock_fd6 != INVALID_SOCK_FD)
server_sock_ref6++;
return server_sock_fd6;
#endif
default:
return INVALID_SOCK_FD;
}
@@ -449,16 +311,12 @@ NIO_CloseServerSocket(int sock_fd)
close_socket(server_sock_fd4);
server_sock_fd4 = INVALID_SOCK_FD;
}
}
#ifdef FEAT_IPV6
else if (sock_fd == server_sock_fd6) {
} else if (sock_fd == server_sock_fd6) {
if (--server_sock_ref6 <= 0) {
close_socket(server_sock_fd6);
server_sock_fd6 = INVALID_SOCK_FD;
}
}
#endif
else {
} else {
assert(0);
}
}
@@ -469,228 +327,154 @@ int
NIO_IsServerSocket(int sock_fd)
{
return sock_fd != INVALID_SOCK_FD &&
(sock_fd == server_sock_fd4
#ifdef FEAT_IPV6
|| sock_fd == server_sock_fd6
#endif
);
(sock_fd == server_sock_fd4 || sock_fd == server_sock_fd6);
}
/* ================================================== */
static void
read_from_socket(void *anything)
int
NIO_IsServerSocketOpen(void)
{
/* This should only be called when there is something
to read, otherwise it will block. */
int status, sock_fd;
NTP_Receive_Buffer message;
union sockaddr_in46 where_from;
unsigned int flags = 0;
struct timeval now;
double now_err;
NTP_Remote_Address remote_addr;
NTP_Local_Address local_addr;
char cmsgbuf[256];
struct msghdr msg;
struct iovec iov;
struct cmsghdr *cmsg;
assert(initialised);
SCH_GetLastEventTime(&now, &now_err, NULL);
iov.iov_base = &message.ntp_pkt;
iov.iov_len = sizeof(message);
msg.msg_name = &where_from;
msg.msg_namelen = sizeof(where_from);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = (void *) cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
msg.msg_flags = 0;
sock_fd = (long)anything;
status = recvmsg(sock_fd, &msg, flags);
/* Don't bother checking if read failed or why if it did. More
likely than not, it will be connection refused, resulting from a
previous sendto() directing a datagram at a port that is not
listening (which appears to generate an ICMP response, and on
some architectures e.g. Linux this is translated into an error
reponse on a subsequent recvfrom). */
if (status > 0) {
if (msg.msg_namelen > sizeof (where_from))
LOG_FATAL(LOGF_NtpIO, "Truncated source address");
UTI_SockaddrToIPAndPort(&where_from.u, &remote_addr.ip_addr, &remote_addr.port);
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.sock_fd = sock_fd;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
#ifdef IP_PKTINFO
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
struct in_pktinfo ipi;
memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_spec_dst.s_addr);
local_addr.ip_addr.family = IPADDR_INET4;
}
#endif
#if defined(IPV6_PKTINFO) && defined(HAVE_IN6_PKTINFO)
if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
struct in6_pktinfo ipi;
memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr,
sizeof (local_addr.ip_addr.addr.in6));
local_addr.ip_addr.family = IPADDR_INET6;
}
#endif
#ifdef SO_TIMESTAMP
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMP) {
struct timeval tv;
memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));
LCL_CookTime(&tv, &now, &now_err);
}
#endif
}
DEBUG_LOG(LOGF_NtpIO, "Received %d bytes from %s:%d to %s fd %d",
status, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd);
if (status >= NTP_NORMAL_PACKET_LENGTH) {
NSR_ProcessReceive((NTP_Packet *) &message.ntp_pkt, &now, now_err,
&remote_addr, &local_addr, status);
} else {
/* Just ignore the packet if it's not of a recognized length */
}
}
return server_sock_fd4 != INVALID_SOCK_FD || server_sock_fd6 != INVALID_SOCK_FD;
}
/* ================================================== */
/* Send a packet to remote address from local address */
static int
send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
int
NIO_IsServerConnectable(NTP_Remote_Address *remote_addr)
{
union sockaddr_in46 remote;
struct msghdr msg;
struct iovec iov;
char cmsgbuf[256];
int cmsglen;
socklen_t addrlen = 0;
int sock_fd;
assert(initialised);
if (local_addr->sock_fd == INVALID_SOCK_FD) {
DEBUG_LOG(LOGF_NtpIO, "No socket to send to %s:%d",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port);
sock_fd = open_separate_client_socket(remote_addr);
if (sock_fd == INVALID_SOCK_FD)
return 0;
}
/* Don't set address with connected socket */
if (local_addr->sock_fd == server_sock_fd4 ||
#ifdef FEAT_IPV6
local_addr->sock_fd == server_sock_fd6 ||
#endif
!separate_client_sockets) {
addrlen = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port,
&remote.u);
if (!addrlen)
return 0;
}
if (addrlen) {
msg.msg_name = &remote.u;
msg.msg_namelen = addrlen;
} else {
msg.msg_name = NULL;
msg.msg_namelen = 0;
}
iov.iov_base = packet;
iov.iov_len = packetlen;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
msg.msg_flags = 0;
cmsglen = 0;
#ifdef IP_PKTINFO
if (local_addr->ip_addr.family == IPADDR_INET4) {
struct cmsghdr *cmsg;
struct in_pktinfo *ipi;
cmsg = CMSG_FIRSTHDR(&msg);
memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_pktinfo)));
cmsglen += CMSG_SPACE(sizeof(struct in_pktinfo));
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
ipi = (struct in_pktinfo *) CMSG_DATA(cmsg);
ipi->ipi_spec_dst.s_addr = htonl(local_addr->ip_addr.addr.in4);
}
#endif
#if defined(IPV6_PKTINFO) && defined(HAVE_IN6_PKTINFO)
if (local_addr->ip_addr.family == IPADDR_INET6) {
struct cmsghdr *cmsg;
struct in6_pktinfo *ipi;
cmsg = CMSG_FIRSTHDR(&msg);
memset(cmsg, 0, CMSG_SPACE(sizeof(struct in6_pktinfo)));
cmsglen += CMSG_SPACE(sizeof(struct in6_pktinfo));
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
ipi = (struct in6_pktinfo *) CMSG_DATA(cmsg);
memcpy(&ipi->ipi6_addr.s6_addr, &local_addr->ip_addr.addr.in6,
sizeof(ipi->ipi6_addr.s6_addr));
}
#endif
msg.msg_controllen = cmsglen;
/* This is apparently required on some systems */
if (!cmsglen)
msg.msg_control = NULL;
if (sendmsg(local_addr->sock_fd, &msg, 0) < 0) {
DEBUG_LOG(LOGF_NtpIO, "Could not send to %s:%d from %s fd %d : %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd,
strerror(errno));
return 0;
}
DEBUG_LOG(LOGF_NtpIO, "Sent %d bytes to %s:%d from %s fd %d", packetlen,
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd);
close_socket(sock_fd);
return 1;
}
/* ================================================== */
/* Send a packet to a given address */
static void
process_message(SCK_Message *message, int sock_fd, int event)
{
NTP_Local_Address local_addr;
NTP_Local_Timestamp local_ts;
struct timespec sched_ts;
SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL);
local_ts.source = NTP_TS_DAEMON;
sched_ts = local_ts.ts;
if (message->addr_type != SCK_ADDR_IP) {
DEBUG_LOG("Unexpected address type");
return;
}
local_addr.ip_addr = message->local_addr.ip;
local_addr.if_index = message->if_index;;
local_addr.sock_fd = sock_fd;
#ifdef HAVE_LINUX_TIMESTAMPING
if (NIO_Linux_ProcessMessage(message, &local_addr, &local_ts, event))
return;
#else
if (!UTI_IsZeroTimespec(&message->timestamp.kernel)) {
LCL_CookTime(&message->timestamp.kernel, &local_ts.ts, &local_ts.err);
local_ts.source = NTP_TS_KERNEL;
}
#endif
if (local_ts.source != NTP_TS_DAEMON)
DEBUG_LOG("Updated RX timestamp delay=%.9f tss=%u",
UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts), local_ts.source);
/* Just ignore the packet if it's not of a recognized length */
if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet)) {
DEBUG_LOG("Unexpected length");
return;
}
NSR_ProcessRx(&message->remote_addr.ip, &local_addr, &local_ts, message->data, message->length);
}
/* ================================================== */
static void
read_from_socket(int sock_fd, int event, void *anything)
{
/* This should only be called when there is something
to read, otherwise it may block */
SCK_Message messages[SCK_MAX_RECV_MESSAGES];
int i, received, flags = 0;
#ifdef HAVE_LINUX_TIMESTAMPING
if (NIO_Linux_ProcessEvent(sock_fd, event))
return;
#endif
if (event == SCH_FILE_EXCEPTION) {
#ifdef HAVE_LINUX_TIMESTAMPING
flags |= SCK_FLAG_MSG_ERRQUEUE;
#else
assert(0);
#endif
}
received = SCK_ReceiveMessages(sock_fd, messages, SCK_MAX_RECV_MESSAGES, flags);
if (received <= 0)
return;
for (i = 0; i < received; i++)
process_message(&messages[i], sock_fd, event);
}
/* ================================================== */
/* Send a packet to remote address from local address */
int
NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
NTP_Local_Address *local_addr, int length, int process_tx)
{
return send_packet((void *) packet, length, remote_addr, local_addr);
SCK_Message message;
assert(initialised);
if (local_addr->sock_fd == INVALID_SOCK_FD) {
DEBUG_LOG("No socket to send to %s", UTI_IPSockAddrToString(remote_addr));
return 0;
}
SCK_InitMessage(&message, SCK_ADDR_IP);
message.data = packet;
message.length = length;
/* Specify remote address if the socket is not connected */
if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) {
message.remote_addr.ip.ip_addr = remote_addr->ip_addr;
message.remote_addr.ip.port = remote_addr->port;
}
message.if_index = local_addr->if_index;
message.local_addr.ip = local_addr->ip_addr;
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
if (message.local_addr.ip.family == IPADDR_INET4 &&
(local_addr->sock_fd != server_sock_fd4 || bound_server_sock_fd4))
message.local_addr.ip.family = IPADDR_UNSPEC;
#endif
#ifdef HAVE_LINUX_TIMESTAMPING
if (process_tx)
NIO_Linux_RequestTxTimestamp(&message, local_addr->sock_fd);
#endif
if (!SCK_SendMessage(local_addr->sock_fd, &message, 0))
return 0;
return 1;
}

View File

@@ -53,7 +53,14 @@ extern void NIO_CloseServerSocket(int sock_fd);
/* Function to check if socket is a server socket */
extern int NIO_IsServerSocket(int sock_fd);
/* Function to check if a server socket is currently open */
extern int NIO_IsServerSocketOpen(void);
/* Function to check if client packets can be sent to a server */
extern int NIO_IsServerConnectable(NTP_Remote_Address *remote_addr);
/* Function to transmit a packet */
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
NTP_Local_Address *local_addr, int length, int process_tx);
#endif /* GOT_NTP_IO_H */

813
ntp_io_linux.c Normal file
View File

@@ -0,0 +1,813 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016-2019
*
* 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.
*
**********************************************************************
=======================================================================
Functions for NTP I/O specific to Linux
*/
#include "config.h"
#include "sysincl.h"
#include <ifaddrs.h>
#include <linux/ethtool.h>
#include <linux/net_tstamp.h>
#include <linux/sockios.h>
#include <net/if.h>
#include "array.h"
#include "conf.h"
#include "hwclock.h"
#include "local.h"
#include "logging.h"
#include "ntp_core.h"
#include "ntp_io.h"
#include "ntp_io_linux.h"
#include "ntp_sources.h"
#include "sched.h"
#include "socket.h"
#include "sys_linux.h"
#include "util.h"
struct Interface {
char name[IF_NAMESIZE];
int if_index;
int phc_fd;
int phc_mode;
int phc_nocrossts;
/* Link speed in mbit/s */
int link_speed;
/* Start of UDP data at layer 2 for IPv4 and IPv6 */
int l2_udp4_ntp_start;
int l2_udp6_ntp_start;
/* Precision of PHC readings */
double precision;
/* Compensation of errors in TX and RX timestamping */
double tx_comp;
double rx_comp;
HCL_Instance clock;
};
/* Number of PHC readings per HW clock sample */
#define PHC_READINGS 10
/* Minimum interval between PHC readings */
#define MIN_PHC_POLL -6
/* Maximum acceptable offset between HW and daemon/kernel timestamp */
#define MAX_TS_DELAY 1.0
/* Array of Interfaces */
static ARR_Instance interfaces;
/* RX/TX and TX-specific timestamping socket options */
static int ts_flags;
static int ts_tx_flags;
/* Flag indicating the socket options can't be changed in control messages */
static int permanent_ts_options;
/* When sending client requests to a close and fast server, it is possible that
a response will be received before the HW transmit timestamp of the request
itself. To avoid processing of the response without the HW timestamp, we
monitor events returned by select() and suspend reading of packets from the
receive queue for up to 200 microseconds. As the requests are normally
separated by at least 200 milliseconds, it is sufficient to monitor and
suspend one socket at a time. */
static int monitored_socket;
static int suspended_socket;
static SCH_TimeoutID resume_timeout_id;
#define RESUME_TIMEOUT 200.0e-6
/* Unbound socket keeping the kernel RX timestamping permanently enabled
in order to avoid a race condition between receiving a server response
and the kernel actually starting to timestamp received packets after
enabling the timestamping and sending a request */
static int dummy_rxts_socket;
#define INVALID_SOCK_FD -3
/* ================================================== */
static int
add_interface(CNF_HwTsInterface *conf_iface)
{
struct ethtool_ts_info ts_info;
struct hwtstamp_config ts_config;
struct ifreq req;
int sock_fd, if_index, phc_fd, req_hwts_flags, rx_filter;
unsigned int i;
struct Interface *iface;
/* Check if the interface was not already added */
for (i = 0; i < ARR_GetSize(interfaces); i++) {
if (!strcmp(conf_iface->name, ((struct Interface *)ARR_GetElement(interfaces, i))->name))
return 1;
}
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
if (sock_fd < 0)
return 0;
memset(&req, 0, sizeof (req));
memset(&ts_info, 0, sizeof (ts_info));
if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", conf_iface->name) >=
sizeof (req.ifr_name)) {
SCK_CloseSocket(sock_fd);
return 0;
}
if (ioctl(sock_fd, SIOCGIFINDEX, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCGIFINDEX", strerror(errno));
SCK_CloseSocket(sock_fd);
return 0;
}
if_index = req.ifr_ifindex;
ts_info.cmd = ETHTOOL_GET_TS_INFO;
req.ifr_data = (char *)&ts_info;
if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
SCK_CloseSocket(sock_fd);
return 0;
}
req_hwts_flags = SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
if ((ts_info.so_timestamping & req_hwts_flags) != req_hwts_flags) {
DEBUG_LOG("HW timestamping not supported on %s", req.ifr_name);
SCK_CloseSocket(sock_fd);
return 0;
}
if (ts_info.phc_index < 0) {
DEBUG_LOG("PHC missing on %s", req.ifr_name);
SCK_CloseSocket(sock_fd);
return 0;
}
switch (conf_iface->rxfilter) {
case CNF_HWTS_RXFILTER_ANY:
#ifdef HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP
if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_NTP_ALL))
rx_filter = HWTSTAMP_FILTER_NTP_ALL;
else
#endif
if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_ALL))
rx_filter = HWTSTAMP_FILTER_ALL;
else
rx_filter = HWTSTAMP_FILTER_NONE;
break;
case CNF_HWTS_RXFILTER_NONE:
rx_filter = HWTSTAMP_FILTER_NONE;
break;
#ifdef HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP
case CNF_HWTS_RXFILTER_NTP:
rx_filter = HWTSTAMP_FILTER_NTP_ALL;
break;
#endif
default:
rx_filter = HWTSTAMP_FILTER_ALL;
break;
}
ts_config.flags = 0;
ts_config.tx_type = HWTSTAMP_TX_ON;
ts_config.rx_filter = rx_filter;
req.ifr_data = (char *)&ts_config;
if (ioctl(sock_fd, SIOCSHWTSTAMP, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno));
/* Check the current timestamping configuration in case this interface
allows only reading of the configuration and it was already configured
as requested */
req.ifr_data = (char *)&ts_config;
#ifdef SIOCGHWTSTAMP
if (ioctl(sock_fd, SIOCGHWTSTAMP, &req) ||
ts_config.tx_type != HWTSTAMP_TX_ON || ts_config.rx_filter != rx_filter)
#endif
{
SCK_CloseSocket(sock_fd);
return 0;
}
}
SCK_CloseSocket(sock_fd);
phc_fd = SYS_Linux_OpenPHC(NULL, ts_info.phc_index);
if (phc_fd < 0)
return 0;
iface = ARR_GetNewElement(interfaces);
snprintf(iface->name, sizeof (iface->name), "%s", conf_iface->name);
iface->if_index = if_index;
iface->phc_fd = phc_fd;
iface->phc_mode = 0;
iface->phc_nocrossts = conf_iface->nocrossts;
/* Start with 1 gbit and no VLANs or IPv4/IPv6 options */
iface->link_speed = 1000;
iface->l2_udp4_ntp_start = 42;
iface->l2_udp6_ntp_start = 62;
iface->precision = conf_iface->precision;
iface->tx_comp = conf_iface->tx_comp;
iface->rx_comp = conf_iface->rx_comp;
iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples,
UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)));
LOG(LOGS_INFO, "Enabled HW timestamping %son %s",
ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name);
return 1;
}
/* ================================================== */
static int
add_all_interfaces(CNF_HwTsInterface *conf_iface_all)
{
CNF_HwTsInterface conf_iface;
struct ifaddrs *ifaddr, *ifa;
int r;
conf_iface = *conf_iface_all;
if (getifaddrs(&ifaddr)) {
DEBUG_LOG("getifaddrs() failed : %s", strerror(errno));
return 0;
}
for (r = 0, ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
conf_iface.name = ifa->ifa_name;
if (add_interface(&conf_iface))
r = 1;
}
freeifaddrs(ifaddr);
/* Return success if at least one interface was added */
return r;
}
/* ================================================== */
static void
update_interface_speed(struct Interface *iface)
{
struct ethtool_cmd cmd;
struct ifreq req;
int sock_fd, link_speed;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
if (sock_fd < 0)
return;
memset(&req, 0, sizeof (req));
memset(&cmd, 0, sizeof (cmd));
snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", iface->name);
cmd.cmd = ETHTOOL_GSET;
req.ifr_data = (char *)&cmd;
if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
SCK_CloseSocket(sock_fd);
return;
}
SCK_CloseSocket(sock_fd);
link_speed = ethtool_cmd_speed(&cmd);
if (iface->link_speed != link_speed) {
iface->link_speed = link_speed;
DEBUG_LOG("Updated speed of %s to %d Mb/s", iface->name, link_speed);
}
}
/* ================================================== */
#if defined(HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO) || defined(HAVE_LINUX_TIMESTAMPING_OPT_TX_SWHW)
static int
check_timestamping_option(int option)
{
int sock_fd;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
if (sock_fd < 0)
return 0;
if (!SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, option)) {
SCK_CloseSocket(sock_fd);
return 0;
}
SCK_CloseSocket(sock_fd);
return 1;
}
#endif
/* ================================================== */
static int
open_dummy_socket(void)
{
int sock_fd, events = 0;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
if (sock_fd < 0)
return INVALID_SOCK_FD;
if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, 1, &events)) {
SCK_CloseSocket(sock_fd);
return INVALID_SOCK_FD;
}
return sock_fd;
}
/* ================================================== */
void
NIO_Linux_Initialise(void)
{
CNF_HwTsInterface *conf_iface;
unsigned int i;
int hwts;
interfaces = ARR_CreateInstance(sizeof (struct Interface));
/* Enable HW timestamping on specified interfaces. If "*" was specified, try
all interfaces. If no interface was specified, enable SW timestamping. */
for (i = hwts = 0; CNF_GetHwTsInterface(i, &conf_iface); i++) {
if (!strcmp("*", conf_iface->name))
continue;
if (!add_interface(conf_iface))
LOG_FATAL("Could not enable HW timestamping on %s", conf_iface->name);
hwts = 1;
}
for (i = 0; CNF_GetHwTsInterface(i, &conf_iface); i++) {
if (strcmp("*", conf_iface->name))
continue;
if (add_all_interfaces(conf_iface))
hwts = 1;
break;
}
ts_flags = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE;
ts_tx_flags = SOF_TIMESTAMPING_TX_SOFTWARE;
if (hwts) {
ts_flags |= SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE;
ts_tx_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO
if (check_timestamping_option(SOF_TIMESTAMPING_OPT_PKTINFO))
ts_flags |= SOF_TIMESTAMPING_OPT_PKTINFO;
#endif
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_TX_SWHW
if (check_timestamping_option(SOF_TIMESTAMPING_OPT_TX_SWHW))
ts_flags |= SOF_TIMESTAMPING_OPT_TX_SWHW;
#endif
}
/* Enable IP_PKTINFO in messages looped back to the error queue */
ts_flags |= SOF_TIMESTAMPING_OPT_CMSG;
/* Kernels before 4.7 ignore timestamping flags set in control messages */
permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7);
monitored_socket = INVALID_SOCK_FD;
suspended_socket = INVALID_SOCK_FD;
dummy_rxts_socket = INVALID_SOCK_FD;
}
/* ================================================== */
void
NIO_Linux_Finalise(void)
{
struct Interface *iface;
unsigned int i;
if (dummy_rxts_socket != INVALID_SOCK_FD)
SCK_CloseSocket(dummy_rxts_socket);
for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i);
HCL_DestroyInstance(iface->clock);
close(iface->phc_fd);
}
ARR_DestroyInstance(interfaces);
}
/* ================================================== */
int
NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
{
int val, flags;
if (!ts_flags)
return 0;
/* Enable SCM_TIMESTAMPING control messages and the socket's error queue in
order to receive our transmitted packets with more accurate timestamps */
val = 1;
flags = ts_flags;
if (client_only || permanent_ts_options)
flags |= ts_tx_flags;
if (!SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, val)) {
ts_flags = 0;
return 0;
}
if (!SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, flags)) {
ts_flags = 0;
return 0;
}
*events |= SCH_FILE_EXCEPTION;
return 1;
}
/* ================================================== */
static void
resume_socket(int sock_fd)
{
if (monitored_socket == sock_fd)
monitored_socket = INVALID_SOCK_FD;
if (sock_fd == INVALID_SOCK_FD || sock_fd != suspended_socket)
return;
suspended_socket = INVALID_SOCK_FD;
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_INPUT, 1);
DEBUG_LOG("Resumed RX processing %s timeout fd=%d",
resume_timeout_id ? "before" : "on", sock_fd);
if (resume_timeout_id) {
SCH_RemoveTimeout(resume_timeout_id);
resume_timeout_id = 0;
}
}
/* ================================================== */
static void
resume_timeout(void *arg)
{
resume_timeout_id = 0;
resume_socket(suspended_socket);
}
/* ================================================== */
static void
suspend_socket(int sock_fd)
{
resume_socket(suspended_socket);
suspended_socket = sock_fd;
SCH_SetFileHandlerEvent(suspended_socket, SCH_FILE_INPUT, 0);
resume_timeout_id = SCH_AddTimeoutByDelay(RESUME_TIMEOUT, resume_timeout, NULL);
DEBUG_LOG("Suspended RX processing fd=%d", sock_fd);
}
/* ================================================== */
int
NIO_Linux_ProcessEvent(int sock_fd, int event)
{
if (sock_fd != monitored_socket)
return 0;
if (event == SCH_FILE_INPUT) {
suspend_socket(monitored_socket);
monitored_socket = INVALID_SOCK_FD;
/* Don't process the message yet */
return 1;
}
return 0;
}
/* ================================================== */
static struct Interface *
get_interface(int if_index)
{
struct Interface *iface;
unsigned int i;
for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i);
if (iface->if_index != if_index)
continue;
return iface;
}
return NULL;
}
/* ================================================== */
static void
process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family,
int l2_length)
{
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts, ts;
double rx_correction, ts_delay, phc_err, local_err;
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
if (!SYS_Linux_GetPHCSample(iface->phc_fd, iface->phc_nocrossts, iface->precision,
&iface->phc_mode, &sample_phc_ts, &sample_sys_ts,
&phc_err))
return;
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
phc_err + local_err);
update_interface_speed(iface);
}
/* We need to transpose RX timestamps as hardware timestamps are normally
preamble timestamps and RX timestamps in NTP are supposed to be trailer
timestamps. If we don't know the length of the packet at layer 2, we
make an assumption that UDP data start at the same position as in the
last transmitted packet which had a HW TX timestamp. */
if (rx_ntp_length && iface->link_speed) {
if (!l2_length)
l2_length = (family == IPADDR_INET4 ? iface->l2_udp4_ntp_start :
iface->l2_udp6_ntp_start) + rx_ntp_length;
/* Include the frame check sequence (FCS) */
l2_length += 4;
rx_correction = l2_length / (1.0e6 / 8 * iface->link_speed);
UTI_AddDoubleToTimespec(hw_ts, rx_correction, hw_ts);
}
if (!HCL_CookTime(iface->clock, hw_ts, &ts, &local_err))
return;
if (!rx_ntp_length && iface->tx_comp)
UTI_AddDoubleToTimespec(&ts, iface->tx_comp, &ts);
else if (rx_ntp_length && iface->rx_comp)
UTI_AddDoubleToTimespec(&ts, -iface->rx_comp, &ts);
ts_delay = UTI_DiffTimespecsToDouble(&local_ts->ts, &ts);
if (fabs(ts_delay) > MAX_TS_DELAY) {
DEBUG_LOG("Unacceptable timestamp delay %.9f", ts_delay);
return;
}
local_ts->ts = ts;
local_ts->err = local_err;
local_ts->source = NTP_TS_HARDWARE;
}
/* ================================================== */
/* Extract UDP data from a layer 2 message. Supported is Ethernet
with optional VLAN tags. */
static int
extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
{
unsigned char *msg_start = msg;
remote_addr->ip_addr.family = IPADDR_UNSPEC;
remote_addr->port = 0;
/* Skip MACs */
if (len < 12)
return 0;
len -= 12, msg += 12;
/* Skip VLAN tag(s) if present */
while (len >= 4 && msg[0] == 0x81 && msg[1] == 0x00)
len -= 4, msg += 4;
/* Skip IPv4 or IPv6 ethertype */
if (len < 2 || !((msg[0] == 0x08 && msg[1] == 0x00) ||
(msg[0] == 0x86 && msg[1] == 0xdd)))
return 0;
len -= 2, msg += 2;
/* Parse destination address and port from IPv4/IPv6 and UDP headers */
if (len >= 20 && msg[0] >> 4 == 4) {
int ihl = (msg[0] & 0xf) * 4;
uint32_t addr;
if (len < ihl + 8 || msg[9] != 17)
return 0;
memcpy(&addr, msg + 16, sizeof (addr));
remote_addr->ip_addr.addr.in4 = ntohl(addr);
remote_addr->port = ntohs(*(uint16_t *)(msg + ihl + 2));
remote_addr->ip_addr.family = IPADDR_INET4;
len -= ihl + 8, msg += ihl + 8;
#ifdef FEAT_IPV6
} else if (len >= 48 && msg[0] >> 4 == 6) {
int eh_len, next_header = msg[6];
memcpy(&remote_addr->ip_addr.addr.in6, msg + 24, sizeof (remote_addr->ip_addr.addr.in6));
len -= 40, msg += 40;
/* Skip IPv6 extension headers if present */
while (next_header != 17) {
switch (next_header) {
case 44: /* Fragment Header */
/* Process only the first fragment */
if (ntohs(*(uint16_t *)(msg + 2)) >> 3 != 0)
return 0;
eh_len = 8;
break;
case 0: /* Hop-by-Hop Options */
case 43: /* Routing Header */
case 60: /* Destination Options */
case 135: /* Mobility Header */
eh_len = 8 * (msg[1] + 1);
break;
case 51: /* Authentication Header */
eh_len = 4 * (msg[1] + 2);
break;
default:
return 0;
}
if (eh_len < 8 || len < eh_len + 8)
return 0;
next_header = msg[0];
len -= eh_len, msg += eh_len;
}
remote_addr->port = ntohs(*(uint16_t *)(msg + 2));
remote_addr->ip_addr.family = IPADDR_INET6;
len -= 8, msg += 8;
#endif
} else {
return 0;
}
/* Move the message to fix alignment of its fields */
if (len > 0)
memmove(msg_start, msg, len);
return len;
}
/* ================================================== */
int
NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, int event)
{
struct Interface *iface;
int is_tx, ts_if_index, l2_length;
is_tx = event == SCH_FILE_EXCEPTION;
iface = NULL;
ts_if_index = message->timestamp.if_index;
if (ts_if_index == INVALID_IF_INDEX)
ts_if_index = message->if_index;
l2_length = message->timestamp.l2_length;
if (!UTI_IsZeroTimespec(&message->timestamp.hw)) {
iface = get_interface(ts_if_index);
if (iface) {
process_hw_timestamp(iface, &message->timestamp.hw, local_ts, !is_tx ? message->length : 0,
message->remote_addr.ip.ip_addr.family, l2_length);
} else {
DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
}
/* If a HW transmit timestamp was received, resume processing
of non-error messages on this socket */
if (is_tx)
resume_socket(local_addr->sock_fd);
}
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&message->timestamp.kernel) &&
(!is_tx || UTI_IsZeroTimespec(&message->timestamp.hw))) {
LCL_CookTime(&message->timestamp.kernel, &local_ts->ts, &local_ts->err);
local_ts->source = NTP_TS_KERNEL;
}
/* If the kernel is slow with enabling RX timestamping, open a dummy
socket to keep the kernel RX timestamping permanently enabled */
if (!is_tx && local_ts->source == NTP_TS_DAEMON && ts_flags) {
DEBUG_LOG("Missing kernel RX timestamp");
if (dummy_rxts_socket == INVALID_SOCK_FD)
dummy_rxts_socket = open_dummy_socket();
}
/* Return the message if it's not received from the error queue */
if (!is_tx)
return 0;
/* The data from the error queue includes all layers up to UDP. We have to
extract the UDP data and also the destination address with port as there
currently doesn't seem to be a better way to get them both. */
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",
UTI_IPSockAddrToString(&message->remote_addr.ip),
local_addr->sock_fd, message->length);
/* Update assumed position of UDP data at layer 2 for next received packet */
if (iface && message->length) {
if (message->remote_addr.ip.ip_addr.family == IPADDR_INET4)
iface->l2_udp4_ntp_start = l2_length - message->length;
else if (message->remote_addr.ip.ip_addr.family == IPADDR_INET6)
iface->l2_udp6_ntp_start = l2_length - message->length;
}
/* Drop the message if it has no timestamp or its processing failed */
if (local_ts->source == NTP_TS_DAEMON) {
DEBUG_LOG("Missing TX timestamp");
return 1;
}
if (message->length < NTP_HEADER_LENGTH)
return 1;
NSR_ProcessTx(&message->remote_addr.ip, local_addr, local_ts, message->data, message->length);
return 1;
}
/* ================================================== */
void
NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd)
{
if (!ts_flags)
return;
/* If a HW transmit timestamp is requested on a client socket, monitor
events on the socket in order to avoid processing of a fast response
without the HW timestamp of the request */
if (ts_tx_flags & SOF_TIMESTAMPING_TX_HARDWARE && !NIO_IsServerSocket(sock_fd))
monitored_socket = sock_fd;
/* Check if TX timestamping is disabled on this socket */
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
return;
message->timestamp.tx_flags = ts_tx_flags;
}
/* ================================================== */
void
NIO_Linux_NotifySocketClosing(int sock_fd)
{
resume_socket(sock_fd);
}

47
ntp_io_linux.h Normal file
View File

@@ -0,0 +1,47 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
This is the header file for the Linux-specific NTP socket I/O bits.
*/
#ifndef GOT_NTP_IO_LINUX_H
#define GOT_NTP_IO_LINUX_H
#include "socket.h"
extern void NIO_Linux_Initialise(void);
extern void NIO_Linux_Finalise(void);
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events);
extern int NIO_Linux_ProcessEvent(int sock_fd, int event);
extern int NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, int event);
extern void NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd);
extern void NIO_Linux_NotifySocketClosing(int sock_fd);
#endif

361
ntp_signd.c Normal file
View File

@@ -0,0 +1,361 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016
*
* 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.
*
**********************************************************************
=======================================================================
Support for MS-SNTP authentication in Samba (ntp_signd)
*/
#include "config.h"
#include "sysincl.h"
#include "array.h"
#include "conf.h"
#include "logging.h"
#include "ntp_io.h"
#include "ntp_signd.h"
#include "sched.h"
#include "socket.h"
#include "util.h"
/* Declarations per samba/source4/librpc/idl/ntp_signd.idl */
#define SIGND_VERSION 0
typedef enum {
SIGN_TO_CLIENT = 0,
ASK_SERVER_TO_SIGN = 1,
CHECK_SERVER_SIGNATURE = 2,
SIGNING_SUCCESS = 3,
SIGNING_FAILURE = 4,
} SigndOp;
typedef struct {
uint32_t length;
uint32_t version;
uint32_t op;
uint16_t packet_id;
uint16_t _pad;
uint32_t key_id;
NTP_Packet packet_to_sign;
} SigndRequest;
typedef struct {
uint32_t length;
uint32_t version;
uint32_t op;
uint32_t packet_id;
NTP_Packet signed_packet;
} SigndResponse;
typedef struct {
NTP_Remote_Address remote_addr;
NTP_Local_Address local_addr;
int sent;
int received;
int request_length;
struct timespec request_ts;
SigndRequest request;
SigndResponse response;
} SignInstance;
/* As the communication with ntp_signd is asynchronous, incoming packets are
saved in a queue in order to avoid loss when they come in bursts */
#define MAX_QUEUE_LENGTH 16U
#define NEXT_QUEUE_INDEX(index) (((index) + 1) % MAX_QUEUE_LENGTH)
#define IS_QUEUE_EMPTY() (queue_head == queue_tail)
/* Fixed-size array of SignInstance */
static ARR_Instance queue;
static unsigned int queue_head;
static unsigned int queue_tail;
#define INVALID_SOCK_FD (-6)
/* 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;
/* ================================================== */
static void read_write_socket(int sock_fd, int event, void *anything);
/* ================================================== */
static void
close_socket(void)
{
SCH_RemoveFileHandler(sock_fd);
SCK_CloseSocket(sock_fd);
sock_fd = INVALID_SOCK_FD;
/* Empty the queue */
queue_head = queue_tail = 0;
}
/* ================================================== */
static int
open_socket(void)
{
char path[PATH_MAX];
if (sock_fd != INVALID_SOCK_FD)
return 1;
if (snprintf(path, sizeof (path), "%s/socket", CNF_GetNtpSigndSocket()) >= sizeof (path)) {
DEBUG_LOG("signd socket path too long");
return 0;
}
sock_fd = SCK_OpenUnixStreamSocket(path, NULL, 0);
if (sock_fd < 0) {
sock_fd = INVALID_SOCK_FD;
return 0;
}
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, NULL);
return 1;
}
/* ================================================== */
static void
process_response(SignInstance *inst)
{
struct timespec ts;
double delay;
if (ntohs(inst->request.packet_id) != ntohl(inst->response.packet_id)) {
DEBUG_LOG("Invalid response ID");
return;
}
if (ntohl(inst->response.op) != SIGNING_SUCCESS) {
DEBUG_LOG("Signing failed");
return;
}
/* Check if the file descriptor is still valid */
if (!NIO_IsServerSocket(inst->local_addr.sock_fd)) {
DEBUG_LOG("Invalid NTP socket");
return;
}
SCH_GetLastEventTime(NULL, NULL, &ts);
delay = UTI_DiffTimespecsToDouble(&ts, &inst->request_ts);
DEBUG_LOG("Signing succeeded (delay %f)", delay);
/* Send the signed NTP packet */
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);
}
/* ================================================== */
static void
read_write_socket(int sock_fd, int event, void *anything)
{
SignInstance *inst;
uint32_t response_length;
int s;
inst = ARR_GetElement(queue, queue_head);
if (event == SCH_FILE_OUTPUT) {
assert(!IS_QUEUE_EMPTY());
assert(inst->sent < inst->request_length);
if (!inst->sent)
SCH_GetLastEventTime(NULL, NULL, &inst->request_ts);
s = SCK_Send(sock_fd, (char *)&inst->request + inst->sent,
inst->request_length - inst->sent, 0);
if (s < 0) {
close_socket();
return;
}
inst->sent += s;
/* Try again later if the request is not complete yet */
if (inst->sent < inst->request_length)
return;
/* Disable output and wait for a response */
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 0);
}
if (event == SCH_FILE_INPUT) {
if (IS_QUEUE_EMPTY()) {
DEBUG_LOG("Unexpected signd response");
close_socket();
return;
}
assert(inst->received < sizeof (inst->response));
s = SCK_Receive(sock_fd, (char *)&inst->response + inst->received,
sizeof (inst->response) - inst->received, 0);
if (s <= 0) {
close_socket();
return;
}
inst->received += s;
if (inst->received < sizeof (inst->response.length))
return;
response_length = ntohl(inst->response.length) + sizeof (inst->response.length);
if (response_length < offsetof(SigndResponse, signed_packet) ||
response_length > sizeof (SigndResponse)) {
DEBUG_LOG("Invalid response length");
close_socket();
return;
}
/* Wait for more data if not complete yet */
if (inst->received < response_length)
return;
process_response(inst);
/* Move the head and enable output for the next packet */
queue_head = NEXT_QUEUE_INDEX(queue_head);
if (!IS_QUEUE_EMPTY())
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 1);
}
}
/* ================================================== */
void
NSD_Initialise()
{
sock_fd = INVALID_SOCK_FD;
auth_delay = MIN_AUTH_DELAY;
enabled = CNF_GetNtpSigndSocket() && CNF_GetNtpSigndSocket()[0];
if (!enabled)
return;
queue = ARR_CreateInstance(sizeof (SignInstance));
ARR_SetSize(queue, MAX_QUEUE_LENGTH);
queue_head = queue_tail = 0;
LOG(LOGS_INFO, "MS-SNTP authentication enabled");
}
/* ================================================== */
void
NSD_Finalise()
{
if (!enabled)
return;
if (sock_fd != INVALID_SOCK_FD)
close_socket();
ARR_DestroyInstance(queue);
}
/* ================================================== */
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)
{
SignInstance *inst;
if (!enabled) {
DEBUG_LOG("signd disabled");
return 0;
}
if (queue_head == NEXT_QUEUE_INDEX(queue_tail)) {
DEBUG_LOG("signd queue full");
return 0;
}
if (info->length != NTP_HEADER_LENGTH) {
DEBUG_LOG("Invalid packet length");
return 0;
}
if (!open_socket())
return 0;
inst = ARR_GetElement(queue, queue_tail);
inst->remote_addr = *remote_addr;
inst->local_addr = *local_addr;
inst->sent = 0;
inst->received = 0;
inst->request_length = offsetof(SigndRequest, packet_to_sign) + info->length;
/* The length field doesn't include itself */
inst->request.length = htonl(inst->request_length - sizeof (inst->request.length));
inst->request.version = htonl(SIGND_VERSION);
inst->request.op = htonl(SIGN_TO_CLIENT);
inst->request.packet_id = htons(queue_tail);
inst->request._pad = 0;
inst->request.key_id = htonl(key_id);
memcpy(&inst->request.packet_to_sign, packet, info->length);
/* Enable output if there was no pending request */
if (IS_QUEUE_EMPTY())
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 1);
queue_tail = NEXT_QUEUE_INDEX(queue_tail);
DEBUG_LOG("Packet added to signd queue (%u:%u)", queue_head, queue_tail);
return 1;
}

45
ntp_signd.h Normal file
View File

@@ -0,0 +1,45 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header for MS-SNTP authentication via Samba (ntp_signd) */
#ifndef GOT_NTP_SIGND_H
#define GOT_NTP_SIGND_H
#include "addressing.h"
#include "ntp.h"
/* Initialisation function */
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);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -44,7 +44,9 @@ typedef enum {
NSR_NoSuchSource, /* Remove - attempt to remove a source that is not known */
NSR_AlreadyInUse, /* AddSource - attempt to add a source that is already known */
NSR_TooManySources, /* AddSource - too many sources already present */
NSR_InvalidAF /* AddSource - attempt to add a source with invalid address family */
NSR_InvalidAF, /* AddSource - attempt to add a source with invalid address family */
NSR_InvalidName, /* AddSourceByName - attempt to add a source with invalid name */
NSR_UnresolvedName, /* AddSourceByName - name will be resolved later */
} NSR_Status;
/* Procedure to add a new server or peer source. */
@@ -52,8 +54,10 @@ extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type
/* 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. */
extern void NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, SourceParameters *params);
intervals until it succeeds or fails with a non-temporary error. 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);
/* Function type for handlers to be called back when an attempt
* (possibly unsuccessful) to resolve unresolved sources ends */
@@ -80,8 +84,28 @@ extern void NSR_RemoveAllSources(void);
/* Procedure to try to find a replacement for a bad source */
extern void NSR_HandleBadSource(IPAddr *address);
/* Procedure to resolve all names again */
extern void NSR_RefreshAddresses(void);
/* Procedure to update the address of a source */
extern NSR_Status NSR_UpdateSourceNtpAddress(NTP_Remote_Address *old_addr,
NTP_Remote_Address *new_addr);
/* Procedure to get local reference ID corresponding to a source */
extern uint32_t NSR_GetLocalRefid(IPAddr *address);
/* Procedure to get the name of a source. If the source doesn't have a name,
it returns a temporary string containing formatted address. */
extern char *NSR_GetName(IPAddr *address);
/* This routine is called by ntp_io when a new packet arrives off the network */
extern void NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
extern void NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length);
/* This routine is called by ntp_io when a packet was sent to the network and
an accurate transmit timestamp was captured */
extern void NSR_ProcessTx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length);
/* Initialisation function */
extern void NSR_Initialise(void);
@@ -90,14 +114,9 @@ extern void NSR_Initialise(void);
extern void NSR_Finalise(void);
/* This routine is used to indicate that sources whose IP addresses
match a particular subnet should be set online again. Returns a
flag indicating whether any hosts matched the address */
extern int NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address);
/* This routine is used to indicate that sources whose IP addresses
match a particular subnet should be set offline. Returns a flag
indicating whether any hosts matched the address */
extern int NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address);
match a particular subnet should be set online or offline. It returns
a flag indicating whether any hosts matched the address. */
extern int NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity);
extern int NSR_ModifyMinpoll(IPAddr *address, int new_minpoll);
@@ -115,7 +134,9 @@ extern int NSR_ModifyPolltarget(IPAddr *address, int new_poll_target);
extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAddr *mask, IPAddr *address);
extern void NSR_ReportSource(RPT_SourceReport *report, struct timeval *now);
extern void NSR_ReportSource(RPT_SourceReport *report, struct timespec *now);
extern int NSR_GetNTPReport(RPT_NTPReport *report);
extern void NSR_GetActivityReport(RPT_ActivityReport *report);

69
nts_ke.h Normal file
View File

@@ -0,0 +1,69 @@
/*
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.
*
**********************************************************************
=======================================================================
Header file for the NTS Key Establishment protocol
*/
#ifndef GOT_NTS_KE_H
#define GOT_NTS_KE_H
#include "siv.h"
#define NKE_RECORD_CRITICAL_BIT (1U << 15)
#define NKE_RECORD_END_OF_MESSAGE 0
#define NKE_RECORD_NEXT_PROTOCOL 1
#define NKE_RECORD_ERROR 2
#define NKE_RECORD_WARNING 3
#define NKE_RECORD_AEAD_ALGORITHM 4
#define NKE_RECORD_COOKIE 5
#define NKE_RECORD_NTPV4_SERVER_NEGOTIATION 6
#define NKE_RECORD_NTPV4_PORT_NEGOTIATION 7
#define NKE_NEXT_PROTOCOL_NTPV4 0
#define NKE_ERROR_UNRECOGNIZED_CRITICAL_RECORD 0
#define NKE_ERROR_BAD_REQUEST 1
#define NKE_ERROR_INTERNAL_SERVER_ERROR 2
#define NKE_ALPN_NAME "ntske/1"
#define NKE_EXPORTER_LABEL "EXPORTER-network-time-security/1"
#define NKE_EXPORTER_CONTEXT_C2S "\x0\x0\x0\xf\x0"
#define NKE_EXPORTER_CONTEXT_S2C "\x0\x0\x0\xf\x1"
#define NKE_MAX_MESSAGE_LENGTH 16384
#define NKE_MAX_RECORD_BODY_LENGTH 256
#define NKE_MAX_COOKIE_LENGTH 256
#define NKE_MAX_COOKIES 8
#define NKE_MAX_KEY_LENGTH SIV_MAX_KEY_LENGTH
typedef struct {
int length;
unsigned char key[NKE_MAX_KEY_LENGTH];
} NKE_Key;
typedef struct {
int length;
unsigned char cookie[NKE_MAX_COOKIE_LENGTH];
} NKE_Cookie;
#endif

389
nts_ke_client.c Normal file
View File

@@ -0,0 +1,389 @@
/*
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.
*
**********************************************************************
=======================================================================
NTS-KE client
*/
#include "config.h"
#include "sysincl.h"
#include "nts_ke_client.h"
#include "conf.h"
#include "logging.h"
#include "memory.h"
#include "nameserv_async.h"
#include "nts_ke_session.h"
#include "siv.h"
#include "socket.h"
#include "util.h"
#define CLIENT_TIMEOUT 16.0
struct NKC_Instance_Record {
char *name;
IPSockAddr address;
NKSN_Instance session;
int destroying;
int got_response;
int resolving_name;
SIV_Algorithm siv_algorithm;
NKE_Key c2s, s2c;
NKE_Cookie cookies[NKE_MAX_COOKIES];
int num_cookies;
char server_name[NKE_MAX_RECORD_BODY_LENGTH + 1];
IPSockAddr ntp_address;
};
/* ================================================== */
static void *client_credentials;
/* ================================================== */
static void
name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *arg)
{
NKC_Instance inst = arg;
int i;
inst->resolving_name = 0;
if (inst->destroying) {
NKC_DestroyInstance(inst);
return;
}
if (status != DNS_Success || n_addrs < 1) {
LOG(LOGS_ERR, "Could not resolve NTP server %s from %s", inst->server_name, inst->name);
/* Force restart */
inst->got_response = 0;
return;
}
inst->ntp_address.ip_addr = ip_addrs[0];
/* Prefer an address of the same family as NTS-KE */
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) {
inst->ntp_address.ip_addr = ip_addrs[i];
break;
}
}
}
/* ================================================== */
static int
prepare_request(NKC_Instance inst)
{
NKSN_Instance session = inst->session;
uint16_t datum;
NKSN_BeginMessage(session);
datum = htons(NKE_NEXT_PROTOCOL_NTPV4);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum)))
return 0;
datum = htons(AEAD_AES_SIV_CMAC_256);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
return 0;
if (!NKSN_EndMessage(session))
return 0;
return 1;
}
/* ================================================== */
static int
process_response(NKC_Instance inst)
{
int next_protocol = -1, aead_algorithm = -1, error = 0;
int i, critical, type, length;
uint16_t data[NKE_MAX_COOKIE_LENGTH / sizeof (uint16_t)];
assert(NKE_MAX_COOKIE_LENGTH % sizeof (uint16_t) == 0);
assert(sizeof (uint16_t) == 2);
inst->num_cookies = 0;
inst->ntp_address.ip_addr.family = IPADDR_UNSPEC;
inst->ntp_address.port = 0;
inst->server_name[0] = '\0';
while (!error) {
if (!NKSN_GetRecord(inst->session, &critical, &type, &length, &data, sizeof (data)))
break;
switch (type) {
case NKE_RECORD_NEXT_PROTOCOL:
if (!critical || length != 2 || ntohs(data[0]) != NKE_NEXT_PROTOCOL_NTPV4) {
DEBUG_LOG("Unexpected NTS-KE next protocol");
error = 1;
break;
}
next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
break;
case NKE_RECORD_AEAD_ALGORITHM:
if (length != 2 || ntohs(data[0]) != AEAD_AES_SIV_CMAC_256) {
DEBUG_LOG("Unexpected NTS-KE AEAD algorithm");
error = 1;
break;
}
aead_algorithm = AEAD_AES_SIV_CMAC_256;
inst->siv_algorithm = aead_algorithm;
break;
case NKE_RECORD_ERROR:
if (length == 2)
DEBUG_LOG("NTS-KE error %d", ntohs(data[0]));
error = 1;
break;
case NKE_RECORD_WARNING:
if (length == 2)
DEBUG_LOG("NTS-KE warning %d", ntohs(data[0]));
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++;
}
break;
case NKE_RECORD_NTPV4_SERVER_NEGOTIATION:
if (length < 1 || length >= sizeof (inst->server_name)) {
DEBUG_LOG("Invalid server name");
error = 1;
break;
}
memcpy(inst->server_name, data, length);
inst->server_name[length] = '\0';
/* Make sure the name is printable and has no spaces */
for (i = 0; i < length && isgraph(inst->server_name[i]); i++)
;
if (i != length) {
DEBUG_LOG("Invalid server name");
error = 1;
break;
}
DEBUG_LOG("Negotiated server %s", inst->server_name);
break;
case NKE_RECORD_NTPV4_PORT_NEGOTIATION:
if (length != 2) {
DEBUG_LOG("Invalid port");
error = 1;
break;
}
inst->ntp_address.port = ntohs(data[0]);
DEBUG_LOG("Negotiated port %d", inst->ntp_address.port);
break;
default:
DEBUG_LOG("Unknown record type=%d length=%d critical=%d", type, length, critical);
if (critical)
error = 1;
}
}
DEBUG_LOG("NTS-KE response: error=%d next=%d aead=%d",
error, next_protocol, aead_algorithm);
if (error || inst->num_cookies == 0 ||
next_protocol != NKE_NEXT_PROTOCOL_NTPV4 ||
aead_algorithm != AEAD_AES_SIV_CMAC_256)
return 0;
return 1;
}
/* ================================================== */
static int
handle_message(void *arg)
{
NKC_Instance inst = arg;
if (!process_response(inst)) {
LOG(LOGS_ERR, "Received invalid NTS-KE response from %s", inst->name);
return 0;
}
if (!NKSN_GetKeys(inst->session, inst->siv_algorithm, &inst->c2s, &inst->s2c))
return 0;
if (inst->server_name[0] != '\0') {
if (inst->resolving_name)
return 0;
if (!UTI_StringToIP(inst->server_name, &inst->ntp_address.ip_addr)) {
DNS_Name2IPAddressAsync(inst->server_name, name_resolve_handler, inst);
inst->resolving_name = 1;
}
}
inst->got_response = 1;
return 1;
}
/* ================================================== */
void
NKC_Initialise(void)
{
client_credentials = NULL;
}
/* ================================================== */
void
NKC_Finalise(void)
{
if (client_credentials)
NKSN_DestroyCertCredentials(client_credentials);
}
/* ================================================== */
NKC_Instance
NKC_CreateInstance(IPSockAddr *address, const char *name)
{
NKC_Instance inst;
inst = MallocNew(struct NKC_Instance_Record);
inst->address = *address;
inst->name = Strdup(name);
inst->session = NKSN_CreateInstance(0, inst->name, handle_message, inst);
inst->resolving_name = 0;
inst->destroying = 0;
inst->got_response = 0;
/* Create the credentials with the first client instance and share them
with other instances */
if (!client_credentials)
client_credentials = NKSN_CreateCertCredentials(NULL, NULL, CNF_GetNtsTrustedCertFile());
return inst;
}
/* ================================================== */
void
NKC_DestroyInstance(NKC_Instance inst)
{
/* If the resolver is running, destroy the instance later when finished */
if (inst->resolving_name) {
inst->destroying = 1;
return;
}
NKSN_DestroyInstance(inst->session);
Free(inst->name);
Free(inst);
}
/* ================================================== */
int
NKC_Start(NKC_Instance inst)
{
IPSockAddr local_addr;
int sock_fd;
assert(!NKC_IsActive(inst));
if (!client_credentials) {
DEBUG_LOG("Missing client credentials");
return 0;
}
/* Follow the bindacqaddress setting */
CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
if (local_addr.ip_addr.family != inst->address.ip_addr.family)
SCK_GetAnyLocalIPAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
local_addr.port = 0;
sock_fd = SCK_OpenTcpSocket(&inst->address, &local_addr, 0);
if (sock_fd < 0)
return 0;
/* Start a NTS-KE session */
if (!NKSN_StartSession(inst->session, sock_fd, client_credentials, CLIENT_TIMEOUT)) {
SCK_CloseSocket(sock_fd);
return 0;
}
/* Send a request */
if (!prepare_request(inst)) {
DEBUG_LOG("Could not prepare NTS-KE request");
NKSN_StopSession(inst->session);
return 0;
}
return 1;
}
/* ================================================== */
int
NKC_IsActive(NKC_Instance inst)
{
return !NKSN_IsStopped(inst->session) || inst->resolving_name;
}
/* ================================================== */
int
NKC_GetNtsData(NKC_Instance inst,
SIV_Algorithm *siv_algorithm, NKE_Key *c2s, NKE_Key *s2c,
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address)
{
int i;
if (!inst->got_response || inst->resolving_name)
return 0;
*siv_algorithm = inst->siv_algorithm;
*c2s = inst->c2s;
*s2c = inst->s2c;
for (i = 0; i < inst->num_cookies && i < max_cookies; i++)
cookies[i] = inst->cookies[i];
*num_cookies = i;
*ntp_address = inst->ntp_address;
return i;
}

58
nts_ke_client.h Normal file
View File

@@ -0,0 +1,58 @@
/*
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.
*
**********************************************************************
=======================================================================
Header file for the NTS-KE client
*/
#ifndef GOT_NTS_KE_CLIENT_H
#define GOT_NTS_KE_CLIENT_H
#include "addressing.h"
#include "nts_ke.h"
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);
/* Destroy an instance */
extern void NKC_DestroyInstance(NKC_Instance inst);
/* Connect to the server, start an NTS-KE session, send an NTS-KE request, and
process the response (asynchronously) */
extern int NKC_Start(NKC_Instance inst);
/* Check if the client is still running */
extern int NKC_IsActive(NKC_Instance inst);
/* Get the NTS data if the session was successful */
extern int NKC_GetNtsData(NKC_Instance inst,
SIV_Algorithm *siv_algorithm, NKE_Key *c2s, NKE_Key *s2c,
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address);
#endif

785
nts_ke_server.c Normal file
View File

@@ -0,0 +1,785 @@
/*
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.
*
**********************************************************************
=======================================================================
NTS-KE server
*/
#include "config.h"
#include "sysincl.h"
#include "nts_ke_server.h"
#include "array.h"
#include "conf.h"
#include "clientlog.h"
#include "logging.h"
#include "memory.h"
#include "ntp_core.h"
#include "nts_ke_session.h"
#include "siv.h"
#include "socket.h"
#include "sched.h"
#include "sys.h"
#include "util.h"
#define SERVER_TIMEOUT 2.0
#define SERVER_COOKIE_SIV AEAD_AES_SIV_CMAC_256
#define SERVER_COOKIE_NONCE_LENGTH 16
#define KEY_ID_INDEX_BITS 2
#define MAX_SERVER_KEYS (1U << KEY_ID_INDEX_BITS)
#define MIN_KEY_ROTATE_INTERVAL 1.0
#define INVALID_SOCK_FD (-7)
typedef struct {
uint32_t key_id;
uint8_t nonce[SERVER_COOKIE_NONCE_LENGTH];
} ServerCookieHeader;
typedef struct {
uint32_t id;
unsigned char key[SIV_MAX_KEY_LENGTH];
SIV_Instance siv;
} ServerKey;
typedef struct {
uint32_t key_id;
unsigned char key[SIV_MAX_KEY_LENGTH];
IPAddr client_addr;
uint16_t client_port;
uint16_t _pad;
} HelperRequest;
/* ================================================== */
static ServerKey server_keys[MAX_SERVER_KEYS];
static int current_server_key;
static int server_sock_fd4;
static int server_sock_fd6;
static int helper_sock_fd;
static int initialised = 0;
/* Array of NKSN instances */
static ARR_Instance sessions;
static void *server_credentials;
/* ================================================== */
static int handle_message(void *arg);
/* ================================================== */
static int
handle_client(int sock_fd, IPSockAddr *addr)
{
NKSN_Instance inst, *instp;
int i;
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 */
for (i = 0, inst = NULL; i < ARR_GetSize(sessions); i++) {
instp = ARR_GetElement(sessions, i);
if (!*instp) {
/* NULL handler arg will be replaced with the session instance */
inst = NKSN_CreateInstance(1, UTI_IPSockAddrToString(addr), handle_message, NULL);
*instp = inst;
break;
} else if (NKSN_IsStopped(*instp)) {
inst = *instp;
break;
}
}
if (!inst) {
DEBUG_LOG("Rejected connection from %s (%s)",
UTI_IPSockAddrToString(addr), "too many connections");
return 0;
}
if (!NKSN_StartSession(inst, sock_fd, server_credentials, SERVER_TIMEOUT))
return 0;
return 1;
}
/* ================================================== */
static void
handle_helper_request(int fd, int event, void *arg)
{
SCK_Message message;
HelperRequest *req;
IPSockAddr client_addr;
int sock_fd;
if (!SCK_ReceiveMessage(fd, &message, SCK_FLAG_MSG_DESCRIPTOR))
return;
sock_fd = message.descriptor;
if (sock_fd < 0) {
/* Message with no descriptor is a shutdown command */
SCH_QuitProgram();
return;
}
if (message.length != sizeof (HelperRequest)) {
DEBUG_LOG("Unexpected message length");
SCK_CloseSocket(sock_fd);
return;
}
req = message.data;
/* Extract the server key and client address from the request */
server_keys[current_server_key].id = ntohl(req->key_id);
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);
client_addr.port = ntohs(req->client_port);
if (!SIV_SetKey(server_keys[current_server_key].siv, server_keys[current_server_key].key,
SIV_GetKeyLength(SERVER_COOKIE_SIV)))
assert(0);
if (!handle_client(sock_fd, &client_addr)) {
SCK_CloseSocket(sock_fd);
return;
}
DEBUG_LOG("Accepted helper request fd=%d", sock_fd);
}
/* ================================================== */
static void
accept_connection(int server_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);
if (sock_fd < 0)
return;
if (!NCR_CheckAccessRestriction(&addr.ip_addr)) {
DEBUG_LOG("Rejected connection from %s (%s)",
UTI_IPSockAddrToString(&addr), "access denied");
SCK_CloseSocket(sock_fd);
return;
}
SCH_GetLastEventTime(&now, NULL, NULL);
log_index = CLG_LogNTPAccess(&addr.ip_addr, &now);
if (log_index >= 0 && CLG_LimitNTPResponseRate(log_index)) {
DEBUG_LOG("Rejected connection from %s (%s)",
UTI_IPSockAddrToString(&addr), "rate limit");
SCK_CloseSocket(sock_fd);
return;
}
/* Pass the socket to a helper process if enabled. Otherwise, handle the
client in the main process. */
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));
req.key_id = htonl(server_keys[current_server_key].id);
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);
SCK_InitMessage(&message, SCK_ADDR_UNSPEC);
message.data = &req;
message.length = sizeof (req);
message.descriptor = sock_fd;
if (!SCK_SendMessage(helper_sock_fd, &message, SCK_FLAG_MSG_DESCRIPTOR)) {
SCK_CloseSocket(sock_fd);
return;
}
SCK_CloseSocket(sock_fd);
} else {
if (!handle_client(sock_fd, &addr)) {
SCK_CloseSocket(sock_fd);
return;
}
}
DEBUG_LOG("Accepted connection from %s fd=%d", UTI_IPSockAddrToString(&addr), sock_fd);
}
/* ================================================== */
static int
open_socket(int family, int port)
{
IPSockAddr local_addr;
int sock_fd;
if (!SCK_IsFamilySupported(family))
return INVALID_SOCK_FD;
CNF_GetBindAddress(family, &local_addr.ip_addr);
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);
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())) {
SCK_CloseSocket(sock_fd);
return INVALID_SOCK_FD;
}
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, accept_connection, NULL);
return sock_fd;
}
/* ================================================== */
static void
helper_signal(int x)
{
SCH_QuitProgram();
}
/* ================================================== */
static int
prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_algorithm)
{
NKE_Cookie cookie;
NKE_Key c2s, s2c;
uint16_t datum;
int i;
DEBUG_LOG("NTS KE response: error=%d next=%d aead=%d", error, next_protocol, aead_algorithm);
NKSN_BeginMessage(session);
if (error >= 0) {
datum = htons(error);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_ERROR, &datum, sizeof (datum)))
return 0;
} else {
datum = htons(next_protocol);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum)))
return 0;
datum = htons(aead_algorithm);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
return 0;
if (CNF_GetNTPPort() != NTP_PORT) {
datum = htons(CNF_GetNTPPort());
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_PORT_NEGOTIATION, &datum, sizeof (datum)))
return 0;
}
/* This should be configurable */
if (0) {
const char server[] = "::1";
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_SERVER_NEGOTIATION, server,
sizeof (server) - 1))
return 0;
}
if (!NKSN_GetKeys(session, aead_algorithm, &c2s, &s2c))
return 0;
for (i = 0; i < NKE_MAX_COOKIES; i++) {
if (!NKS_GenerateCookie(&c2s, &s2c, &cookie))
return 0;
if (!NKSN_AddRecord(session, 0, NKE_RECORD_COOKIE, cookie.cookie, cookie.length))
return 0;
}
}
if (!NKSN_EndMessage(session))
return 0;
return 1;
}
/* ================================================== */
static int
process_request(NKSN_Instance session)
{
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)];
assert(NKE_MAX_RECORD_BODY_LENGTH % sizeof (uint16_t) == 0);
assert(sizeof (uint16_t) == 2);
while (error == -1) {
if (!NKSN_GetRecord(session, &critical, &type, &length, &data, sizeof (data)))
break;
switch (type) {
case NKE_RECORD_NEXT_PROTOCOL:
if (!critical || length < 2 || length % 2 != 0) {
error = NKE_ERROR_BAD_REQUEST;
break;
}
for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
if (ntohs(data[i]) == NKE_NEXT_PROTOCOL_NTPV4)
next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
}
break;
case NKE_RECORD_AEAD_ALGORITHM:
if (length < 2 || length % 2 != 0) {
error = NKE_ERROR_BAD_REQUEST;
break;
}
for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
if (ntohs(data[i]) == AEAD_AES_SIV_CMAC_256)
aead_algorithm = AEAD_AES_SIV_CMAC_256;
}
break;
case NKE_RECORD_ERROR:
case NKE_RECORD_WARNING:
case NKE_RECORD_COOKIE:
error = NKE_ERROR_BAD_REQUEST;
break;
default:
if (critical)
error = NKE_ERROR_UNRECOGNIZED_CRITICAL_RECORD;
}
}
if (aead_algorithm < 0 || next_protocol < 0)
error = NKE_ERROR_BAD_REQUEST;
if (!prepare_response(session, error, next_protocol, aead_algorithm))
return 0;
return 1;
}
/* ================================================== */
static int
handle_message(void *arg)
{
NKSN_Instance session = arg;
return process_request(session);
}
/* ================================================== */
static void
generate_key(int index)
{
int key_length;
assert(index < MAX_SERVER_KEYS);
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);
UTI_GetRandomBytes(&server_keys[index].id, sizeof (server_keys[index].id));
server_keys[index].id &= -1U << KEY_ID_INDEX_BITS;
server_keys[index].id |= index;
DEBUG_LOG("Generated server key %"PRIX32, server_keys[index].id);
}
/* ================================================== */
static void
save_keys(void)
{
char hex_key[SIV_MAX_KEY_LENGTH * 2 + 1];
int i, index, key_length;
char *cachedir;
FILE *f;
cachedir = CNF_GetNtsCacheDir();
if (!cachedir)
return;
f = UTI_OpenFile(cachedir, "ntskeys", ".tmp", 'w', 0600);
if (!f)
return;
key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
for (i = 0; i < MAX_SERVER_KEYS; i++) {
index = (current_server_key + i + 1) % MAX_SERVER_KEYS;
if (key_length > sizeof (server_keys[index].key) ||
!UTI_BytesToHex(server_keys[index].key, key_length, hex_key, sizeof (hex_key))) {
assert(0);
break;
}
fprintf(f, "%08"PRIX32" %s\n", server_keys[index].id, hex_key);
}
fclose(f);
if (!UTI_RenameTempFile(cachedir, "ntskeys", ".tmp", NULL))
;
}
/* ================================================== */
static void
load_keys(void)
{
int i, index, line_length, key_length, n;
char *cachedir, line[1024];
FILE *f;
uint32_t id;
cachedir = CNF_GetNtsCacheDir();
if (!cachedir)
return;
f = UTI_OpenFile(cachedir, "ntskeys", NULL, 'r', 0);
if (!f)
return;
key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
for (i = 0; i < MAX_SERVER_KEYS; i++) {
if (!fgets(line, sizeof (line), f))
break;
line_length = strlen(line);
if (line_length < 10)
break;
/* Drop '\n' */
line[line_length - 1] = '\0';
if (sscanf(line, "%"PRIX32"%n", &id, &n) != 1 || line[n] != ' ')
break;
index = id % MAX_SERVER_KEYS;
if (UTI_HexToBytes(line + n + 1, server_keys[index].key,
sizeof (server_keys[index].key)) != key_length)
break;
server_keys[index].id = id;
if (!SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length))
assert(0);
DEBUG_LOG("Loaded key %"PRIX32, id);
current_server_key = index;
}
fclose(f);
}
/* ================================================== */
static void
key_timeout(void *arg)
{
current_server_key = (current_server_key + 1) % MAX_SERVER_KEYS;
generate_key(current_server_key);
save_keys();
SCH_AddTimeoutByDelay(MAX(CNF_GetNtsRotate(), MIN_KEY_ROTATE_INTERVAL),
key_timeout, NULL);
}
/* ================================================== */
static void
start_helper(int id, int scfilter_level, int main_fd, int helper_fd)
{
pid_t pid;
pid = fork();
if (pid < 0)
LOG_FATAL("fork() failed : %s", strerror(errno));
if (pid > 0)
return;
SCK_CloseSocket(main_fd);
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("NTS-KE helper #%d exiting", id);
exit(0);
}
/* ================================================== */
void
NKS_Initialise(int scfilter_level)
{
char *cert, *key;
int i, processes;
server_sock_fd4 = INVALID_SOCK_FD;
server_sock_fd6 = INVALID_SOCK_FD;
helper_sock_fd = INVALID_SOCK_FD;
cert = CNF_GetNtsServerCertFile();
key = CNF_GetNtsServerKeyFile();
if (!cert || !key)
return;
server_credentials = NKSN_CreateCertCredentials(cert, key, NULL);
if (!server_credentials)
return;
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());
for (i = 0; i < MAX_SERVER_KEYS; i++) {
server_keys[i].siv = SIV_CreateInstance(SERVER_COOKIE_SIV);
generate_key(i);
}
current_server_key = MAX_SERVER_KEYS - 1;
load_keys();
key_timeout(NULL);
processes = CNF_GetNtsServerProcesses();
if (processes > 0) {
int sock_fd1, sock_fd2;
sock_fd1 = SCK_OpenUnixSocketPair(0, &sock_fd2);
for (i = 0; i < processes; i++)
start_helper(i + 1, scfilter_level, sock_fd1, sock_fd2);
SCK_CloseSocket(sock_fd2);
helper_sock_fd = sock_fd1;
}
initialised = 1;
}
/* ================================================== */
void
NKS_Finalise(void)
{
int i;
if (!initialised)
return;
if (helper_sock_fd != INVALID_SOCK_FD) {
for (i = 0; i < CNF_GetNtsServerProcesses(); i++) {
if (!SCK_Send(helper_sock_fd, "", 1, 0))
;
}
SCK_CloseSocket(helper_sock_fd);
}
if (server_sock_fd4 != INVALID_SOCK_FD)
SCK_CloseSocket(server_sock_fd4);
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);
}
for (i = 0; i < ARR_GetSize(sessions); i++) {
NKSN_Instance session = *(NKSN_Instance *)ARR_GetElement(sessions, i);
if (session)
NKSN_DestroyInstance(session);
}
ARR_DestroyInstance(sessions);
NKSN_DestroyCertCredentials(server_credentials);
}
/* ================================================== */
/* A server cookie consists of key ID, nonce, and encrypted C2S+S2C keys */
int
NKS_GenerateCookie(NKE_Key *c2s, NKE_Key *s2c, NKE_Cookie *cookie)
{
unsigned char plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
int plaintext_length, tag_length;
ServerCookieHeader *header;
ServerKey *key;
if (!initialised) {
DEBUG_LOG("NTS server disabled");
return 0;
}
if (c2s->length < 0 || c2s->length > NKE_MAX_KEY_LENGTH ||
s2c->length < 0 || s2c->length > NKE_MAX_KEY_LENGTH) {
DEBUG_LOG("Invalid key length");
return 0;
}
key = &server_keys[current_server_key];
header = (ServerCookieHeader *)cookie->cookie;
/* Keep the fields in the host byte order */
header->key_id = key->id;
UTI_GetRandomBytes(header->nonce, sizeof (header->nonce));
plaintext_length = c2s->length + s2c->length;
assert(plaintext_length <= sizeof (plaintext));
memcpy(plaintext, c2s->key, c2s->length);
memcpy(plaintext + c2s->length, s2c->key, s2c->length);
tag_length = SIV_GetTagLength(key->siv);
cookie->length = sizeof (*header) + plaintext_length + tag_length;
assert(cookie->length <= sizeof (cookie->cookie));
ciphertext = cookie->cookie + sizeof (*header);
if (!SIV_Encrypt(key->siv, header->nonce, sizeof (header->nonce),
"", 0,
plaintext, plaintext_length,
ciphertext, plaintext_length + tag_length)) {
DEBUG_LOG("Could not encrypt cookie");
return 0;
}
return 1;
}
/* ================================================== */
int
NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Key *c2s, NKE_Key *s2c)
{
unsigned char plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
int ciphertext_length, plaintext_length, tag_length;
ServerCookieHeader *header;
ServerKey *key;
if (!initialised) {
DEBUG_LOG("NTS server disabled");
return 0;
}
if (cookie->length <= sizeof (*header)) {
DEBUG_LOG("Invalid cookie length");
return 0;
}
header = (ServerCookieHeader *)cookie->cookie;
ciphertext = cookie->cookie + sizeof (*header);
ciphertext_length = cookie->length - sizeof (*header);
key = &server_keys[header->key_id % MAX_SERVER_KEYS];
if (header->key_id != key->id) {
DEBUG_LOG("Unknown key %"PRIX32, header->key_id);
return 0;
}
tag_length = SIV_GetTagLength(key->siv);
if (tag_length >= ciphertext_length) {
DEBUG_LOG("Invalid cookie length");
return 0;
}
plaintext_length = ciphertext_length - tag_length;
if (plaintext_length > sizeof (plaintext) || plaintext_length % 2 != 0) {
DEBUG_LOG("Invalid cookie length");
return 0;
}
if (!SIV_Decrypt(key->siv, header->nonce, sizeof (header->nonce),
"", 0,
ciphertext, ciphertext_length,
plaintext, plaintext_length)) {
DEBUG_LOG("Could not decrypt cookie");
return 0;
}
c2s->length = plaintext_length / 2;
s2c->length = plaintext_length / 2;
assert(c2s->length <= sizeof (c2s->key));
memcpy(c2s->key, plaintext, c2s->length);
memcpy(s2c->key, plaintext + c2s->length, s2c->length);
return 1;
}

42
nts_ke_server.h Normal file
View File

@@ -0,0 +1,42 @@
/*
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.
*
**********************************************************************
=======================================================================
Header file for the NTS-KE server
*/
#ifndef GOT_NTS_KE_SERVER_H
#define GOT_NTS_KE_SERVER_H
#include "nts_ke.h"
/* Init and fini functions */
extern void NKS_Initialise(int scfilter_level);
extern void NKS_Finalise(void);
/* Generate a new NTS cookie containing the C2S and S2C keys */
extern int NKS_GenerateCookie(NKE_Key *c2s, NKE_Key *s2c, NKE_Cookie *cookie);
/* Validate a cookie and extract the C2S and S2C keys */
extern int NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Key *c2s, NKE_Key *s2c);
#endif

779
nts_ke_session.c Normal file
View File

@@ -0,0 +1,779 @@
/*
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.
*
**********************************************************************
=======================================================================
NTS-KE session used by server and client
*/
#include "config.h"
#include "sysincl.h"
#include "nts_ke_session.h"
#include "conf.h"
#include "logging.h"
#include "memory.h"
#include "siv.h"
#include "socket.h"
#include "sched.h"
#include "util.h"
#include <gnutls/gnutls.h>
#define INVALID_SOCK_FD (-8)
struct RecordHeader {
uint16_t type;
uint16_t body_length;
};
struct Message {
int length;
int sent;
int parsed;
int complete;
unsigned char data[NKE_MAX_MESSAGE_LENGTH];
};
typedef enum {
KE_WAIT_CONNECT,
KE_HANDSHAKE,
KE_SEND,
KE_RECEIVE,
KE_SHUTDOWN,
KE_STOPPED,
} KeState;
struct NKSN_Instance_Record {
int server;
char *name;
NKSN_MessageHandler handler;
void *handler_arg;
KeState state;
int sock_fd;
gnutls_session_t tls_session;
SCH_TimeoutID timeout_id;
struct Message message;
int new_message;
int ended_message;
};
/* ================================================== */
static gnutls_priority_t priority_cache;
static int credentials_counter = 0;
/* ================================================== */
static void
reset_message(struct Message *message)
{
message->length = 0;
message->sent = 0;
message->parsed = 0;
message->complete = 0;
}
/* ================================================== */
static int
add_record(struct Message *message, int critical, int type, const void *body, int body_length)
{
struct RecordHeader header;
if (body_length < 0 || body_length > 0xffff || type < 0 || type > 0x7fff ||
message->length + sizeof (header) + body_length > sizeof (message->data))
return 0;
header.type = htons(!!critical * NKE_RECORD_CRITICAL_BIT | type);
header.body_length = htons(body_length);
memcpy(&message->data[message->length], &header, sizeof (header));
message->length += sizeof (header);
if (body_length > 0) {
memcpy(&message->data[message->length], body, body_length);
message->length += body_length;
}
return 1;
}
/* ================================================== */
static void
reset_message_parsing(struct Message *message)
{
message->parsed = 0;
}
/* ================================================== */
static int
get_record(struct Message *message, int *critical, int *type, int *body_length,
void *body, int buffer_length)
{
struct RecordHeader header;
int blen, rlen;
if (message->length < message->parsed + sizeof (header) ||
buffer_length < 0)
return 0;
memcpy(&header, &message->data[message->parsed], sizeof (header));
blen = ntohs(header.body_length);
rlen = sizeof (header) + blen;
if (message->length < message->parsed + rlen)
return 0;
if (critical)
*critical = !!(ntohs(header.type) & NKE_RECORD_CRITICAL_BIT);
if (type)
*type = ntohs(header.type) & ~NKE_RECORD_CRITICAL_BIT;
if (body)
memcpy(body, &message->data[message->parsed + sizeof (header)], MIN(buffer_length, blen));
if (body_length)
*body_length = blen;
message->parsed += rlen;
return 1;
}
/* ================================================== */
static int
check_message_format(struct Message *message, int eof)
{
int critical = 0, type = -1, length = -1, ends = 0;
reset_message_parsing(message);
message->complete = 0;
while (get_record(message, &critical, &type, &length, NULL, 0)) {
if (type == NKE_RECORD_END_OF_MESSAGE) {
if (!critical || length != 0 || ends > 0)
return 0;
ends++;
}
}
/* If the message cannot be fully parsed, but more data may be coming,
consider the format to be ok */
if (message->length == 0 || message->parsed < message->length)
return !eof;
if (type != NKE_RECORD_END_OF_MESSAGE)
return !eof;
message->complete = 1;
return 1;
}
/* ================================================== */
static gnutls_session_t
create_tls_session(int server_mode, int sock_fd, const char *server_name,
gnutls_certificate_credentials_t credentials,
gnutls_priority_t priority)
{
unsigned char alpn_name[sizeof (NKE_ALPN_NAME)];
gnutls_session_t session;
gnutls_datum_t alpn;
int r;
r = gnutls_init(&session, GNUTLS_NONBLOCK | (server_mode ? GNUTLS_SERVER : GNUTLS_CLIENT));
if (r < 0) {
LOG(LOGS_ERR, "Could not %s TLS session : %s", "create", gnutls_strerror(r));
return NULL;
}
if (!server_mode) {
r = gnutls_server_name_set(session, GNUTLS_NAME_DNS, server_name, strlen(server_name));
if (r < 0)
goto error;
gnutls_session_set_verify_cert(session, server_name, 0);
}
r = gnutls_priority_set(session, priority);
if (r < 0)
goto error;
r = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, credentials);
if (r < 0)
goto error;
memcpy(alpn_name, NKE_ALPN_NAME, sizeof (alpn_name));
alpn.data = alpn_name;
alpn.size = sizeof (alpn_name) - 1;
r = gnutls_alpn_set_protocols(session, &alpn, 1, 0);
if (r < 0)
goto error;
gnutls_transport_set_int(session, sock_fd);
return session;
error:
LOG(LOGS_ERR, "Could not %s TLS session : %s", "set", gnutls_strerror(r));
gnutls_deinit(session);
return NULL;
}
/* ================================================== */
static void
stop_session(NKSN_Instance inst)
{
if (inst->state == KE_STOPPED)
return;
inst->state = KE_STOPPED;
SCH_RemoveFileHandler(inst->sock_fd);
SCK_CloseSocket(inst->sock_fd);
inst->sock_fd = INVALID_SOCK_FD;
gnutls_deinit(inst->tls_session);
inst->tls_session = NULL;
SCH_RemoveTimeout(inst->timeout_id);
inst->timeout_id = 0;
}
/* ================================================== */
static void
session_timeout(void *arg)
{
NKSN_Instance inst = arg;
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE session with %s timed out", inst->name);
inst->timeout_id = 0;
stop_session(inst);
}
/* ================================================== */
static int
get_socket_error(int sock_fd)
{
int optval;
socklen_t optlen = sizeof (optval);
if (getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) {
DEBUG_LOG("getsockopt() failed : %s", strerror(errno));
return EINVAL;
}
return optval;
}
/* ================================================== */
static int
check_alpn(NKSN_Instance inst)
{
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;
}
/* ================================================== */
static void
change_state(NKSN_Instance inst, KeState state)
{
int output;
switch (state) {
case KE_HANDSHAKE:
output = !inst->server;
break;
case KE_WAIT_CONNECT:
case KE_SEND:
case KE_SHUTDOWN:
output = 1;
break;
case KE_RECEIVE:
output = 0;
break;
default:
assert(0);
}
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT, output);
inst->state = state;
}
/* ================================================== */
static int
handle_event(NKSN_Instance inst, int event)
{
struct Message *message = &inst->message;
int r;
DEBUG_LOG("Session event %d fd=%d state=%d", event, inst->sock_fd, (int)inst->state);
switch (inst->state) {
case KE_WAIT_CONNECT:
/* Check if connect() succeeded */
if (event != SCH_FILE_OUTPUT)
return 0;
r = get_socket_error(inst->sock_fd);
if (r) {
LOG(LOGS_ERR, "Could not connect to %s : %s", inst->name, strerror(r));
stop_session(inst);
return 0;
}
DEBUG_LOG("Connected to %s", inst->name);
change_state(inst, KE_HANDSHAKE);
return 0;
case KE_HANDSHAKE:
r = gnutls_handshake(inst->tls_session);
if (r < 0) {
if (gnutls_error_is_fatal(r)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"TLS handshake with %s failed : %s", inst->name, gnutls_strerror(r));
stop_session(inst);
return 0;
}
/* Disable output when the handshake is trying to receive data */
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT,
gnutls_record_get_direction(inst->tls_session));
return 0;
}
if (DEBUG) {
char *description = gnutls_session_get_desc(inst->tls_session);
DEBUG_LOG("Handshake with %s completed %s",
inst->name, description ? description : "");
gnutls_free(description);
}
if (!check_alpn(inst)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE not supported by %s", inst->name);
stop_session(inst);
return 0;
}
/* Client will send a request to the server */
change_state(inst, inst->server ? KE_RECEIVE : KE_SEND);
return 0;
case KE_SEND:
assert(inst->new_message && message->complete);
r = gnutls_record_send(inst->tls_session, &message->data[message->sent],
message->length - message->sent);
if (r < 0) {
if (gnutls_error_is_fatal(r)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"Could not send NTS-KE message to %s : %s", inst->name, gnutls_strerror(r));
stop_session(inst);
}
return 0;
}
DEBUG_LOG("Sent %d bytes to %s", r, inst->name);
message->sent += r;
if (message->sent < message->length)
return 0;
/* Client will receive a response */
change_state(inst, inst->server ? KE_SHUTDOWN : KE_RECEIVE);
reset_message(&inst->message);
inst->new_message = 0;
return 0;
case KE_RECEIVE:
do {
if (message->length >= sizeof (message->data)) {
DEBUG_LOG("Message is too long");
stop_session(inst);
return 0;
}
r = gnutls_record_recv(inst->tls_session, &message->data[message->length],
sizeof (message->data) - message->length);
if (r < 0) {
/* Handle a renegotiation request on both client and server as
a protocol error */
if (gnutls_error_is_fatal(r) || r == GNUTLS_E_REHANDSHAKE) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"Could not receive NTS-KE message from %s : %s",
inst->name, gnutls_strerror(r));
stop_session(inst);
}
return 0;
}
DEBUG_LOG("Received %d bytes from %s", r, inst->name);
message->length += r;
} while (gnutls_record_check_pending(inst->tls_session) > 0);
if (!check_message_format(message, r == 0)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"Received invalid NTS-KE message from %s", inst->name);
stop_session(inst);
return 0;
}
/* Wait for more data if the message is not complete yet */
if (!message->complete)
return 0;
/* Server will send a response to the client */
change_state(inst, inst->server ? KE_SEND : KE_SHUTDOWN);
break;
case KE_SHUTDOWN:
r = gnutls_bye(inst->tls_session, GNUTLS_SHUT_RDWR);
if (r < 0) {
if (gnutls_error_is_fatal(r)) {
DEBUG_LOG("Shutdown with %s failed : %s", inst->name, gnutls_strerror(r));
stop_session(inst);
return 0;
}
/* Disable output when the TLS shutdown is trying to receive data */
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT,
gnutls_record_get_direction(inst->tls_session));
return 0;
}
SCK_ShutdownConnection(inst->sock_fd);
stop_session(inst);
DEBUG_LOG("Shutdown completed");
return 0;
default:
assert(0);
}
return 1;
}
/* ================================================== */
static void
read_write_socket(int fd, int event, void *arg)
{
NKSN_Instance inst = arg;
if (!handle_event(inst, event))
return;
reset_message_parsing(&inst->message);
if (!(inst->handler)(inst->handler_arg)) {
stop_session(inst);
return;
}
}
/* ================================================== */
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));
/* NTS specification requires TLS1.2 or later */
r = gnutls_priority_init2(&priority_cache, "-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1",
NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND);
if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "priority cache", gnutls_strerror(r));
gnutls_initialised = 1;
}
/* ================================================== */
static void
deinit_gnutls(void)
{
assert(gnutls_initialised);
gnutls_priority_deinit(priority_cache);
gnutls_global_deinit();
gnutls_initialised = 0;
}
/* ================================================== */
void *
NKSN_CreateCertCredentials(char *cert, char *key, char *trusted_certs)
{
gnutls_certificate_credentials_t credentials = NULL;
int r;
init_gnutls();
r = gnutls_certificate_allocate_credentials(&credentials);
if (r < 0)
goto error;
if (cert && key) {
r = gnutls_certificate_set_x509_key_file(credentials, cert, key,
GNUTLS_X509_FMT_PEM);
if (r < 0)
goto error;
} else {
if (!CNF_GetNoSystemCert()) {
r = gnutls_certificate_set_x509_system_trust(credentials);
if (r < 0)
goto error;
}
if (trusted_certs) {
r = gnutls_certificate_set_x509_trust_file(credentials, trusted_certs,
GNUTLS_X509_FMT_PEM);
if (r < 0)
goto error;
}
}
credentials_counter++;
return credentials;
error:
LOG(LOGS_ERR, "Could not set credentials : %s", gnutls_strerror(r));
if (credentials)
gnutls_certificate_free_credentials(credentials);
return NULL;
}
/* ================================================== */
void
NKSN_DestroyCertCredentials(void *credentials)
{
gnutls_certificate_free_credentials(credentials);
credentials_counter--;
if (credentials_counter != 0)
return;
deinit_gnutls();
}
/* ================================================== */
NKSN_Instance
NKSN_CreateInstance(int server_mode, const char *name,
NKSN_MessageHandler handler, void *handler_arg)
{
NKSN_Instance inst;
inst = MallocNew(struct NKSN_Instance_Record);
inst->server = server_mode;
inst->name = Strdup(name);
inst->handler = handler;
inst->handler_arg = handler_arg;
/* Replace NULL arg with the session itself */
if (!inst->handler_arg)
inst->handler_arg = inst;
inst->state = KE_STOPPED;
inst->sock_fd = INVALID_SOCK_FD;
inst->tls_session = NULL;
inst->timeout_id = 0;
return inst;
}
/* ================================================== */
void
NKSN_DestroyInstance(NKSN_Instance inst)
{
stop_session(inst);
Free(inst->name);
Free(inst);
}
/* ================================================== */
int
NKSN_StartSession(NKSN_Instance inst, int sock_fd, void *credentials, double timeout)
{
assert(inst->state == KE_STOPPED);
inst->tls_session = create_tls_session(inst->server, sock_fd,
inst->server ? NULL : inst->name,
credentials, priority_cache);
if (!inst->tls_session)
return 0;
inst->sock_fd = sock_fd;
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, inst);
inst->timeout_id = SCH_AddTimeoutByDelay(timeout, session_timeout, inst);
reset_message(&inst->message);
inst->new_message = 0;
inst->ended_message = 0;
change_state(inst, inst->server ? KE_HANDSHAKE : KE_WAIT_CONNECT);
return 1;
}
/* ================================================== */
void
NKSN_BeginMessage(NKSN_Instance inst)
{
reset_message(&inst->message);
inst->new_message = 1;
}
/* ================================================== */
int
NKSN_AddRecord(NKSN_Instance inst, int critical, int type, const void *body, int body_length)
{
assert(inst->new_message && !inst->message.complete);
assert(type != NKE_RECORD_END_OF_MESSAGE);
return add_record(&inst->message, critical, type, body, body_length);
}
/* ================================================== */
int
NKSN_EndMessage(NKSN_Instance inst)
{
assert(!inst->message.complete);
if (!add_record(&inst->message, 1, NKE_RECORD_END_OF_MESSAGE, NULL, 0))
return 0;
inst->message.complete = 1;
return 1;
}
/* ================================================== */
int
NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
void *body, int buffer_length)
{
int type2;
assert(inst->message.complete);
if (!get_record(&inst->message, critical, &type2, body_length, body, buffer_length))
return 0;
if (type2 == NKE_RECORD_END_OF_MESSAGE)
return 0;
if (type)
*type = type2;
return 1;
}
/* ================================================== */
int
NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c)
{
c2s->length = SIV_GetKeyLength(siv);
s2c->length = SIV_GetKeyLength(siv);
assert(c2s->length <= sizeof (c2s->key));
assert(s2c->length <= sizeof (s2c->key));
if (gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (NKE_EXPORTER_CONTEXT_C2S) - 1, NKE_EXPORTER_CONTEXT_C2S,
c2s->length, (char *)c2s->key) < 0)
return 0;
if (gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (NKE_EXPORTER_CONTEXT_S2C) - 1, NKE_EXPORTER_CONTEXT_S2C,
s2c->length, (char *)s2c->key) < 0)
return 0;
return 1;
}
/* ================================================== */
int
NKSN_IsStopped(NKSN_Instance inst)
{
return inst->state == KE_STOPPED;
}
/* ================================================== */
void
NKSN_StopSession(NKSN_Instance inst)
{
stop_session(inst);
}

83
nts_ke_session.h Normal file
View File

@@ -0,0 +1,83 @@
/*
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.
*
**********************************************************************
=======================================================================
Header file for the NTS-KE session
*/
#ifndef GOT_NTS_KE_SESSION_H
#define GOT_NTS_KE_SESSION_H
#include "nts_ke.h"
#include "siv.h"
typedef struct NKSN_Instance_Record *NKSN_Instance;
/* Handler for received NTS-KE messages. A non-zero return code stops
the session. */
typedef int (*NKSN_MessageHandler)(void *arg);
/* Get client or server credentials using certificates of trusted CAs,
or a server certificate and key. The credentials may be shared between
different clients or servers. */
extern void *NKSN_CreateCertCredentials(char *cert, char *key, char *trusted_certs);
/* Destroy the credentials */
extern void NKSN_DestroyCertCredentials(void *credentials);
/* Create an instance */
extern NKSN_Instance NKSN_CreateInstance(int server_mode, const char *name,
NKSN_MessageHandler handler, void *handler_arg);
/* Destroy an instance */
extern void NKSN_DestroyInstance(NKSN_Instance inst);
/* Start a new NTS-KE session */
extern int NKSN_StartSession(NKSN_Instance inst, int sock_fd, void *credentials,
double timeout);
/* Begin an NTS-KE message. A request should be made right after starting
the session and response should be made in the message handler. */
extern void NKSN_BeginMessage(NKSN_Instance inst);
/* Add a record to the message */
extern int NKSN_AddRecord(NKSN_Instance inst, int critical, int type,
const void *body, int body_length);
/* Terminate the message */
extern int NKSN_EndMessage(NKSN_Instance inst);
/* Get the next record from the received message. This function should be
called from the message handler. */
extern int NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
void *body, int buffer_length);
/* Export NTS keys for a specified algorithm */
extern int NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c);
/* Check if the session has stopped */
extern int NKSN_IsStopped(NKSN_Instance inst);
/* Stop the session */
extern void NKSN_StopSession(NKSN_Instance inst);
#endif

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2002
* 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
@@ -21,20 +21,21 @@
=======================================================================
The header file for the adjtimex wrapper
Header file for the NTS-NTP protocol
*/
#ifndef GOT_WRAP_ADJTIMEX_H
#define GOT_WRAP_ADJTIMEX_H
#ifndef GOT_NTS_NTP_H
#define GOT_NTS_NTP_H
int TMX_ResetOffset(void);
int TMX_SetFrequency(double *freq, long tick);
int TMX_GetFrequency(double *freq, long *tick);
int TMX_SetLeap(int leap);
int TMX_GetLeap(int *leap);
int TMX_SetSync(int sync, double est_error, double max_error);
int TMX_TestStepOffset(void);
int TMX_ApplyStepOffset(double offset);
#define NTP_EF_NTS_UNIQUE_IDENTIFIER 0x0104
#define NTP_EF_NTS_COOKIE 0x0204
#define NTP_EF_NTS_COOKIE_PLACEHOLDER 0x0304
#define NTP_EF_NTS_AUTH_AND_EEF 0x0404
#endif /* GOT_WRAP_ADJTIMEX_H */
#define NTP_KOD_NTS_NAK 0x4e54534e
#define NTS_MIN_UNIQ_ID_LENGTH 32
#define NTS_MIN_UNPADDED_NONCE_LENGTH 16
#define NTS_MAX_COOKIES 8
#endif

174
nts_ntp_auth.c Normal file
View File

@@ -0,0 +1,174 @@
/*
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.
*
**********************************************************************
=======================================================================
NTS Authenticator and Encrypted Extension Fields extension field
*/
#include "config.h"
#include "sysincl.h"
#include "nts_ntp_auth.h"
#include "logging.h"
#include "ntp_ext.h"
#include "nts_ntp.h"
#include "siv.h"
#include "util.h"
struct AuthHeader {
uint16_t nonce_length;
uint16_t ciphertext_length;
};
/* ================================================== */
static int
get_padding_length(int length)
{
return length % 4U ? 4 - length % 4U : 0;
}
/* ================================================== */
static int
get_padded_length(int length)
{
return length + get_padding_length(length);
}
/* ================================================== */
int
NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
const unsigned char *nonce, int nonce_length,
const unsigned char *plaintext, int plaintext_length,
int min_ef_length)
{
int auth_length, ciphertext_length, assoc_length;
int nonce_padding, ciphertext_padding, additional_padding;
unsigned char *ciphertext, *body;
struct AuthHeader *header;
assert(sizeof (*header) == 4);
if (nonce_length <= 0 || plaintext_length < 0) {
DEBUG_LOG("Invalid nonce/plaintext length");
return 0;
}
assoc_length = info->length;
ciphertext_length = SIV_GetTagLength(siv) + plaintext_length;
nonce_padding = get_padding_length(nonce_length);
ciphertext_padding = get_padding_length(ciphertext_length);
min_ef_length = get_padded_length(min_ef_length);
auth_length = sizeof (*header) + nonce_length + nonce_padding +
ciphertext_length + ciphertext_padding;
additional_padding = MAX(min_ef_length - auth_length - 4, 0);
additional_padding = MAX(NTS_MIN_UNPADDED_NONCE_LENGTH - nonce_length - nonce_padding,
additional_padding);
auth_length += additional_padding;
if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, auth_length,
(void **)&header)) {
DEBUG_LOG("Could not add EF");
return 0;
}
header->nonce_length = htons(nonce_length);
header->ciphertext_length = htons(ciphertext_length);
body = (unsigned char *)(header + 1);
ciphertext = body + nonce_length + nonce_padding;
memcpy(body, nonce, nonce_length);
memset(body + nonce_length, 0, nonce_padding);
if (!SIV_Encrypt(siv, nonce, nonce_length, packet, assoc_length,
plaintext, plaintext_length, ciphertext, ciphertext_length)) {
DEBUG_LOG("SIV encrypt failed");
return 0;
}
memset(ciphertext + ciphertext_length, 0, ciphertext_padding + additional_padding);
return 1;
}
/* ================================================== */
int
NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, int ef_start,
unsigned char *plaintext, int buffer_length, int *plaintext_length)
{
unsigned int siv_tag_length, nonce_length, ciphertext_length;
unsigned char *nonce, *ciphertext;
int ef_type, ef_body_length;
void *ef_body;
struct AuthHeader *header;
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)
return 0;
header = ef_body;
nonce_length = ntohs(header->nonce_length);
ciphertext_length = ntohs(header->ciphertext_length);
if (get_padded_length(nonce_length) +
get_padded_length(ciphertext_length) > ef_body_length)
return 0;
nonce = (unsigned char *)(header + 1);
ciphertext = (unsigned char *)(header + 1) + get_padded_length(nonce_length);
siv_tag_length = SIV_GetTagLength(siv);
if (nonce_length < 1 ||
ciphertext_length < siv_tag_length ||
ciphertext_length - siv_tag_length > buffer_length) {
DEBUG_LOG("Unexpected nonce/ciphertext length");
return 0;
}
if (ef_body_length < sizeof (*header) +
NTS_MIN_UNPADDED_NONCE_LENGTH + get_padded_length(ciphertext_length)) {
DEBUG_LOG("Missing padding");
return 0;
}
*plaintext_length = ciphertext_length - siv_tag_length;
if (!SIV_Decrypt(siv, nonce, nonce_length, packet, info->length - ef_body_length - 4,
ciphertext, ciphertext_length, plaintext, *plaintext_length)) {
DEBUG_LOG("SIV decrypt failed");
return 0;
}
return 1;
}

43
nts_ntp_auth.h Normal file
View File

@@ -0,0 +1,43 @@
/*
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.
*
**********************************************************************
=======================================================================
Header for NTS Authenticator and Encrypted Extension Fields
extension field
*/
#ifndef GOT_NTS_NTP_AUTH_H
#define GOT_NTS_NTP_AUTH_H
#include "ntp.h"
#include "siv.h"
extern int NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
const unsigned char *nonce, int nonce_length,
const unsigned char *plaintext, int plaintext_length,
int min_ef_length);
extern int NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
int ef_start, unsigned char *plaintext, int buffer_length,
int *plaintext_length);
#endif

441
nts_ntp_client.c Normal file
View File

@@ -0,0 +1,441 @@
/*
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.
*
**********************************************************************
=======================================================================
Client NTS-NTP authentication
*/
#include "config.h"
#include "sysincl.h"
#include "nts_ntp_client.h"
#include "conf.h"
#include "logging.h"
#include "memory.h"
#include "ntp.h"
#include "ntp_ext.h"
#include "ntp_sources.h"
#include "nts_ke_client.h"
#include "nts_ntp.h"
#include "nts_ntp_auth.h"
#include "sched.h"
#include "siv.h"
#include "util.h"
#define MAX_TOTAL_COOKIE_LENGTH (8 * 108)
#define MIN_NKE_RETRY_INTERVAL 1000
struct NNC_Instance_Record {
const IPSockAddr *ntp_address;
IPSockAddr nts_address;
char *name;
SIV_Instance siv_c2s;
SIV_Instance siv_s2c;
NKC_Instance nke;
double last_nke_attempt;
double last_nke_success;
NKE_Cookie cookies[NTS_MAX_COOKIES];
int num_cookies;
int cookie_index;
int nak_response;
int ok_response;
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
unsigned char uniq_id[NTS_MIN_UNIQ_ID_LENGTH];
};
/* ================================================== */
static void
reset_instance(NNC_Instance inst)
{
inst->last_nke_attempt = -MIN_NKE_RETRY_INTERVAL;
inst->last_nke_success = 0.0;
inst->num_cookies = 0;
inst->cookie_index = 0;
inst->nak_response = 0;
inst->ok_response = 1;
memset(inst->nonce, 0, sizeof (inst->nonce));
memset(inst->uniq_id, 0, sizeof (inst->uniq_id));
}
/* ================================================== */
NNC_Instance
NNC_CreateInstance(IPSockAddr *nts_address, const char *name, const IPSockAddr *ntp_address)
{
NNC_Instance inst;
inst = MallocNew(struct NNC_Instance_Record);
inst->ntp_address = ntp_address;
inst->nts_address = *nts_address;
inst->name = name ? Strdup(name) : NULL;
inst->siv_c2s = NULL;
inst->siv_s2c = NULL;
inst->nke = NULL;
reset_instance(inst);
return inst;
}
/* ================================================== */
void
NNC_DestroyInstance(NNC_Instance inst)
{
if (inst->nke)
NKC_DestroyInstance(inst->nke);
if (inst->siv_c2s)
SIV_DestroyInstance(inst->siv_c2s);
if (inst->siv_s2c)
SIV_DestroyInstance(inst->siv_s2c);
Free(inst->name);
Free(inst);
}
/* ================================================== */
static int
is_nke_needed(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) {
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;
}
/* ================================================== */
static int
set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address)
{
NTP_Remote_Address old_address, new_address;
old_address = *inst->ntp_address;
new_address = *negotiated_address;
if (new_address.ip_addr.family == IPADDR_UNSPEC)
new_address.ip_addr = old_address.ip_addr;
if (new_address.port == 0)
new_address.port = old_address.port;
if (UTI_CompareIPs(&old_address.ip_addr, &new_address.ip_addr, NULL) == 0 &&
old_address.port == new_address.port)
/* Nothing to do */
return 1;
if (NSR_UpdateSourceNtpAddress(&old_address, &new_address) != NSR_Success) {
LOG(LOGS_ERR, "Could not change %s to negotiated address %s",
UTI_IPToString(&old_address.ip_addr), UTI_IPToString(&new_address.ip_addr));
return 0;
}
return 1;
}
/* ================================================== */
static int
get_nke_data(NNC_Instance inst)
{
NTP_Remote_Address ntp_address;
SIV_Algorithm siv;
NKE_Key c2s, s2c;
double now;
int got_data;
assert(is_nke_needed(inst));
now = SCH_GetLastEventMonoTime();
if (!inst->nke) {
if (now - inst->last_nke_attempt < MIN_NKE_RETRY_INTERVAL) {
DEBUG_LOG("Limiting NTS-KE request rate");
return 0;
}
if (!inst->name) {
LOG(LOGS_ERR, "Missing name of %s for NTS-KE",
UTI_IPToString(&inst->nts_address.ip_addr));
return 0;
}
inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name);
if (!NKC_Start(inst->nke))
return 0;
inst->last_nke_attempt = now;
}
if (NKC_IsActive(inst->nke))
return 0;
got_data = NKC_GetNtsData(inst->nke, &siv, &c2s, &s2c,
inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES,
&ntp_address);
NKC_DestroyInstance(inst->nke);
inst->nke = NULL;
if (!got_data)
return 0;
if (!set_ntp_address(inst, &ntp_address)) {
inst->num_cookies = 0;
return 0;
}
inst->cookie_index = 0;
if (inst->siv_c2s)
SIV_DestroyInstance(inst->siv_c2s);
if (inst->siv_s2c)
SIV_DestroyInstance(inst->siv_s2c);
inst->siv_c2s = SIV_CreateInstance(siv);
inst->siv_s2c = SIV_CreateInstance(siv);
if (!inst->siv_c2s || !inst->siv_s2c ||
!SIV_SetKey(inst->siv_c2s, c2s.key, c2s.length) ||
!SIV_SetKey(inst->siv_s2c, s2c.key, s2c.length)) {
DEBUG_LOG("Could not initialise SIV");
inst->num_cookies = 0;
return 0;
}
inst->nak_response = 0;
inst->last_nke_success = now;
return 1;
}
/* ================================================== */
int
NNC_PrepareForAuth(NNC_Instance inst)
{
if (is_nke_needed(inst)) {
if (!get_nke_data(inst))
return 0;
}
UTI_GetRandomBytes(&inst->uniq_id, sizeof (inst->uniq_id));
UTI_GetRandomBytes(&inst->nonce, sizeof (inst->nonce));
return 1;
}
/* ================================================== */
int
NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
NTP_PacketInfo *info)
{
NKE_Cookie *cookie;
int i, req_cookies;
if (inst->num_cookies == 0 || !inst->siv_c2s)
return 0;
if (info->mode != MODE_CLIENT)
return 0;
cookie = &inst->cookies[inst->cookie_index];
req_cookies = MIN(NTS_MAX_COOKIES - inst->num_cookies + 1,
MAX_TOTAL_COOKIE_LENGTH / (cookie->length + 4));
if (!NEF_AddField(packet, info, NTP_EF_NTS_UNIQUE_IDENTIFIER,
&inst->uniq_id, sizeof (inst->uniq_id)))
return 0;
if (!NEF_AddField(packet, info, NTP_EF_NTS_COOKIE,
cookie->cookie, cookie->length))
return 0;
for (i = 0; i < req_cookies - 1; i++) {
if (!NEF_AddField(packet, info, NTP_EF_NTS_COOKIE_PLACEHOLDER,
cookie->cookie, cookie->length))
return 0;
}
if (!NNA_GenerateAuthEF(packet, info, inst->siv_c2s, inst->nonce, sizeof (inst->nonce),
(const unsigned char *)"", 0, NTP_MAX_V4_MAC_LENGTH + 4))
return 0;
inst->num_cookies--;
inst->cookie_index = (inst->cookie_index + 1) % NTS_MAX_COOKIES;
inst->ok_response = 0;
return 1;
}
/* ================================================== */
static int
extract_cookies(NNC_Instance inst, unsigned char *plaintext, int length)
{
int ef_type, ef_body_length, ef_length, parsed, index, acceptable, saved;
void *ef_body;
acceptable = saved = 0;
for (parsed = 0; parsed < length; parsed += ef_length) {
if (!NEF_ParseSingleField(plaintext, length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length))
break;
if (ef_type != NTP_EF_NTS_COOKIE)
continue;
if (ef_length < NTP_MIN_EF_LENGTH || ef_body_length > sizeof (inst->cookies[0].cookie)) {
DEBUG_LOG("Unexpected cookie length %d", ef_body_length);
continue;
}
acceptable++;
if (inst->num_cookies >= NTS_MAX_COOKIES)
continue;
index = (inst->cookie_index + inst->num_cookies) % NTS_MAX_COOKIES;
memcpy(inst->cookies[index].cookie, ef_body, ef_body_length);
inst->cookies[index].length = ef_body_length;
inst->num_cookies++;
saved++;
}
DEBUG_LOG("Extracted %d cookies (saved %d)", acceptable, saved);
return acceptable > 0;
}
/* ================================================== */
int
NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
NTP_PacketInfo *info)
{
int ef_type, ef_body_length, ef_length, parsed, plaintext_length;
int has_valid_uniq_id = 0, has_valid_auth = 0;
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
void *ef_body;
if (info->ext_fields == 0 || info->mode != MODE_SERVER)
return 0;
/* Accept only one response per request */
if (inst->ok_response)
return 0;
if (!inst->siv_s2c)
return 0;
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
if (!NEF_ParseField(packet, info->length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length))
break;
switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
if (ef_body_length != sizeof (inst->uniq_id) ||
memcmp(ef_body, inst->uniq_id, sizeof (inst->uniq_id)) != 0) {
DEBUG_LOG("Invalid uniq id");
return 0;
}
has_valid_uniq_id = 1;
break;
case NTP_EF_NTS_COOKIE:
DEBUG_LOG("Unencrypted cookie");
break;
case NTP_EF_NTS_AUTH_AND_EEF:
if (parsed + ef_length != info->length) {
DEBUG_LOG("Auth not last EF");
return 0;
}
if (!NNA_DecryptAuthEF(packet, info, inst->siv_s2c, parsed,
plaintext, sizeof (plaintext), &plaintext_length))
return 0;
has_valid_auth = 1;
break;
default:
break;
}
}
if (!has_valid_uniq_id || !has_valid_auth) {
if (has_valid_uniq_id && packet->stratum == NTP_INVALID_STRATUM &&
ntohl(packet->reference_id) == NTP_KOD_NTS_NAK) {
DEBUG_LOG("NTS NAK");
inst->nak_response = 1;
return 0;
}
DEBUG_LOG("Missing NTS EF");
return 0;
}
if (!extract_cookies(inst, plaintext, plaintext_length))
return 0;
inst->ok_response = 1;
/* At this point we know the client interoperates with the server. Allow a
new NTS-KE session to be started as soon as the cookies run out. */
inst->last_nke_attempt = -MIN_NKE_RETRY_INTERVAL;
return 1;
}
/* ================================================== */
void
NNC_ChangeAddress(NNC_Instance inst, IPAddr *address)
{
if (inst->nke)
NKC_DestroyInstance(inst->nke);
inst->nke = NULL;
inst->num_cookies = 0;
inst->nts_address.ip_addr = *address;
reset_instance(inst);
DEBUG_LOG("NTS reset");
}

46
nts_ntp_client.h Normal file
View File

@@ -0,0 +1,46 @@
/*
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.
*
**********************************************************************
=======================================================================
Header file for client NTS-NTP authentication
*/
#ifndef GOT_NTS_NTP_CLIENT_H
#define GOT_NTS_NTP_CLIENT_H
#include "addressing.h"
#include "ntp.h"
typedef struct NNC_Instance_Record *NNC_Instance;
extern NNC_Instance NNC_CreateInstance(IPSockAddr *nts_address, const char *name,
const IPSockAddr *ntp_address);
extern void NNC_DestroyInstance(NNC_Instance inst);
extern int NNC_PrepareForAuth(NNC_Instance inst);
extern int NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
NTP_PacketInfo *info);
extern int NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
NTP_PacketInfo *info);
extern void NNC_ChangeAddress(NNC_Instance inst, IPAddr *address);
#endif

253
nts_ntp_server.c Normal file
View File

@@ -0,0 +1,253 @@
/*
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.
*
**********************************************************************
=======================================================================
Server NTS-NTP authentication
*/
#include "config.h"
#include "sysincl.h"
#include "nts_ntp_server.h"
#include "conf.h"
#include "logging.h"
#include "memory.h"
#include "ntp.h"
#include "ntp_ext.h"
#include "nts_ke_server.h"
#include "nts_ntp.h"
#include "nts_ntp_auth.h"
#include "siv.h"
#include "util.h"
struct NtsServer {
SIV_Instance siv;
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
NKE_Cookie cookies[NTS_MAX_COOKIES];
int num_cookies;
NTP_int64 req_tx;
};
/* The server instance handling all requests */
struct NtsServer *server;
/* ================================================== */
void
NNS_Initialise(void)
{
/* Create an NTS-NTP server instance only if NTS-KE server is enabled */
if (!CNF_GetNtsServerCertFile() || !CNF_GetNtsServerKeyFile()) {
server = NULL;
return;
}
server = Malloc(sizeof (struct NtsServer));
server->siv = SIV_CreateInstance(AEAD_AES_SIV_CMAC_256);
}
/* ================================================== */
void
NNS_Finalise(void)
{
if (!server)
return;
SIV_DestroyInstance(server->siv);
Free(server);
server = NULL;
}
/* ================================================== */
int
NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
{
int ef_type, ef_body_length, ef_length, has_uniq_id = 0, has_auth = 0, has_cookie = 0;
int i, plaintext_length, parsed, requested_cookies, cookie_length = -1, auth_start = 0;
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
NKE_Cookie cookie;
NKE_Key c2s, s2c;
void *ef_body;
if (!server)
return 0;
*kod = 0;
server->num_cookies = 0;
server->req_tx = packet->transmit_ts;
if (info->ext_fields == 0 || info->mode != MODE_CLIENT)
return 0;
requested_cookies = 0;
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
if (!NEF_ParseField(packet, info->length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length))
break;
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))
return 0;
cookie.length = ef_body_length;
memcpy(cookie.cookie, ef_body, ef_body_length);
has_cookie = 1;
/* Fall through */
case NTP_EF_NTS_COOKIE_PLACEHOLDER:
requested_cookies++;
if (cookie_length >= 0 && cookie_length != ef_body_length) {
DEBUG_LOG("Invalid cookie/placeholder length");
return 0;
}
cookie_length = ef_body_length;
break;
case NTP_EF_NTS_AUTH_AND_EEF:
auth_start = parsed;
has_auth = 1;
break;
default:
break;
}
}
if (!has_uniq_id || !has_cookie || !has_auth) {
DEBUG_LOG("Missing an NTS EF");
return 0;
}
if (!NKS_DecodeCookie(&cookie, &c2s, &s2c)) {
*kod = NTP_KOD_NTS_NAK;
return 0;
}
if (!SIV_SetKey(server->siv, c2s.key, c2s.length)) {
DEBUG_LOG("Could not set C2S key");
return 0;
}
if (!NNA_DecryptAuthEF(packet, info, server->siv, auth_start,
plaintext, sizeof (plaintext), &plaintext_length)) {
*kod = NTP_KOD_NTS_NAK;
return 0;
}
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;
switch (ef_type) {
case NTP_EF_NTS_COOKIE_PLACEHOLDER:
if (cookie_length != ef_body_length) {
DEBUG_LOG("Invalid cookie/placeholder length");
return 0;
}
requested_cookies++;
break;
default:
break;
}
}
if (!SIV_SetKey(server->siv, s2c.key, s2c.length)) {
DEBUG_LOG("Could not set S2C key");
return 0;
}
UTI_GetRandomBytes(server->nonce, sizeof (server->nonce));
server->num_cookies = MIN(NTS_MAX_COOKIES, requested_cookies);
for (i = 0; i < server->num_cookies; i++)
if (!NKS_GenerateCookie(&c2s, &s2c, &server->cookies[i]))
return 0;
return 1;
}
/* ================================================== */
int
NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
NTP_Packet *response, NTP_PacketInfo *res_info,
uint32_t kod)
{
int i, ef_type, ef_body_length, ef_length, parsed;
void *ef_body;
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
int plaintext_length;
if (!server || req_info->mode != MODE_CLIENT || res_info->mode != MODE_SERVER)
return 0;
/* Make sure this is a response to the expected request */
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;
switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
/* Copy the ID from the request */
if (!NEF_AddField(response, res_info, ef_type, ef_body, ef_body_length))
return 0;
default:
break;
}
}
/* NTS NAK response does not have any other fields */
if (kod)
return 1;
for (i = 0, plaintext_length = 0; i < server->num_cookies; i++) {
if (!NEF_SetField(plaintext, sizeof (plaintext), plaintext_length,
NTP_EF_NTS_COOKIE, &server->cookies[i].cookie,
server->cookies[i].length, &ef_length))
return 0;
plaintext_length += ef_length;
assert(plaintext_length <= sizeof (plaintext));
}
server->num_cookies = 0;
if (!NNA_GenerateAuthEF(response, res_info, server->siv,
server->nonce, sizeof (server->nonce),
plaintext, plaintext_length,
req_info->length - res_info->length))
return 0;
return 1;
}

40
nts_ntp_server.h Normal file
View File

@@ -0,0 +1,40 @@
/*
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.
*
**********************************************************************
=======================================================================
Header file for server NTS-NTP authentication
*/
#ifndef GOT_NTS_NTP_SERVER_H
#define GOT_NTS_NTP_SERVER_H
#include "ntp.h"
extern void NNS_Initialise(void);
extern void NNS_Finalise(void);
extern int NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod);
extern int NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
NTP_Packet *response, NTP_PacketInfo *res_info,
uint32_t kod);
#endif

View File

@@ -3,6 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2002
* Copyright (C) Miroslav Lichvar 2014-2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -33,143 +34,136 @@
#include "util.h"
#include "pktlength.h"
#define PADDING_LENGTH_(request_length, reply_length) \
(uint16_t)((request_length) < (reply_length) ? (reply_length) - (request_length) : 0)
#define PADDING_LENGTH(request_data, reply_data) \
PADDING_LENGTH_(offsetof(CMD_Request, request_data), offsetof(CMD_Reply, reply_data))
#define REQ_LENGTH_ENTRY(request_data_field, reply_data_field) \
{ offsetof(CMD_Request, data.request_data_field.EOR), \
PADDING_LENGTH(data.request_data_field.EOR, data.reply_data_field.EOR) }
#define RPY_LENGTH_ENTRY(reply_data_field) \
offsetof(CMD_Reply, data.reply_data_field.EOR)
/* ================================================== */
static int
command_unpadded_length(CMD_Request *r)
{
int type;
type = ntohs(r->command);
if (type < 0 || type >= N_REQUEST_TYPES) {
return 0;
} else {
switch (type) {
case REQ_NULL:
return offsetof(CMD_Request, data.null.EOR);
case REQ_ONLINE:
return offsetof(CMD_Request, data.online.EOR);
case REQ_OFFLINE:
return offsetof(CMD_Request, data.offline.EOR);
case REQ_BURST:
return offsetof(CMD_Request, data.burst.EOR);
case REQ_MODIFY_MINPOLL:
return offsetof(CMD_Request, data.modify_minpoll.EOR);
case REQ_MODIFY_MAXPOLL:
return offsetof(CMD_Request, data.modify_maxpoll.EOR);
case REQ_DUMP:
return offsetof(CMD_Request, data.dump.EOR);
case REQ_MODIFY_MAXDELAY:
return offsetof(CMD_Request, data.modify_maxdelay.EOR);
case REQ_MODIFY_MAXDELAYRATIO:
return offsetof(CMD_Request, data.modify_maxdelayratio.EOR);
case REQ_MODIFY_MAXDELAYDEVRATIO:
return offsetof(CMD_Request, data.modify_maxdelaydevratio.EOR);
case REQ_MODIFY_MAXUPDATESKEW:
return offsetof(CMD_Request, data.modify_maxupdateskew.EOR);
case REQ_MODIFY_MAKESTEP:
return offsetof(CMD_Request, data.modify_makestep.EOR);
case REQ_LOGON :
return offsetof(CMD_Request, data.logon.EOR);
case REQ_SETTIME :
return offsetof(CMD_Request, data.settime.EOR);
case REQ_LOCAL :
return offsetof(CMD_Request, data.local.EOR);
case REQ_MANUAL :
return offsetof(CMD_Request, data.manual.EOR);
case REQ_N_SOURCES :
return offsetof(CMD_Request, data.null.EOR);
case REQ_SOURCE_DATA :
return offsetof(CMD_Request, data.source_data.EOR);
case REQ_REKEY :
return offsetof(CMD_Request, data.null.EOR);
case REQ_ALLOW :
return offsetof(CMD_Request, data.allow_deny.EOR);
case REQ_ALLOWALL :
return offsetof(CMD_Request, data.allow_deny.EOR);
case REQ_DENY :
return offsetof(CMD_Request, data.allow_deny.EOR);
case REQ_DENYALL :
return offsetof(CMD_Request, data.allow_deny.EOR);
case REQ_CMDALLOW :
return offsetof(CMD_Request, data.allow_deny.EOR);
case REQ_CMDALLOWALL :
return offsetof(CMD_Request, data.allow_deny.EOR);
case REQ_CMDDENY :
return offsetof(CMD_Request, data.allow_deny.EOR);
case REQ_CMDDENYALL :
return offsetof(CMD_Request, data.allow_deny.EOR);
case REQ_ACCHECK :
return offsetof(CMD_Request, data.ac_check.EOR);
case REQ_CMDACCHECK :
return offsetof(CMD_Request, data.ac_check.EOR);
case REQ_ADD_SERVER :
return offsetof(CMD_Request, data.ntp_source.EOR);
case REQ_ADD_PEER :
return offsetof(CMD_Request, data.ntp_source.EOR);
case REQ_DEL_SOURCE :
return offsetof(CMD_Request, data.del_source.EOR);
case REQ_WRITERTC :
return offsetof(CMD_Request, data.null.EOR);
case REQ_DFREQ :
return offsetof(CMD_Request, data.dfreq.EOR);
case REQ_DOFFSET :
return offsetof(CMD_Request, data.doffset.EOR);
case REQ_TRACKING :
return offsetof(CMD_Request, data.null.EOR);
case REQ_SOURCESTATS :
return offsetof(CMD_Request, data.sourcestats.EOR);
case REQ_RTCREPORT :
return offsetof(CMD_Request, data.null.EOR);
case REQ_TRIMRTC :
return offsetof(CMD_Request, data.null.EOR);
case REQ_CYCLELOGS :
return offsetof(CMD_Request, data.null.EOR);
case REQ_SUBNETS_ACCESSED :
case REQ_CLIENT_ACCESSES:
/* No longer supported */
return 0;
case REQ_CLIENT_ACCESSES_BY_INDEX:
return offsetof(CMD_Request, data.client_accesses_by_index.EOR);
case REQ_MANUAL_LIST:
return offsetof(CMD_Request, data.null.EOR);
case REQ_MANUAL_DELETE:
return offsetof(CMD_Request, data.manual_delete.EOR);
case REQ_MAKESTEP:
return offsetof(CMD_Request, data.null.EOR);
case REQ_ACTIVITY:
return offsetof(CMD_Request, data.null.EOR);
case REQ_RESELECT:
return offsetof(CMD_Request, data.null.EOR);
case REQ_RESELECTDISTANCE:
return offsetof(CMD_Request, data.reselect_distance.EOR);
case REQ_MODIFY_MINSTRATUM:
return offsetof(CMD_Request, data.modify_minstratum.EOR);
case REQ_MODIFY_POLLTARGET:
return offsetof(CMD_Request, data.modify_polltarget.EOR);
case REQ_SMOOTHING:
return offsetof(CMD_Request, data.null.EOR);
case REQ_SMOOTHTIME:
return offsetof(CMD_Request, data.smoothtime.EOR);
default:
/* If we fall through the switch, it most likely means we've forgotten to implement a new case */
assert(0);
}
}
struct request_length {
uint16_t command;
uint16_t padding;
};
/* Catch-all case */
return 0;
}
static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(null, null), /* NULL */
REQ_LENGTH_ENTRY(online, null), /* ONLINE */
REQ_LENGTH_ENTRY(offline, null), /* OFFLINE */
REQ_LENGTH_ENTRY(burst, null), /* BURST */
REQ_LENGTH_ENTRY(modify_minpoll, null), /* MODIFY_MINPOLL */
REQ_LENGTH_ENTRY(modify_maxpoll, null), /* MODIFY_MAXPOLL */
REQ_LENGTH_ENTRY(dump, null), /* DUMP */
REQ_LENGTH_ENTRY(modify_maxdelay, null), /* MODIFY_MAXDELAY */
REQ_LENGTH_ENTRY(modify_maxdelayratio, null), /* MODIFY_MAXDELAYRATIO */
REQ_LENGTH_ENTRY(modify_maxupdateskew, null), /* MODIFY_MAXUPDATESKEW */
REQ_LENGTH_ENTRY(logon, null), /* LOGON */
REQ_LENGTH_ENTRY(settime, manual_timestamp), /* SETTIME */
{ 0, 0 }, /* LOCAL */
REQ_LENGTH_ENTRY(manual, null), /* MANUAL */
REQ_LENGTH_ENTRY(null, n_sources), /* N_SOURCES */
REQ_LENGTH_ENTRY(source_data, source_data), /* SOURCE_DATA */
REQ_LENGTH_ENTRY(null, null), /* REKEY */
REQ_LENGTH_ENTRY(allow_deny, null), /* ALLOW */
REQ_LENGTH_ENTRY(allow_deny, null), /* ALLOWALL */
REQ_LENGTH_ENTRY(allow_deny, null), /* DENY */
REQ_LENGTH_ENTRY(allow_deny, null), /* DENYALL */
REQ_LENGTH_ENTRY(allow_deny, null), /* CMDALLOW */
REQ_LENGTH_ENTRY(allow_deny, null), /* CMDALLOWALL */
REQ_LENGTH_ENTRY(allow_deny, null), /* CMDDENY */
REQ_LENGTH_ENTRY(allow_deny, null), /* CMDDENYALL */
REQ_LENGTH_ENTRY(ac_check, null), /* ACCHECK */
REQ_LENGTH_ENTRY(ac_check, null), /* CMDACCHECK */
{ 0, 0 }, /* ADD_SERVER */
{ 0, 0 }, /* ADD_PEER */
REQ_LENGTH_ENTRY(del_source, null), /* DEL_SOURCE */
REQ_LENGTH_ENTRY(null, null), /* WRITERTC */
REQ_LENGTH_ENTRY(dfreq, null), /* DFREQ */
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET */
REQ_LENGTH_ENTRY(null, tracking), /* TRACKING */
REQ_LENGTH_ENTRY(sourcestats, sourcestats), /* SOURCESTATS */
REQ_LENGTH_ENTRY(null, rtc), /* RTCREPORT */
REQ_LENGTH_ENTRY(null, null), /* TRIMRTC */
REQ_LENGTH_ENTRY(null, null), /* CYCLELOGS */
{ 0, 0 }, /* SUBNETS_ACCESSED - not supported */
{ 0, 0 }, /* CLIENT_ACCESSES - not supported */
{ 0, 0 }, /* CLIENT_ACCESSES_BY_INDEX - not supported */
REQ_LENGTH_ENTRY(null, manual_list), /* MANUAL_LIST */
REQ_LENGTH_ENTRY(manual_delete, null), /* MANUAL_DELETE */
REQ_LENGTH_ENTRY(null, null), /* MAKESTEP */
REQ_LENGTH_ENTRY(null, activity), /* ACTIVITY */
REQ_LENGTH_ENTRY(modify_minstratum, null), /* MODIFY_MINSTRATUM */
REQ_LENGTH_ENTRY(modify_polltarget, null), /* MODIFY_POLLTARGET */
REQ_LENGTH_ENTRY(modify_maxdelaydevratio, null), /* MODIFY_MAXDELAYDEVRATIO */
REQ_LENGTH_ENTRY(null, null), /* RESELECT */
REQ_LENGTH_ENTRY(reselect_distance, null), /* RESELECTDISTANCE */
REQ_LENGTH_ENTRY(modify_makestep, null), /* MODIFY_MAKESTEP */
REQ_LENGTH_ENTRY(null, smoothing), /* SMOOTHING */
REQ_LENGTH_ENTRY(smoothtime, null), /* SMOOTHTIME */
REQ_LENGTH_ENTRY(null, null), /* REFRESH */
REQ_LENGTH_ENTRY(null, server_stats), /* SERVER_STATS */
REQ_LENGTH_ENTRY(client_accesses_by_index,
client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
REQ_LENGTH_ENTRY(local, null), /* LOCAL2 */
REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */
{ 0, 0 }, /* ADD_SERVER2 */
{ 0, 0 }, /* ADD_PEER2 */
{ 0, 0 }, /* ADD_SERVER3 */
{ 0, 0 }, /* ADD_PEER3 */
REQ_LENGTH_ENTRY(null, null), /* SHUTDOWN */
REQ_LENGTH_ENTRY(null, null), /* ONOFFLINE */
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 */
};
static const uint16_t reply_lengths[] = {
0, /* empty slot */
RPY_LENGTH_ENTRY(null), /* NULL */
RPY_LENGTH_ENTRY(n_sources), /* N_SOURCES */
RPY_LENGTH_ENTRY(source_data), /* SOURCE_DATA */
0, /* MANUAL_TIMESTAMP */
RPY_LENGTH_ENTRY(tracking), /* TRACKING */
RPY_LENGTH_ENTRY(sourcestats), /* SOURCESTATS */
RPY_LENGTH_ENTRY(rtc), /* RTC */
0, /* SUBNETS_ACCESSED - not supported */
0, /* CLIENT_ACCESSES - not supported */
0, /* CLIENT_ACCESSES_BY_INDEX - not supported */
0, /* MANUAL_LIST - not supported */
RPY_LENGTH_ENTRY(activity), /* ACTIVITY */
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */
RPY_LENGTH_ENTRY(manual_list), /* MANUAL_LIST2 */
RPY_LENGTH_ENTRY(ntp_source_name), /* NTP_SOURCE_NAME */
};
/* ================================================== */
int
PKL_CommandLength(CMD_Request *r)
{
uint32_t type;
int command_length;
command_length = command_unpadded_length(r);
assert(sizeof (request_lengths) / sizeof (request_lengths[0]) == N_REQUEST_TYPES);
type = ntohs(r->command);
if (type >= N_REQUEST_TYPES)
return 0;
command_length = request_lengths[type].command;
if (!command_length)
return 0;
@@ -178,137 +172,20 @@ PKL_CommandLength(CMD_Request *r)
/* ================================================== */
#define PADDING_LENGTH_(request_length, reply_length) \
((request_length) < (reply_length) ? (reply_length) - (request_length) : 0)
#define PADDING_LENGTH(request_data, reply_data) \
PADDING_LENGTH_(offsetof(CMD_Request, request_data), offsetof(CMD_Reply, reply_data))
int
PKL_CommandPaddingLength(CMD_Request *r)
{
int type;
uint32_t type;
if (r->version < PROTO_VERSION_PADDING)
return 0;
type = ntohs(r->command);
if (type < 0 || type >= N_REQUEST_TYPES)
if (type >= N_REQUEST_TYPES)
return 0;
switch (type) {
case REQ_NULL:
return PADDING_LENGTH(data, data.null.EOR);
case REQ_ONLINE:
return PADDING_LENGTH(data.online.EOR, data.null.EOR);
case REQ_OFFLINE:
return PADDING_LENGTH(data.offline.EOR, data.null.EOR);
case REQ_BURST:
return PADDING_LENGTH(data.burst.EOR, data.null.EOR);
case REQ_MODIFY_MINPOLL:
return PADDING_LENGTH(data.modify_minpoll.EOR, data.null.EOR);
case REQ_MODIFY_MAXPOLL:
return PADDING_LENGTH(data.modify_maxpoll.EOR, data.null.EOR);
case REQ_DUMP:
return PADDING_LENGTH(data.dump.EOR, data.null.EOR);
case REQ_MODIFY_MAXDELAY:
return PADDING_LENGTH(data.modify_maxdelay.EOR, data.null.EOR);
case REQ_MODIFY_MAXDELAYRATIO:
return PADDING_LENGTH(data.modify_maxdelayratio.EOR, data.null.EOR);
case REQ_MODIFY_MAXDELAYDEVRATIO:
return PADDING_LENGTH(data.modify_maxdelaydevratio.EOR, data.null.EOR);
case REQ_MODIFY_MAXUPDATESKEW:
return PADDING_LENGTH(data.modify_maxupdateskew.EOR, data.null.EOR);
case REQ_MODIFY_MAKESTEP:
return PADDING_LENGTH(data.modify_makestep.EOR, data.null.EOR);
case REQ_LOGON:
return PADDING_LENGTH(data.logon.EOR, data.null.EOR);
case REQ_SETTIME:
return PADDING_LENGTH(data.settime.EOR, data.manual_timestamp.EOR);
case REQ_LOCAL:
return PADDING_LENGTH(data.local.EOR, data.null.EOR);
case REQ_MANUAL:
return PADDING_LENGTH(data.manual.EOR, data.null.EOR);
case REQ_N_SOURCES:
return PADDING_LENGTH(data.null.EOR, data.n_sources.EOR);
case REQ_SOURCE_DATA:
return PADDING_LENGTH(data.source_data.EOR, data.source_data.EOR);
case REQ_REKEY:
return PADDING_LENGTH(data.null.EOR, data.null.EOR);
case REQ_ALLOW:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_ALLOWALL:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_DENY:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_DENYALL:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_CMDALLOW:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_CMDALLOWALL:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_CMDDENY:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_CMDDENYALL:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_ACCHECK:
return PADDING_LENGTH(data.ac_check.EOR, data.null.EOR);
case REQ_CMDACCHECK:
return PADDING_LENGTH(data.ac_check.EOR, data.null.EOR);
case REQ_ADD_SERVER:
return PADDING_LENGTH(data.ntp_source.EOR, data.null.EOR);
case REQ_ADD_PEER:
return PADDING_LENGTH(data.ntp_source.EOR, data.null.EOR);
case REQ_DEL_SOURCE:
return PADDING_LENGTH(data.del_source.EOR, data.null.EOR);
case REQ_WRITERTC:
return PADDING_LENGTH(data.null.EOR, data.null.EOR);
case REQ_DFREQ:
return PADDING_LENGTH(data.dfreq.EOR, data.null.EOR);
case REQ_DOFFSET:
return PADDING_LENGTH(data.doffset.EOR, data.null.EOR);
case REQ_TRACKING:
return PADDING_LENGTH(data.null.EOR, data.tracking.EOR);
case REQ_SOURCESTATS:
return PADDING_LENGTH(data.sourcestats.EOR, data.sourcestats.EOR);
case REQ_RTCREPORT:
return PADDING_LENGTH(data.null.EOR, data.rtc.EOR);
case REQ_TRIMRTC:
return PADDING_LENGTH(data.null.EOR, data.null.EOR);
case REQ_CYCLELOGS:
return PADDING_LENGTH(data.null.EOR, data.null.EOR);
case REQ_SUBNETS_ACCESSED:
case REQ_CLIENT_ACCESSES:
/* No longer supported */
return 0;
case REQ_CLIENT_ACCESSES_BY_INDEX:
return PADDING_LENGTH(data.client_accesses_by_index.EOR, data.client_accesses_by_index.EOR);
case REQ_MANUAL_LIST:
return PADDING_LENGTH(data.null.EOR, data.manual_list.EOR);
case REQ_MANUAL_DELETE:
return PADDING_LENGTH(data.manual_delete.EOR, data.null.EOR);
case REQ_MAKESTEP:
return PADDING_LENGTH(data.null.EOR, data.null.EOR);
case REQ_ACTIVITY:
return PADDING_LENGTH(data.null.EOR, data.activity.EOR);
case REQ_RESELECT:
return PADDING_LENGTH(data.null.EOR, data.null.EOR);
case REQ_RESELECTDISTANCE:
return PADDING_LENGTH(data.reselect_distance.EOR, data.null.EOR);
case REQ_MODIFY_MINSTRATUM:
return PADDING_LENGTH(data.modify_minstratum.EOR, data.null.EOR);
case REQ_MODIFY_POLLTARGET:
return PADDING_LENGTH(data.modify_polltarget.EOR, data.null.EOR);
case REQ_SMOOTHING:
return PADDING_LENGTH(data.null.EOR, data.smoothing.EOR);
case REQ_SMOOTHTIME:
return PADDING_LENGTH(data.smoothtime.EOR, data.null.EOR);
default:
/* If we fall through the switch, it most likely means we've forgotten to implement a new case */
assert(0);
return 0;
}
return request_lengths[ntohs(r->command)].padding;
}
/* ================================================== */
@@ -316,65 +193,17 @@ PKL_CommandPaddingLength(CMD_Request *r)
int
PKL_ReplyLength(CMD_Reply *r)
{
int type;
type = ntohs(r->reply);
/* Note that reply type codes start from 1, not 0 */
if (type < 1 || type >= N_REPLY_TYPES) {
return 0;
} else {
switch (type) {
case RPY_NULL:
return offsetof(CMD_Reply, data.null.EOR);
case RPY_N_SOURCES:
return offsetof(CMD_Reply, data.n_sources.EOR);
case RPY_SOURCE_DATA:
return offsetof(CMD_Reply, data.source_data.EOR);
case RPY_MANUAL_TIMESTAMP:
return offsetof(CMD_Reply, data.manual_timestamp.EOR);
case RPY_TRACKING:
return offsetof(CMD_Reply, data.tracking.EOR);
case RPY_SOURCESTATS:
return offsetof(CMD_Reply, data.sourcestats.EOR);
case RPY_RTC:
return offsetof(CMD_Reply, data.rtc.EOR);
case RPY_SUBNETS_ACCESSED :
case RPY_CLIENT_ACCESSES:
/* No longer supported */
return 0;
case RPY_CLIENT_ACCESSES_BY_INDEX:
{
unsigned long nc = ntohl(r->data.client_accesses_by_index.n_clients);
if (r->status == htons(STT_SUCCESS)) {
if (nc > MAX_CLIENT_ACCESSES)
return 0;
return (offsetof(CMD_Reply, data.client_accesses_by_index.clients) +
nc * sizeof(RPY_ClientAccesses_Client));
} else {
return offsetof(CMD_Reply, data);
}
}
case RPY_MANUAL_LIST:
{
unsigned long ns = ntohl(r->data.manual_list.n_samples);
if (ns > MAX_MANUAL_LIST_SAMPLES)
return 0;
if (r->status == htons(STT_SUCCESS)) {
return (offsetof(CMD_Reply, data.manual_list.samples) +
ns * sizeof(RPY_ManualListSample));
} else {
return offsetof(CMD_Reply, data);
}
}
case RPY_ACTIVITY:
return offsetof(CMD_Reply, data.activity.EOR);
case RPY_SMOOTHING:
return offsetof(CMD_Reply, data.smoothing.EOR);
default:
assert(0);
}
}
uint32_t type;
assert(sizeof (reply_lengths) / sizeof (reply_lengths[0]) == N_REPLY_TYPES);
type = ntohs(r->reply);
/* Note that reply type codes start from 1, not 0 */
if (type < 1 || type >= N_REPLY_TYPES)
return 0;
return reply_lengths[type];
}
/* ================================================== */

692
privops.c Normal file
View File

@@ -0,0 +1,692 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Bryan Christianson 2015
* Copyright (C) Miroslav Lichvar 2017
*
* 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.
*
**********************************************************************
=======================================================================
Perform privileged operations over a unix socket to a privileged fork.
*/
#include "config.h"
#include "sysincl.h"
#include "conf.h"
#include "nameserv.h"
#include "logging.h"
#include "privops.h"
#include "socket.h"
#include "util.h"
#define OP_ADJUSTTIME 1024
#define OP_ADJUSTTIMEX 1025
#define OP_SETTIME 1026
#define OP_BINDSOCKET 1027
#define OP_NAME2IPADDRESS 1028
#define OP_RELOADDNS 1029
#define OP_QUIT 1099
union sockaddr_in46 {
struct sockaddr_in in4;
#ifdef FEAT_IPV6
struct sockaddr_in6 in6;
#endif
struct sockaddr u;
};
/* daemon request structs */
typedef struct {
struct timeval tv;
} ReqAdjustTime;
#ifdef PRIVOPS_ADJUSTTIMEX
typedef struct {
struct timex tmx;
} ReqAdjustTimex;
#endif
typedef struct {
struct timeval tv;
} ReqSetTime;
typedef struct {
int sock;
socklen_t sa_len;
union sockaddr_in46 sa;
} ReqBindSocket;
typedef struct {
char name[256];
} ReqName2IPAddress;
typedef struct {
int op;
union {
ReqAdjustTime adjust_time;
#ifdef PRIVOPS_ADJUSTTIMEX
ReqAdjustTimex adjust_timex;
#endif
ReqSetTime set_time;
ReqBindSocket bind_socket;
#ifdef PRIVOPS_NAME2IPADDRESS
ReqName2IPAddress name_to_ipaddress;
#endif
} data;
} PrvRequest;
/* helper response structs */
typedef struct {
struct timeval tv;
} ResAdjustTime;
#ifdef PRIVOPS_ADJUSTTIMEX
typedef struct {
struct timex tmx;
} ResAdjustTimex;
#endif
typedef struct {
IPAddr addresses[DNS_MAX_ADDRESSES];
} ResName2IPAddress;
typedef struct {
char msg[256];
} ResFatalMsg;
typedef struct {
int fatal_error;
int rc;
int res_errno;
union {
ResFatalMsg fatal_msg;
ResAdjustTime adjust_time;
#ifdef PRIVOPS_ADJUSTTIMEX
ResAdjustTimex adjust_timex;
#endif
#ifdef PRIVOPS_NAME2IPADDRESS
ResName2IPAddress name_to_ipaddress;
#endif
} data;
} PrvResponse;
static int helper_fd;
static pid_t helper_pid;
static int
have_helper(void)
{
return helper_fd >= 0;
}
/* ======================================================================= */
/* HELPER - prepare fatal error for daemon */
static void
res_fatal(PrvResponse *res, const char *fmt, ...)
{
va_list ap;
res->fatal_error = 1;
va_start(ap, fmt);
vsnprintf(res->data.fatal_msg.msg, sizeof (res->data.fatal_msg.msg), fmt, ap);
va_end(ap);
}
/* ======================================================================= */
/* HELPER - send response to the fd */
static int
send_response(int fd, const PrvResponse *res)
{
if (SCK_Send(fd, res, sizeof (*res), 0) != sizeof (*res))
return 0;
return 1;
}
/* ======================================================================= */
/* receive daemon request plus optional file descriptor over a unix socket */
static int
receive_from_daemon(int fd, PrvRequest *req)
{
SCK_Message message;
if (!SCK_ReceiveMessage(fd, &message, SCK_FLAG_MSG_DESCRIPTOR) ||
message.length != sizeof (*req))
return 0;
memcpy(req, message.data, sizeof (*req));
if (req->op == OP_BINDSOCKET) {
req->data.bind_socket.sock = message.descriptor;
/* return error if valid descriptor not found */
if (req->data.bind_socket.sock < 0)
return 0;
} else if (message.descriptor >= 0) {
SCK_CloseSocket(message.descriptor);
return 0;
}
return 1;
}
/* ======================================================================= */
/* HELPER - perform adjtime() */
#ifdef PRIVOPS_ADJUSTTIME
static void
do_adjust_time(const ReqAdjustTime *req, PrvResponse *res)
{
res->rc = adjtime(&req->tv, &res->data.adjust_time.tv);
if (res->rc)
res->res_errno = errno;
}
#endif
/* ======================================================================= */
/* HELPER - perform ntp_adjtime() */
#ifdef PRIVOPS_ADJUSTTIMEX
static void
do_adjust_timex(const ReqAdjustTimex *req, PrvResponse *res)
{
res->data.adjust_timex.tmx = req->tmx;
res->rc = ntp_adjtime(&res->data.adjust_timex.tmx);
if (res->rc < 0)
res->res_errno = errno;
}
#endif
/* ======================================================================= */
/* HELPER - perform settimeofday() */
#ifdef PRIVOPS_SETTIME
static void
do_set_time(const ReqSetTime *req, PrvResponse *res)
{
res->rc = settimeofday(&req->tv, NULL);
if (res->rc)
res->res_errno = errno;
}
#endif
/* ======================================================================= */
/* HELPER - perform bind() */
#ifdef PRIVOPS_BINDSOCKET
static void
do_bind_socket(ReqBindSocket *req, PrvResponse *res)
{
IPSockAddr ip_saddr;
int sock_fd;
struct sockaddr *sa;
socklen_t sa_len;
sa = &req->sa.u;
sa_len = req->sa_len;
sock_fd = req->sock;
SCK_SockaddrToIPSockAddr(sa, sa_len, &ip_saddr);
if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() &&
ip_saddr.port != CNF_GetAcquisitionPort()) {
SCK_CloseSocket(sock_fd);
res_fatal(res, "Invalid port %d", ip_saddr.port);
return;
}
res->rc = bind(sock_fd, sa, sa_len);
if (res->rc)
res->res_errno = errno;
/* sock is still open on daemon side, but we're done with it in the helper */
SCK_CloseSocket(sock_fd);
}
#endif
/* ======================================================================= */
/* HELPER - perform DNS_Name2IPAddress() */
#ifdef PRIVOPS_NAME2IPADDRESS
static void
do_name_to_ipaddress(ReqName2IPAddress *req, PrvResponse *res)
{
/* make sure the string is terminated */
req->name[sizeof (req->name) - 1] = '\0';
res->rc = DNS_Name2IPAddress(req->name, res->data.name_to_ipaddress.addresses,
DNS_MAX_ADDRESSES);
}
#endif
/* ======================================================================= */
/* HELPER - perform DNS_Reload() */
#ifdef PRIVOPS_RELOADDNS
static void
do_reload_dns(PrvResponse *res)
{
DNS_Reload();
res->rc = 0;
}
#endif
/* ======================================================================= */
/* HELPER - main loop - action requests from the daemon */
static void
helper_main(int fd)
{
PrvRequest req;
PrvResponse res;
int quit = 0;
while (!quit) {
if (!receive_from_daemon(fd, &req))
/* read error or closed input - we cannot recover - give up */
break;
memset(&res, 0, sizeof (res));
switch (req.op) {
#ifdef PRIVOPS_ADJUSTTIME
case OP_ADJUSTTIME:
do_adjust_time(&req.data.adjust_time, &res);
break;
#endif
#ifdef PRIVOPS_ADJUSTTIMEX
case OP_ADJUSTTIMEX:
do_adjust_timex(&req.data.adjust_timex, &res);
break;
#endif
#ifdef PRIVOPS_SETTIME
case OP_SETTIME:
do_set_time(&req.data.set_time, &res);
break;
#endif
#ifdef PRIVOPS_BINDSOCKET
case OP_BINDSOCKET:
do_bind_socket(&req.data.bind_socket, &res);
break;
#endif
#ifdef PRIVOPS_NAME2IPADDRESS
case OP_NAME2IPADDRESS:
do_name_to_ipaddress(&req.data.name_to_ipaddress, &res);
break;
#endif
#ifdef PRIVOPS_RELOADDNS
case OP_RELOADDNS:
do_reload_dns(&res);
break;
#endif
case OP_QUIT:
quit = 1;
continue;
default:
res_fatal(&res, "Unexpected operator %d", req.op);
break;
}
send_response(fd, &res);
}
SCK_CloseSocket(fd);
exit(0);
}
/* ======================================================================= */
/* DAEMON - receive helper response */
static void
receive_response(PrvResponse *res)
{
int resp_len;
resp_len = SCK_Receive(helper_fd, res, sizeof (*res), 0);
if (resp_len < 0)
LOG_FATAL("Could not read from helper : %s", strerror(errno));
if (resp_len != sizeof (*res))
LOG_FATAL("Invalid helper response");
if (res->fatal_error)
LOG_FATAL("Error in helper : %s", res->data.fatal_msg.msg);
DEBUG_LOG("Received response rc=%d", res->rc);
/* if operation failed in the helper, set errno so daemon can print log message */
if (res->res_errno)
errno = res->res_errno;
}
/* ======================================================================= */
/* DAEMON - send daemon request to the helper */
static void
send_request(PrvRequest *req)
{
SCK_Message message;
int flags;
SCK_InitMessage(&message, SCK_ADDR_UNSPEC);
message.data = req;
message.length = sizeof (*req);
flags = 0;
if (req->op == OP_BINDSOCKET) {
/* send file descriptor as a control message */
message.descriptor = req->data.bind_socket.sock;
flags |= SCK_FLAG_MSG_DESCRIPTOR;
}
if (!SCK_SendMessage(helper_fd, &message, flags)) {
/* don't try to send another request from exit() */
helper_fd = -1;
LOG_FATAL("Could not send to helper : %s", strerror(errno));
}
DEBUG_LOG("Sent request op=%d", req->op);
}
/* ======================================================================= */
/* DAEMON - send daemon request and wait for response */
static void
submit_request(PrvRequest *req, PrvResponse *res)
{
send_request(req);
receive_response(res);
}
/* ======================================================================= */
/* DAEMON - send the helper a request to exit and wait until it exits */
static void
stop_helper(void)
{
PrvRequest req;
int status;
if (!have_helper())
return;
memset(&req, 0, sizeof (req));
req.op = OP_QUIT;
send_request(&req);
waitpid(helper_pid, &status, 0);
}
/* ======================================================================= */
/* DAEMON - request adjtime() */
#ifdef PRIVOPS_ADJUSTTIME
int
PRV_AdjustTime(const struct timeval *delta, struct timeval *olddelta)
{
PrvRequest req;
PrvResponse res;
if (!have_helper() || delta == NULL)
/* helper is not running or read adjustment call */
return adjtime(delta, olddelta);
memset(&req, 0, sizeof (req));
req.op = OP_ADJUSTTIME;
req.data.adjust_time.tv = *delta;
submit_request(&req, &res);
if (olddelta)
*olddelta = res.data.adjust_time.tv;
return res.rc;
}
#endif
/* ======================================================================= */
/* DAEMON - request ntp_adjtime() */
#ifdef PRIVOPS_ADJUSTTIMEX
int
PRV_AdjustTimex(struct timex *tmx)
{
PrvRequest req;
PrvResponse res;
if (!have_helper())
return ntp_adjtime(tmx);
memset(&req, 0, sizeof (req));
req.op = OP_ADJUSTTIMEX;
req.data.adjust_timex.tmx = *tmx;
submit_request(&req, &res);
*tmx = res.data.adjust_timex.tmx;
return res.rc;
}
#endif
/* ======================================================================= */
/* DAEMON - request settimeofday() */
#ifdef PRIVOPS_SETTIME
int
PRV_SetTime(const struct timeval *tp, const struct timezone *tzp)
{
PrvRequest req;
PrvResponse res;
/* only support setting the time */
assert(tp != NULL);
assert(tzp == NULL);
if (!have_helper())
return settimeofday(tp, NULL);
memset(&req, 0, sizeof (req));
req.op = OP_SETTIME;
req.data.set_time.tv = *tp;
submit_request(&req, &res);
return res.rc;
}
#endif
/* ======================================================================= */
/* DAEMON - request bind() */
#ifdef PRIVOPS_BINDSOCKET
int
PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len)
{
IPSockAddr ip_saddr;
PrvRequest req;
PrvResponse res;
SCK_SockaddrToIPSockAddr(address, address_len, &ip_saddr);
if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() &&
ip_saddr.port != CNF_GetAcquisitionPort())
assert(0);
if (!have_helper())
return bind(sock, address, address_len);
memset(&req, 0, sizeof (req));
req.op = OP_BINDSOCKET;
req.data.bind_socket.sock = sock;
req.data.bind_socket.sa_len = address_len;
assert(address_len <= sizeof (req.data.bind_socket.sa));
memcpy(&req.data.bind_socket.sa.u, address, address_len);
submit_request(&req, &res);
return res.rc;
}
#endif
/* ======================================================================= */
/* DAEMON - request DNS_Name2IPAddress() */
#ifdef PRIVOPS_NAME2IPADDRESS
int
PRV_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
{
PrvRequest req;
PrvResponse res;
int i;
if (!have_helper())
return DNS_Name2IPAddress(name, ip_addrs, max_addrs);
memset(&req, 0, sizeof (req));
req.op = OP_NAME2IPADDRESS;
if (snprintf(req.data.name_to_ipaddress.name, sizeof (req.data.name_to_ipaddress.name),
"%s", name) >= sizeof (req.data.name_to_ipaddress.name)) {
return DNS_Failure;
}
submit_request(&req, &res);
for (i = 0; i < max_addrs && i < DNS_MAX_ADDRESSES; i++)
ip_addrs[i] = res.data.name_to_ipaddress.addresses[i];
return res.rc;
}
#endif
/* ======================================================================= */
/* DAEMON - request res_init() */
#ifdef PRIVOPS_RELOADDNS
void
PRV_ReloadDNS(void)
{
PrvRequest req;
PrvResponse res;
if (!have_helper()) {
DNS_Reload();
return;
}
memset(&req, 0, sizeof (req));
req.op = OP_RELOADDNS;
submit_request(&req, &res);
assert(!res.rc);
}
#endif
/* ======================================================================= */
void
PRV_Initialise(void)
{
helper_fd = -1;
}
/* ======================================================================= */
/* DAEMON - setup socket(s) then fork to run the helper */
/* must be called before privileges are dropped */
void
PRV_StartHelper(void)
{
pid_t pid;
int fd, sock_fd1, sock_fd2;
if (have_helper())
LOG_FATAL("Helper already running");
sock_fd1 = SCK_OpenUnixSocketPair(SCK_FLAG_BLOCK, &sock_fd2);
pid = fork();
if (pid < 0)
LOG_FATAL("fork() failed : %s", strerror(errno));
if (pid == 0) {
/* child process */
SCK_CloseSocket(sock_fd1);
/* close other descriptors inherited from the parent process, except
stdin, stdout, and stderr */
for (fd = STDERR_FILENO + 1; fd < 1024; fd++) {
if (fd != sock_fd2)
close(fd);
}
/* ignore signals, the process will exit on OP_QUIT request */
UTI_SetQuitSignalsHandler(SIG_IGN, 1);
helper_main(sock_fd2);
} else {
/* parent process */
SCK_CloseSocket(sock_fd2);
helper_fd = sock_fd1;
helper_pid = pid;
/* stop the helper even when not exiting cleanly from the main function */
atexit(stop_helper);
}
}
/* ======================================================================= */
/* DAEMON - graceful shutdown of the helper */
void
PRV_Finalise(void)
{
if (!have_helper())
return;
stop_helper();
close(helper_fd);
helper_fd = -1;
}

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