Compare commits

...

29 Commits
4.7 ... 4.8

Author SHA1 Message Date
Miroslav Lichvar
9e8541e3c4 sys_linux: improve error message for failed PHC open
If the specified PHC device cannot be opened directly, an attempt is
made to open it as a network interface. When that fails, the error
"Could not open PHC of iface" is misleading the user that it was handled
only as an interface. Change the message to "Could not open PHC (of)" to
better cover both possibilities. Also remove the errno as it's not set
in all code paths.
2025-08-27 14:05:31 +02:00
Miroslav Lichvar
e95d5a161d test: avoid using cmdport equal to ntpport in system tests
Make sure the two randomly generated port numbers used in system tests
are different to avoid failures.
2025-08-27 14:05:27 +02:00
Miroslav Lichvar
2c63dfee34 doc: update URL in test documentation 2025-08-26 12:34:22 +02:00
Miroslav Lichvar
42e6b5577a test: check credentials in nts_ke_session test
Make it more clear when the test fails because the credentials could not
be created.
2025-08-26 12:33:14 +02:00
Miroslav Lichvar
830c8bb18a util: switch create_dir() from chown() to lchown()
Use lchown(), the safer variant of chown() that does not follow
symlinks, when changing the ownership of a created directory (logdir,
dumpdir, ntsdumpdir, and the directory of bindcmdaddress) to the chrony
user.
2025-08-26 12:32:33 +02:00
Miroslav Lichvar
0289442998 client: fix sizeof in open_unix_socket()
Fix one of the sizeofs in open_unix_socket() to correctly specify
sock_dir2 instead of sock_dir1. They have the same size, but don't rely
on that.

Fixes: 90d808ed28 ("client: mitigate unsafe permissions change on chronyc socket")
2025-08-26 09:59:37 +02:00
Miroslav Lichvar
e9848c0176 doc: update NEWS 2025-08-14 15:55:15 +02:00
Miroslav Lichvar
2cfe969940 doc: update credits in README 2025-08-14 15:30:27 +02:00
Miroslav Lichvar
487cf3840f doc: update FAQ 2025-08-14 15:30:27 +02:00
Miroslav Lichvar
4886c776d5 update copyright years 2025-08-14 15:30:27 +02:00
Miroslav Lichvar
d3f3638b3d util: avoid compiler warning in UTI_IPSockAddrToString()
Don't print directly a buffer of the pool to another buffer of
the pool to avoid a -Wrestrict warning produced by a recent gcc version.
2025-08-14 15:30:27 +02:00
Miroslav Lichvar
6c5973741b configure: fix compiler warnings in system function checks
Avoid using (void *)1 as an output buffer to fix detection of supported
system functions due to -Werror and -Wstringop-overflow etc.
2025-08-14 14:25:38 +02:00
Miroslav Lichvar
51d161a028 refclock: rework update of reachability again
The recent rework of refclock reachability to better work with
driver-specific filtering (PHC driver dropping samples with unexpected
delay) introduced an issue that a PPS refclock is indicated as reachable
even when its "lock" refclock is permanently unreachable, or its samples
constistently fail in other sample checks, and no actual samples can be
accumulated. This breaks the new maxunreach option.

Rework the refclock code to provide samples from drivers together with
their quality level (all drivers except PHC provide samples with
constant quality of 1) and drop samples with quality 0 after passing
all checks, right before the actual accumulation in the median sample
filter. Increment the reachability counter only for samples that would
be accumulated.

This fixes the problem with refclocks indicated as reachable when their
samples would be dropped for other reasons than the PHC-specific delay
filter, and the maxunreach option can work as expected.

Fixes: b9b338a8df ("refclock: rework update of reachability")
2025-08-14 14:25:38 +02:00
Miroslav Lichvar
5535384878 hwclock: don't drop valid samples in HCL_ProcessReadings()
Modify the HCL_ProcessReadings() function to try to always provide
a valid sample. Instead of dropping a sample outside of the expected
delay, provide its assumed quality level as a small integer (relative to
already accumulated samples), and let the caller decide what quality is
acceptable.
2025-08-14 14:24:54 +02:00
Miroslav Lichvar
f78e4681ef refclock_phc: open device for writing with extpps option
In version 6.15 the Linux kernel started checking write access on the
PHC file descriptor in the PTP_PIN_SETFUNC and PTP_EXTTS_REQUEST ioctls.
chronyd opened the PHC device as readonly, which caused the PHC refclock
driver configured with the extpps option to fail with the
"Could not enable external PHC timestamping" error message.

To ensure compatibility with new kernel versions, add flags to the
SYS_Linux_OpenPHC() function and open the device with the O_RDWR flag
when the extpps option is enabled.
2025-08-07 14:43:37 +02:00
Miroslav Lichvar
b365edb48e tls: don't accept NULL ALPN name in TLS_CreateInstance()
The TLS_CreateInstance() function handles a NULL alpn_name, but the
other session functions would crash if it was NULL. Change the function
to not handle the NULL for consistency and avoid potential confusion.

Fixes: 3e32e7e694 ("tls: move gnutls code into tls_gnutls.c")
2025-08-07 10:18:31 +02:00
Miroslav Lichvar
93a78c73ad tls: fix server log messages to have client IP address
Add an additional parameter to TLS_CreateInstance() to save the label of
the connection (server name on the client side and client IP
address:port on the server side) instead of the server name (which is
NULL on the server side) to fix the log messages.

Fixes: 3e32e7e694 ("tls: move gnutls code into tls_gnutls.c")
2025-08-07 10:18:31 +02:00
Miroslav Lichvar
abc267a556 tls: don't call gnutls_deinit() after failed gnutls_init()
Don't assume gnutls_init() leaves the session pointer at NULL when it
returns with an error status. It might be a session that was already
allocated and then freed without resetting it to NULL after an error.

Fixes: 3e32e7e694 ("tls: move gnutls code into tls_gnutls.c")
2025-08-07 10:18:31 +02:00
Miroslav Lichvar
9b183fe98f sources: add option to limit selection of unreachable sources
Add maxunreach option to NTP sources and refclocks to specify the
maximum number of polls that the source can stay selected for
synchronization when it is unreachable (i.e. no valid sample was
received in the last 8 polls).

It is an additional requirement to having at least one sample more
recent than the oldest sample of reachable sources.

The default value is 100000. Setting the option to 0 disables selection
of unreachable sources, which matches RFC 5905.
2025-08-07 10:18:31 +02:00
Miroslav Lichvar
be7f5e8916 client: add support for dropping root privileges
To minimize the impact of potential attacks targeting chronyc started
under root (e.g. performed by a local chronyd process running without
root privileges, a remote chronyd process, or a MITM attacker on the
network), add support for changing the effective UID/GID in chronyc
after start.

The user can be specified by the -u option, similarly to chronyd. The
default chronyc user can be changed by the --with-chronyc-user
configure option. The default value of the default chronyc user is
"root", i.e. chronyc doesn't try to change the identity by default.

The default chronyc user does not follow the default chronyd user
set by the configure --with-user option to avoid errors on systems where
chronyc is not allowed to change its UID/GID (e.g. by a SELinux policy).
2025-08-07 10:18:31 +02:00
Miroslav Lichvar
5e2cd47ad1 test: fix system tests to change also tempcomp owner 2025-07-30 14:57:01 +02:00
Miroslav Lichvar
9eaf8bc521 socket: rename sun variable to fix compilation on illumos
"sun" is reserved on Solaris/illumos.

Fixes: 3dea7dd723 ("socket: rework setting of struct sockaddr_un")
2025-07-30 14:46:59 +02:00
Miroslav Lichvar
54010586aa socket: remove unused chmod() call
Drop the SCK_FLAG_ALL_PERMISSIONS support from the socket code.
chronyc is now calling chmod() on its socket itself in a hidden
directory to mitigate the unsafe operation.
2025-07-30 14:46:59 +02:00
Miroslav Lichvar
90d808ed28 client: mitigate unsafe permissions change on chronyc socket
When chronyc running under root binds its Unix domain socket, it needs
to change the socket permissions in order for chronyd running without
root privileges to be able to send a response to the socket.

There is a race condition between the bind() and chmod() calls. If an
attacker was able to execute arbitrary code in the chronyd process, it
might be able to wait for chronyc to be executed under root, replace the
socket with a symlink between the two calls, and cause the privileged
chronyc process to change permissions of something else, possibly
leading to a privilege escalation.

There doesn't seem to be a safe and portable way to change the socket
permissions directly. Changing the process umask could be problematic in
future with threads.

Hide the socket in two levels of subdirectories (the lower one having
a randomly generated name and not visible to the chronyd process) to
make the socket path unpredictable, and force the bind() or chmod() call
to fail if the visible upper directory is replaced.

Reported-by: Matthias Gerstner <mgerstner@suse.de>
2025-07-30 14:46:59 +02:00
Miroslav Lichvar
1d9e080749 util: warn if UTI_OpenFile() is stuck in a loop
When UTI_OpenFile() is removing an existing file to be replaced by a new
file, it could potentially get stuck in an infinite loop if something
was able to consistently win the race and create a new file before
chronyd.

Log a warning message after 100 failed attempts and repeat on each 10x
increase to make it more obvious to the admin, if it ever happens.

Reported-by: Eric Sesterhenn <eric.sesterhenn@x41-dsec.de>
2025-07-02 16:02:41 +02:00
Miroslav Lichvar
d30913e78c keys: remove misleading memset()
After (re)loading symmetric NTP keys from the key file, there is an
attempt to erase the strings from the stack by calling memset() on the
buffer. However, compilers are free (and have been shown to do) optimize
this call out.

Remove the memset() call to not pretend the stack cannot not contain any
sensitive information. There is no such attempt made for the server and
client NTS keys.

Reported-by: Eric Sesterhenn <eric.sesterhenn@x41-dsec.de>
2025-07-02 16:02:31 +02:00
Ahmad Fatoum
c5d3be8cc4 leapdb: fix ordered comparison against NULL pointer
fgets returns either a valid pointer with the same value as its first
argument or NULL on error or EOF.

GCC 12.2.0 -Wextra warns against relational comparison of the return
value:

  leapdb.c:127:38: warning: ordered comparison of pointer with integer zero [-Wextra]

For clarity, and because the C standard doesn't mandate that valid pointers
have to compare greater than the null pointer constant, replace the
relational expression with an equality expression
2025-07-02 14:49:21 +02:00
Anthony Brandon
3e32e7e694 tls: move gnutls code into tls_gnutls.c
Currently nts_ke_session.c directly calls into gnutls.
This patch moves the calls to gnutls into tls_gnutls.c with an API
defined in tls.h. This way it becomes possible to use different TLS
implementations in future patches.

Signed-off-by: Anthony Brandon <anthony@amarulasolutions.com>
2025-06-26 15:53:41 +02:00
Miroslav Lichvar
52cce3dea8 sys_linux: drop support for kernels before 2.6.39
Linux 2.6.39 was released in 2011.

Refuse to start if a kernel version before 2.6.39 is detected. Assume
the ADJ_SETOFFSET adjtimex mode is always supported. Its verification
briefly reset the timex maxerror value to 0, which possibly confused
applications checking the value at that moment.

Drop the unneeded workaround for slow frequency updates in versions
2.6.27-2.6.32.
2025-06-24 15:49:33 +02:00
44 changed files with 1011 additions and 452 deletions

15
NEWS
View File

@@ -1,3 +1,18 @@
New in version 4.8
==================
Enhancements
------------
* Add maxunreach option to limit selection of unreachable sources
* Add -u option to chronyc to drop root privileges (default chronyc
user is set by configure script)
Bug fixes
---------
* Hide chronyc socket to mitigate unsafe permissions change
* Fix refclock extpps option to work on Linux >= 6.15
* Validate refclock samples for reachability updates
New in version 4.7
==================

1
README
View File

@@ -78,6 +78,7 @@ Vincent Blut <vincent.debian@free.fr>
Luca Boccassi <bluca@debian.org>
Stephan I. Boettcher <stephan@nevis1.columbia.edu>
David Bohman <debohman@gmail.com>
Anthony Brandon <anthony@amarulasolutions.com>
Goswin Brederlow <brederlo@informatik.uni-tuebingen.de>
Leigh Brown <leigh@solinno.co.uk>
Erik Bryer <ebryer@spots.ab.ca>

View File

@@ -306,7 +306,7 @@ typedef struct {
int32_t filter_length;
uint32_t cert_set;
Float max_delay_quant;
uint32_t reserved[1];
int32_t max_unreach;
int32_t EOR;
} REQ_NTP_Source;

177
client.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Lonnie Abelbeck 2016, 2018
* Copyright (C) Miroslav Lichvar 2009-2024
* Copyright (C) Miroslav Lichvar 2009-2025
*
* 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
@@ -47,6 +47,8 @@
#include <editline/readline.h>
#endif
#define MAX_UNIX_SOCKET_LENGTH (sizeof ((struct sockaddr_un *)NULL)->sun_path)
/* ================================================== */
struct Address {
@@ -61,6 +63,9 @@ static ARR_Instance server_addresses;
static int sock_fd = -1;
static char sock_dir1[MAX_UNIX_SOCKET_LENGTH];
static char sock_dir2[MAX_UNIX_SOCKET_LENGTH];
static volatile int quit = 0;
static int on_terminal = 0;
@@ -207,35 +212,139 @@ free_addresses(ARR_Instance addresses)
ARR_DestroyInstance(addresses);
}
/* ================================================== */
/* Bind a Unix domain socket and connect it to the server chronyd socket.
Access to the sockets is restricted by the permissions and ownership
of the directory in which they are bound (by default /var/run/chrony).
The location of the chronyc socket follows the location of the chronyd
socket. Due to the possibility of chronyc running under a different
user, the permissions of the chronyc socket need to be loosened on
most systems (illumos is an exception) to allow chronyd to send a
response to the chronyc socket.
Unfortunately, there does not seem to be a safe and portable way to
do that directly (e.g. a compromised chronyd process could replace it
with a symlink and cause a privileged chronyc process to change the
permissions of something else).
The workaround is to bind the socket in a randomly named subdirectory
hidden in another subdirectory to avoid matching an existing path and
force the bind()/chmod() call to fail if the visible upper directory
is replaced.
Note that the socket cannot be moved once bound, because the source
address that chronyd will see would not match the actual path and the
responses would not reach chronyc. */
static int
open_unix_socket(char *server_path)
{
char *sock_dir0, sock_path[MAX_UNIX_SOCKET_LENGTH], rand_dir[16 + 1];
int i, s, dir_fd1 = -1;
struct stat st;
/* Check if the server socket is accessible */
s = SCK_OpenUnixDatagramSocket(server_path, NULL, 0);
if (s < 0)
return -1;
SCK_CloseSocket(s);
/* Generate the random hidden component of the socket path */
for (i = 0; i + 1 < sizeof (rand_dir); i++) {
do
UTI_GetRandomBytesUrandom(&rand_dir[i], sizeof (rand_dir[i]));
while (!isalnum((unsigned char)rand_dir[i]));
}
rand_dir[i] = '\0';
/* Format the paths of the subdirectories and socket:
sock_dir0 = dirname(server_path)
sock_dir1 = sock_dir0/chronyc.$PID
sock_dir2 = sock_dir0/chronyc.$PID/$RANDOM
sock_path = sock_dir0/chronyc.$PID/$RANDOM/sock */
sock_dir0 = UTI_PathToDir(server_path);
if (snprintf(sock_dir1, sizeof (sock_dir1),
"%s/chronyc.%d", sock_dir0, (int)getpid()) >= sizeof (sock_dir1) ||
snprintf(sock_dir2, sizeof (sock_dir2),
"%s/%s", sock_dir1, rand_dir) >= sizeof (sock_dir2) ||
snprintf(sock_path, sizeof (sock_path),
"%s/sock", sock_dir2) >= sizeof (sock_path)) {
LOG(LOGS_ERR, "Server socket path %s is too long", server_path);
Free(sock_dir0);
goto error1;
}
Free(sock_dir0);
if (mkdir(sock_dir1, 0711) < 0 && errno != EEXIST) {
LOG(LOGS_ERR, "Could not create %s : %s", sock_dir1, strerror(errno));
goto error1;
}
dir_fd1 = open(sock_dir1, O_RDONLY | O_NOFOLLOW);
if (dir_fd1 < 0 || fstat(dir_fd1, &st) < 0) {
LOG(LOGS_ERR, "Could not open/stat %s : %s", sock_dir1, strerror(errno));
goto error2;
}
if (!S_ISDIR(st.st_mode) || (st.st_mode & 0777 & ~0711) != 0 || st.st_uid != geteuid()) {
LOG(LOGS_ERR, "Unexpected mode/owner of %s", sock_dir1);
goto error2;
}
if (mkdirat(dir_fd1, rand_dir, 0711) < 0) {
LOG(LOGS_ERR, "Could not create %s : %s", sock_dir2, strerror(errno));
goto error2;
}
s = SCK_OpenUnixDatagramSocket(server_path, sock_path, 0);
if (s < 0) {
LOG(LOGS_ERR, "Could not bind/connect client Unix socket");
goto error3;
}
if (chmod(sock_path, 0666) < 0 ||
chmod(sock_dir2, 0711) < 0 ||
fchmod(dir_fd1, 0711) < 0) {
LOG(LOGS_ERR, "Could not change socket or directory permissions : %s", strerror(errno));
SCK_RemoveSocket(s);
SCK_CloseSocket(s);
goto error3;
}
close(dir_fd1);
return s;
error3:
rmdir(sock_dir2);
error2:
rmdir(sock_dir1);
error1:
if (dir_fd1 >= 0)
close(dir_fd1);
sock_dir1[0] = '\0';
sock_dir2[0] = '\0';
return -1;
}
/* ================================================== */
/* Initialise the socket used to talk to the daemon */
static int
open_socket(struct Address *addr)
{
char *dir, *local_addr;
size_t local_addr_len;
switch (addr->type) {
case SCK_ADDR_IP:
sock_fd = SCK_OpenUdpSocket(&addr->addr.ip, NULL, NULL, 0);
break;
case SCK_ADDR_UNIX:
/* Construct path of our socket. Use the same directory as the server
socket and include our process ID to allow multiple chronyc instances
running at the same time. */
dir = UTI_PathToDir(addr->addr.path);
local_addr_len = strlen(dir) + 50;
local_addr = Malloc(local_addr_len);
snprintf(local_addr, local_addr_len, "%s/chronyc.%d.sock", dir, (int)getpid());
sock_fd = SCK_OpenUnixDatagramSocket(addr->addr.path, local_addr,
SCK_FLAG_ALL_PERMISSIONS);
Free(dir);
Free(local_addr);
sock_fd = open_unix_socket(addr->addr.path);
break;
default:
assert(0);
@@ -257,6 +366,14 @@ close_io(void)
SCK_RemoveSocket(sock_fd);
SCK_CloseSocket(sock_fd);
if (sock_dir2[0] != '\0')
rmdir(sock_dir2);
if (sock_dir1[0] != '\0')
rmdir(sock_dir1);
sock_dir2[0] = '\0';
sock_dir1[0] = '\0';
sock_fd = -1;
}
@@ -987,7 +1104,7 @@ process_cmd_add_source(CMD_Request *msg, char *line)
msg->data.ntp_source.cert_set = htonl(data.params.cert_set);
msg->data.ntp_source.max_delay_quant =
UTI_FloatHostToNetwork(data.params.max_delay_quant);
memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved));
msg->data.ntp_source.max_unreach = htonl(data.params.max_unreach);
result = 1;
@@ -3473,9 +3590,11 @@ print_help(const char *progname)
" -m\t\tAccept multiple commands\n"
" -h HOST\tSpecify server (%s)\n"
" -p PORT\tSpecify UDP port (%d)\n"
" -u USER\tSpecify user (%s)\n"
" -v, --version\tPrint version and exit\n"
" --help\tPrint usage and exit\n",
progname, DEFAULT_COMMAND_SOCKET",127.0.0.1,::1", DEFAULT_CANDM_PORT);
progname, DEFAULT_COMMAND_SOCKET",127.0.0.1,::1",
DEFAULT_CANDM_PORT, DEFAULT_CHRONYC_USER);
}
/* ================================================== */
@@ -3492,10 +3611,11 @@ int
main(int argc, char **argv)
{
char *line;
const char *hostnames = NULL, *user = DEFAULT_CHRONYC_USER;
const char *progname = argv[0];
const char *hostnames = NULL;
int opt, ret = 1, multi = 0, family = IPADDR_UNSPEC;
int port = DEFAULT_CANDM_PORT;
struct passwd *pw;
/* Parse long command-line options */
for (optind = 1; optind < argc; optind++) {
@@ -3511,7 +3631,7 @@ main(int argc, char **argv)
optind = 1;
/* Parse short command-line options */
while ((opt = getopt(argc, argv, "+46acdef:h:mnNp:v")) != -1) {
while ((opt = getopt(argc, argv, "+46acdef:h:mnNp:u:v")) != -1) {
switch (opt) {
case '4':
case '6':
@@ -3547,6 +3667,9 @@ main(int argc, char **argv)
case 'p':
port = atoi(optarg);
break;
case 'u':
user = optarg;
break;
case 'v':
print_version();
return 0;
@@ -3556,6 +3679,14 @@ main(int argc, char **argv)
}
}
/* Drop root privileges if configured to do so */
if (strcmp(user, "root") != 0 && geteuid() == 0) {
pw = getpwnam(user);
if (!pw)
LOG_FATAL("Could not get user/group ID of %s", user);
UTI_DropRoot(pw->pw_uid, pw->pw_gid);
}
if (isatty(0) && isatty(1) && isatty(2)) {
on_terminal = 1;
}

View File

@@ -686,6 +686,7 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
params.max_sources = ntohl(rx_message->data.ntp_source.max_sources);
params.min_samples = ntohl(rx_message->data.ntp_source.min_samples);
params.max_samples = ntohl(rx_message->data.ntp_source.max_samples);
params.max_unreach = ntohl(rx_message->data.ntp_source.max_unreach);
params.filter_length = ntohl(rx_message->data.ntp_source.filter_length);
params.authkey = ntohl(rx_message->data.ntp_source.authkey);
params.nts_port = ntohl(rx_message->data.ntp_source.nts_port);

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2013-2014, 2016, 2021
* Copyright (C) Miroslav Lichvar 2013-2014, 2016, 2021, 2025
*
* 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
@@ -66,6 +66,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
src->params.max_unreach = SRC_DEFAULT_MAXUNREACH;
src->params.filter_length = 0;
src->params.interleaved = 0;
src->params.sel_options = 0;
@@ -158,6 +159,9 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "maxsources")) {
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.max_sources, &n, 1, INT_MAX))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "maxunreach")) {
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.max_unreach, &n, 0, INT_MAX))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "mindelay")) {
if (sscanf(line, "%lf%n", &src->params.min_delay, &n) != 1)
return CPS_InvalidValue;

7
conf.c
View File

@@ -958,7 +958,7 @@ static void
parse_refclock(char *line)
{
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
int local, max_lock_age, pps_forced, sel_option, stratum, tai;
int local, max_lock_age, max_unreach, pps_forced, sel_option, stratum, tai;
uint32_t ref_id, lock_ref_id;
double offset, delay, precision, max_dispersion, pulse_width;
char *p, *cmd, *name, *param;
@@ -972,6 +972,7 @@ parse_refclock(char *line)
pps_rate = 0;
min_samples = SRC_DEFAULT_MINSAMPLES;
max_samples = SRC_DEFAULT_MAXSAMPLES;
max_unreach = SRC_DEFAULT_MAXUNREACH;
sel_options = 0;
offset = 0.0;
delay = 1e-9;
@@ -1036,6 +1037,9 @@ parse_refclock(char *line)
} else if (!strcasecmp(cmd, "maxsamples")) {
if (!SSCANF_IN_RANGE(line, "%d%n", &max_samples, &n, 0, INT_MAX))
break;
} else if (!strcasecmp(cmd, "maxunreach")) {
if (!SSCANF_IN_RANGE(line, "%d%n", &max_unreach, &n, 0, INT_MAX))
break;
} else if (!strcasecmp(cmd, "offset")) {
if (sscanf(line, "%lf%n", &offset, &n) != 1)
break;
@@ -1085,6 +1089,7 @@ parse_refclock(char *line)
refclock->pps_rate = pps_rate;
refclock->min_samples = min_samples;
refclock->max_samples = max_samples;
refclock->max_unreach = max_unreach;
refclock->sel_options = sel_options;
refclock->stratum = stratum;
refclock->tai = tai;

30
configure vendored
View File

@@ -133,6 +133,7 @@ For better control, use the options below.
--with-ntp-era=SECONDS Specify earliest assumed NTP time in seconds
since 1970-01-01 [50*365 days ago]
--with-user=USER Specify default chronyd user [root]
--with-chronyc-user=USER Specify default chronyc user [root]
--with-hwclockfile=PATH Specify default path to hwclock(8) adjtime file
--with-pidfile=PATH Specify default pidfile [/var/run/chrony/chronyd.pid]
--with-rtcdevice=PATH Specify default path to RTC device [/dev/rtc]
@@ -250,6 +251,7 @@ try_timestamping=0
feat_ntp_signd=0
ntp_era_split=""
default_user="root"
default_chronyc_user="root"
default_hwclockfile=""
default_pidfile="/var/run/chrony/chronyd.pid"
default_rtcdevice="/dev/rtc"
@@ -354,6 +356,9 @@ do
--with-user=* )
default_user=`echo $option | sed -e 's/^.*=//;'`
;;
--with-chronyc-user=* )
default_chronyc_user=`echo $option | sed -e 's/^.*=//;'`
;;
--with-hwclockfile=* )
default_hwclockfile=`echo $option | sed -e 's/^.*=//;'`
;;
@@ -661,13 +666,15 @@ then
fi
if [ $try_clock_gettime = "1" ]; then
if test_code 'clock_gettime()' 'time.h' '' '' \
'clock_gettime(CLOCK_REALTIME, (void *)1);'
if test_code 'clock_gettime()' 'time.h' '' '' '
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);'
then
add_def HAVE_CLOCK_GETTIME
else
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
'clock_gettime(CLOCK_REALTIME, (void *)1);'
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' '
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);'
then
add_def HAVE_CLOCK_GETTIME
EXTRA_LIBS="$EXTRA_LIBS -lrt"
@@ -694,13 +701,16 @@ else
fi
if [ $try_arc4random = "1" ] && \
test_code 'arc4random_buf()' 'stdlib.h' '' '' \
'arc4random_buf((void *)1, 1);'
test_code 'arc4random_buf()' 'stdlib.h' '' '' '
char c;
arc4random_buf(&c, 1);'
then
add_def HAVE_ARC4RANDOM
else
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
'return getrandom((void *)1, 1, 0);'; then
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' '
char c;
return getrandom(&c, 1, 0);'
then
add_def HAVE_GETRANDOM
fi
fi
@@ -1011,7 +1021,7 @@ if [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
fi
if grep '#define HAVE_SIV' config.h > /dev/null; then
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o"
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o tls_gnutls.o"
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ntp_auth.o nts_ntp_client.o nts_ntp_server.o"
LIBS="$LIBS $test_link"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
@@ -1080,6 +1090,7 @@ add_def DEFAULT_HWCLOCK_FILE "\"$default_hwclockfile\""
add_def DEFAULT_PID_FILE "\"$default_pidfile\""
add_def DEFAULT_RTC_DEVICE "\"$default_rtcdevice\""
add_def DEFAULT_USER "\"$default_user\""
add_def DEFAULT_CHRONYC_USER "\"$default_chronyc_user\""
add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYRUNDIR/chronyd.sock\""
add_def MAIL_PROGRAM "\"$mail_program\""
@@ -1123,6 +1134,7 @@ do
s%@DEFAULT_PID_FILE@%${default_pidfile}%;\
s%@DEFAULT_RTC_DEVICE@%${default_rtcdevice}%;\
s%@DEFAULT_USER@%${default_user}%;\
s%@DEFAULT_CHRONYC_USER@%${default_chronyc_user}%;\
s%@CHRONY_VERSION@%${CHRONY_VERSION}%;" \
< ${f}.in > $f
done

View File

@@ -17,6 +17,7 @@ CHRONYRUNDIR = @CHRONYRUNDIR@
CHRONYVARDIR = @CHRONYVARDIR@
CHRONY_VERSION = @CHRONY_VERSION@
DEFAULT_USER = @DEFAULT_USER@
DEFAULT_CHRONYC_USER = @DEFAULT_CHRONYC_USER@
DEFAULT_HWCLOCK_FILE = @DEFAULT_HWCLOCK_FILE@
DEFAULT_PID_FILE = @DEFAULT_PID_FILE@
DEFAULT_RTC_DEVICE = @DEFAULT_RTC_DEVICE@
@@ -29,6 +30,7 @@ SED_COMMANDS = "s%\@SYSCONFDIR\@%$(SYSCONFDIR)%g;\
s%\@DEFAULT_PID_FILE\@%$(DEFAULT_PID_FILE)%g;\
s%\@DEFAULT_RTC_DEVICE\@%$(DEFAULT_RTC_DEVICE)%g;\
s%\@DEFAULT_USER\@%$(DEFAULT_USER)%g;\
s%\@DEFAULT_CHRONYC_USER\@%$(DEFAULT_CHRONYC_USER)%g;\
s%\@CHRONYRUNDIR\@%$(CHRONYRUNDIR)%g;\
s%\@CHRONYVARDIR\@%$(CHRONYVARDIR)%g;"

View File

@@ -218,6 +218,12 @@ Set the minimum number of samples kept for this source. This overrides the
*maxsamples* _samples_:::
Set the maximum number of samples kept for this source. This overrides the
<<maxsamples,*maxsamples*>> directive.
*maxunreach* _polls_:::
This option specifies the maximum number of polls that this source can stay
selected for synchronisation when it is unreachable (i.e. no valid response was
received to the last 8 requests). Only sources with at least one sample more
recent than the oldest sample of all reachable sources can be selected. The
default is 100000.
*filter* _polls_:::
This option enables a median filter to reduce noise in NTP measurements. The
filter will process samples collected in the specified number of polls
@@ -731,6 +737,12 @@ Set the minimum number of samples kept for this source. This overrides the
*maxsamples* _samples_:::
Set the maximum number of samples kept for this source. This overrides the
<<maxsamples,*maxsamples*>> directive.
*maxunreach* _polls_:::
This option specifies the maximum number of polls that this source can stay
selected for synchronisation when it is unreachable (i.e. no valid sample was
received in the last 8 polls). Only sources with at least one sample more
recent than the oldest sample of all reachable sources can be selected.
The default is 100000.
[[manual]]*manual*::
The *manual* directive enables support at run-time for the

View File

@@ -2,7 +2,7 @@
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2024
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2025
//
// 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
@@ -121,6 +121,13 @@ This option allows the user to specify the UDP port number which the target
*chronyd* is using for its monitoring connections. This defaults to 323; there
would rarely be a need to change this.
*-u* _user_::
This option sets the name of the user to which *chronyc* will switch when it is
started under the root user in order to drop its privileges. *chronyc* will not
try to change its identity if the option is set to _root_, or the effective
user ID of the started process is not 0. The compiled-in default value is
_@DEFAULT_CHRONYC_USER@_.
*-f* _file_::
This option is ignored and is provided only for compatibility.
@@ -472,7 +479,8 @@ synchronisation:
* _~_ - has a jitter larger than the maximum jitter (configured by the
<<chrony.conf.adoc#maxjitter,*maxjitter*>> directive).
* _w_ - waits for other sources to get out of the _M_ state.
* _S_ - has older measurements than other sources.
* _S_ - has only measurements older than reachable sources, or is unreachable
for too many polls (configured by the *maxunreach* option).
* _O_ - has a stratum equal or larger than the orphan stratum (configured by
the <<chrony.conf.adoc#local,*local*>> directive).
* _T_ - does not fully agree with sources that have the *trust* option.

View File

@@ -165,6 +165,13 @@ versions or implementations of the libraries might make different system calls.
If the filter is missing some system call, `chronyd` could be killed even in
normal operation.
The impact of potential security issues in `chronyc` can be reduced by running
`chronyc` under the _chrony_ user instead of root, or another unprivileged user
if access to the Unix domain socket is not needed. Since version 4.8, `chronyc`
drops root privileges automatically if it is started with the `-u` option
specifying the _chrony_ user, or the name was specified to be the compiled-in
default by the `--with-chronyc-user` option of the configure script.
=== How can I make the system clock more secure?
An NTP client synchronising the system clock to an NTP server is susceptible to
@@ -897,7 +904,9 @@ measurements from both sources.
If the first source was significantly better than the second source, it can
take many hours before the second source is selected, depending on its polling
interval. You can force a faster reselection by increasing the clock error rate
interval. You can force a faster reselection by reducing the maximum number of
polls the source can still be selected when unreachable (`maxunreach` option
supported since `chrony` version 4.8), increasing the clock error rate
(`maxclockerror` directive), shortening the polling interval (`maxpoll`
option), or reducing the number of samples (`maxsamples` option).

View File

@@ -163,7 +163,8 @@ HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now)
int
HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3],
struct timespec *hw_ts, struct timespec *local_ts, double *err)
struct timespec *hw_ts, struct timespec *local_ts, double *err,
int *quality)
{
double delay, raw_delay, min_delay, low_delay, high_delay, e, pred_err;
double delay_sum, hw_sum, local_sum, local_prec, freq;
@@ -228,11 +229,13 @@ HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3]
UTI_AddDoubleToTimespec(&tss[0][1], hw_sum / combined, hw_ts);
UTI_AddDoubleToTimespec(&tss[0][0], local_sum / combined, local_ts);
*err = MAX(delay_sum / combined / 2.0, clock->precision);
*quality = 2;
return 1;
}
/* Accept the reading with minimum delay if its interval does not contain
the current offset predicted from previous samples */
/* Indicate acceptable quality of the reading with minimum delay if its
interval does not contain the current offset predicted from previous
samples, or a new sample is needed to get the tracking working */
*hw_ts = tss[min_reading][1];
UTI_AddDoubleToTimespec(&tss[min_reading][0], min_delay / freq / 2.0, local_ts);
@@ -242,11 +245,14 @@ HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3]
LCL_CookTime(local_ts, &ts1, NULL);
if (!HCL_CookTime(clock, hw_ts, &ts2, &e) ||
((pred_err = UTI_DiffTimespecsToDouble(&ts1, &ts2)) > *err)) {
DEBUG_LOG("Accepted reading err=%e prerr=%e", *err, pred_err);
return 1;
*quality = 1;
} else {
*quality = 0;
}
return 0;
DEBUG_LOG("Min-delay reading err=%e prerr=%e ql=%d", *err, pred_err, *quality);
return 1;
}
/* ================================================== */

View File

@@ -38,10 +38,14 @@ extern void HCL_DestroyInstance(HCL_Instance clock);
/* Check if a new sample should be accumulated at this time */
extern int HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now);
/* Process new readings of the HW clock in form of (sys, hw, sys) triplets and
produce a sample which can be accumulated */
/* Process new readings of the HW clock in the form of (sys, hw, sys) triplets
and produce a sample which can be accumulated by HCL_AccumulateSample().
Indicate the quality of the sample relative to already processed samples as
a value of 0, 1, or 2, where a sample of quality 0 should normally be
dropped. */
extern int HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3],
struct timespec *hw_ts, struct timespec *local_ts, double *err);
struct timespec *hw_ts, struct timespec *local_ts, double *err,
int *quality);
/* Accumulate a new sample */
extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,

3
keys.c
View File

@@ -265,9 +265,6 @@ KEY_Reload(void)
if (get_key(i - 1)->id == get_key(i)->id)
LOG(LOGS_WARN, "Detected duplicate key %"PRIu32, get_key(i - 1)->id);
}
/* Erase any passwords from stack */
memset(line, 0, sizeof (line));
}
/* ================================================== */

View File

@@ -124,7 +124,7 @@ get_list_leap(time_t when, int *tai_offset)
/* leap-seconds.list timestamps are relative to 1 Jan 1900, 00:00:00 */
when1900 = (int64_t)when + LEAP_SEC_LIST_OFFSET;
while (fgets(line, sizeof line, f) > 0) {
while (fgets(line, sizeof line, f)) {
int64_t lsl_when;
int lsl_tai_offset;
char *p;

View File

@@ -686,7 +686,8 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SRC_NTP, NAU_IsAuthEnabled(result->auth),
params->sel_options, &result->remote_addr.ip_addr,
params->min_samples, params->max_samples,
params->min_delay, params->asymmetry);
params->min_delay, params->asymmetry,
params->max_unreach);
if (params->max_delay_quant > 0.0) {
int k = round(CLAMP(0.05, params->max_delay_quant, 0.95) * DELAY_QUANT_Q);

View File

@@ -220,7 +220,7 @@ add_interface(CNF_HwTsInterface *conf_iface)
SCK_CloseSocket(sock_fd);
phc_fd = SYS_Linux_OpenPHC(req.ifr_name);
phc_fd = SYS_Linux_OpenPHC(req.ifr_name, O_RDONLY);
if (phc_fd < 0)
return 0;
@@ -519,7 +519,7 @@ poll_phc(struct Interface *iface, struct timespec *now)
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts;
struct timespec phc_readings[PHC_READINGS][3];
double phc_err, local_err, interval;
int n_readings;
int n_readings, quality;
if (!HCL_NeedsNewSample(iface->clock, now))
return;
@@ -543,7 +543,8 @@ poll_phc(struct Interface *iface, struct timespec *now)
return;
if (!HCL_ProcessReadings(iface->clock, n_readings, phc_readings,
&sample_phc_ts, &sample_sys_ts, &phc_err))
&sample_phc_ts, &sample_sys_ts, &phc_err, &quality) ||
quality <= 0)
return;
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);

View File

@@ -3,6 +3,7 @@
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020-2021
* Copyright (C) Anthony Brandon 2025
*
* 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,11 +38,9 @@
#include "siv.h"
#include "socket.h"
#include "sched.h"
#include "tls.h"
#include "util.h"
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#define INVALID_SOCK_FD (-8)
struct RecordHeader {
@@ -75,7 +74,7 @@ struct NKSN_Instance_Record {
KeState state;
int sock_fd;
char *label;
gnutls_session_t tls_session;
TLS_Instance tls_session;
SCH_TimeoutID timeout_id;
int retry_factor;
@@ -85,8 +84,6 @@ struct NKSN_Instance_Record {
/* ================================================== */
static gnutls_priority_t priority_cache;
static int credentials_counter = 0;
static int clock_updates = 0;
@@ -206,71 +203,6 @@ check_message_format(struct Message *message, int eof)
/* ================================================== */
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;
unsigned int flags;
int r;
r = gnutls_init(&session, GNUTLS_NONBLOCK | GNUTLS_NO_TICKETS |
(server_mode ? GNUTLS_SERVER : GNUTLS_CLIENT));
if (r < 0) {
LOG(LOGS_ERR, "Could not %s TLS session : %s", "create", gnutls_strerror(r));
return NULL;
}
if (!server_mode) {
assert(server_name);
if (!UTI_IsStringIP(server_name)) {
r = gnutls_server_name_set(session, GNUTLS_NAME_DNS, server_name, strlen(server_name));
if (r < 0)
goto error;
}
flags = 0;
if (clock_updates < CNF_GetNoCertTimeCheck()) {
flags |= GNUTLS_VERIFY_DISABLE_TIME_CHECKS | GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS;
DEBUG_LOG("Disabled time checks");
}
gnutls_session_set_verify_cert(session, server_name, flags);
}
r = gnutls_priority_set(session, priority);
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)
{
@@ -286,7 +218,7 @@ stop_session(NKSN_Instance inst)
Free(inst->label);
inst->label = NULL;
gnutls_deinit(inst->tls_session);
TLS_DestroyInstance(inst->tls_session);
inst->tls_session = NULL;
SCH_RemoveTimeout(inst->timeout_id);
@@ -308,21 +240,6 @@ session_timeout(void *arg)
/* ================================================== */
static int
check_alpn(NKSN_Instance inst)
{
gnutls_datum_t alpn;
if (gnutls_alpn_get_selected_protocol(inst->tls_session, &alpn) < 0 ||
alpn.size != sizeof (NKE_ALPN_NAME) - 1 ||
memcmp(alpn.data, NKE_ALPN_NAME, sizeof (NKE_ALPN_NAME) - 1) != 0)
return 0;
return 1;
}
/* ================================================== */
static void
set_input_output(NKSN_Instance inst, int output)
{
@@ -364,6 +281,7 @@ static int
handle_event(NKSN_Instance inst, int event)
{
struct Message *message = &inst->message;
TLS_Status s;
int r;
DEBUG_LOG("Session event %d fd=%d state=%d", event, inst->sock_fd, (int)inst->state);
@@ -390,56 +308,28 @@ handle_event(NKSN_Instance inst, int event)
return 0;
case KE_HANDSHAKE:
r = gnutls_handshake(inst->tls_session);
if (r < 0) {
if (gnutls_error_is_fatal(r)) {
gnutls_datum_t cert_error;
/* Get a description of verification errors */
if (r != GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR ||
gnutls_certificate_verification_status_print(
gnutls_session_get_verify_cert_status(inst->tls_session),
gnutls_certificate_type_get(inst->tls_session), &cert_error, 0) < 0)
cert_error.data = NULL;
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"TLS handshake with %s failed : %s%s%s", inst->label, gnutls_strerror(r),
cert_error.data ? " " : "", cert_error.data ? (const char *)cert_error.data : "");
if (cert_error.data)
gnutls_free(cert_error.data);
s = TLS_DoHandshake(inst->tls_session);
switch (s) {
case TLS_SUCCESS:
break;
case TLS_AGAIN_OUTPUT:
case TLS_AGAIN_INPUT:
set_input_output(inst, s == TLS_AGAIN_OUTPUT);
return 0;
default:
stop_session(inst);
/* Increase the retry interval if the handshake did not fail due
to the other end closing the connection */
if (r != GNUTLS_E_PULL_ERROR && r != GNUTLS_E_PREMATURE_TERMINATION)
if (s != TLS_CLOSED)
inst->retry_factor = NKE_RETRY_FACTOR2_TLS;
return 0;
}
/* Disable output when the handshake is trying to receive data */
set_input_output(inst, gnutls_record_get_direction(inst->tls_session));
return 0;
}
inst->retry_factor = NKE_RETRY_FACTOR2_TLS;
if (DEBUG) {
char *description = gnutls_session_get_desc(inst->tls_session);
DEBUG_LOG("Handshake with %s completed %s",
inst->label, description ? description : "");
gnutls_free(description);
}
if (!check_alpn(inst)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE not supported by %s", inst->label);
stop_session(inst);
return 0;
}
/* Client will send a request to the server */
change_state(inst, inst->server ? KE_RECEIVE : KE_SEND);
return 0;
@@ -448,16 +338,17 @@ handle_event(NKSN_Instance inst, int event)
assert(inst->new_message && message->complete);
assert(message->length <= sizeof (message->data) && message->length > message->sent);
r = gnutls_record_send(inst->tls_session, &message->data[message->sent],
message->length - message->sent);
s = TLS_Send(inst->tls_session, &message->data[message->sent],
message->length - message->sent, &r);
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->label, gnutls_strerror(r));
switch (s) {
case TLS_SUCCESS:
break;
case TLS_AGAIN_OUTPUT:
return 0;
default:
stop_session(inst);
}
return 0;
return 0;
}
DEBUG_LOG("Sent %d bytes to %s", r, inst->label);
@@ -480,26 +371,24 @@ handle_event(NKSN_Instance inst, int event)
return 0;
}
r = gnutls_record_recv(inst->tls_session, &message->data[message->length],
sizeof (message->data) - message->length);
s = TLS_Receive(inst->tls_session, &message->data[message->length],
sizeof (message->data) - message->length, &r);
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->label, gnutls_strerror(r));
switch (s) {
case TLS_SUCCESS:
break;
case TLS_AGAIN_INPUT:
return 0;
default:
stop_session(inst);
}
return 0;
return 0;
}
DEBUG_LOG("Received %d bytes from %s", r, inst->label);
message->length += r;
} while (gnutls_record_check_pending(inst->tls_session) > 0);
} while (TLS_CheckPending(inst->tls_session));
if (!check_message_format(message, r == 0)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
@@ -519,18 +408,18 @@ handle_event(NKSN_Instance inst, int event)
return 1;
case KE_SHUTDOWN:
r = gnutls_bye(inst->tls_session, GNUTLS_SHUT_RDWR);
s = TLS_Shutdown(inst->tls_session);
if (r < 0) {
if (gnutls_error_is_fatal(r)) {
DEBUG_LOG("Shutdown with %s failed : %s", inst->label, gnutls_strerror(r));
switch (s) {
case TLS_SUCCESS:
break;
case TLS_AGAIN_OUTPUT:
case TLS_AGAIN_INPUT:
set_input_output(inst, s == TLS_AGAIN_OUTPUT);
return 0;
default:
stop_session(inst);
return 0;
}
/* Disable output when the TLS shutdown is trying to receive data */
set_input_output(inst, gnutls_record_get_direction(inst->tls_session));
return 0;
}
SCK_ShutdownConnection(inst->sock_fd);
@@ -592,36 +481,18 @@ handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
/* ================================================== */
static int gnutls_initialised = 0;
static int tls_initialised = 0;
static int
init_gnutls(void)
init_tls(void)
{
int r;
if (gnutls_initialised)
if (tls_initialised)
return 1;
r = gnutls_global_init();
if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
/* Prepare a priority cache for server and client NTS-KE sessions
(the NTS specification requires TLS1.3 or later) */
r = gnutls_priority_init2(&priority_cache,
"-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-DTLS-ALL",
NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND);
if (r < 0) {
LOG(LOGS_ERR, "Could not initialise %s : %s",
"priority cache for TLS", gnutls_strerror(r));
gnutls_global_deinit();
if (!TLS_Initialise(&get_time))
return 0;
}
/* Use our clock instead of the system clock in certificate verification */
gnutls_global_set_time_function(get_time);
gnutls_initialised = 1;
tls_initialised = 1;
DEBUG_LOG("Initialised");
LCL_AddParameterChangeHandler(handle_step, NULL);
@@ -632,16 +503,15 @@ init_gnutls(void)
/* ================================================== */
static void
deinit_gnutls(void)
deinit_tls(void)
{
if (!gnutls_initialised || credentials_counter > 0)
if (!tls_initialised || credentials_counter > 0)
return;
LCL_RemoveParameterChangeHandler(handle_step, NULL);
gnutls_priority_deinit(priority_cache);
gnutls_global_deinit();
gnutls_initialised = 0;
TLS_Finalise();
tls_initialised = 0;
DEBUG_LOG("Deinitialised");
}
@@ -652,67 +522,21 @@ create_credentials(const char **certs, const char **keys, int n_certs_keys,
const char **trusted_certs, uint32_t *trusted_certs_ids,
int n_trusted_certs, uint32_t trusted_cert_set)
{
gnutls_certificate_credentials_t credentials = NULL;
int i, r;
TLS_Credentials credentials;
if (!init_gnutls())
if (!init_tls())
return NULL;
r = gnutls_certificate_allocate_credentials(&credentials);
if (r < 0)
goto error;
if (certs && keys) {
BRIEF_ASSERT(!trusted_certs && !trusted_certs_ids);
for (i = 0; i < n_certs_keys; i++) {
if (!UTI_CheckFilePermissions(keys[i], 0771))
;
r = gnutls_certificate_set_x509_key_file(credentials, certs[i], keys[i],
GNUTLS_X509_FMT_PEM);
if (r < 0)
goto error;
}
} else {
BRIEF_ASSERT(!certs && !keys && n_certs_keys <= 0);
if (trusted_cert_set == 0 && !CNF_GetNoSystemCert()) {
r = gnutls_certificate_set_x509_system_trust(credentials);
if (r < 0)
goto error;
}
if (trusted_certs && trusted_certs_ids) {
for (i = 0; i < n_trusted_certs; i++) {
struct stat buf;
if (trusted_certs_ids[i] != trusted_cert_set)
continue;
if (stat(trusted_certs[i], &buf) == 0 && S_ISDIR(buf.st_mode))
r = gnutls_certificate_set_x509_trust_dir(credentials, trusted_certs[i],
GNUTLS_X509_FMT_PEM);
else
r = gnutls_certificate_set_x509_trust_file(credentials, trusted_certs[i],
GNUTLS_X509_FMT_PEM);
if (r < 0)
goto error;
DEBUG_LOG("Added %d trusted certs from %s", r, trusted_certs[i]);
}
}
credentials = TLS_CreateCredentials(certs, keys, n_certs_keys, trusted_certs,
trusted_certs_ids, n_trusted_certs, trusted_cert_set);
if (!credentials) {
deinit_tls();
return NULL;
}
credentials_counter++;
return (NKSN_Credentials)credentials;
error:
LOG(LOGS_ERR, "Could not set credentials : %s", gnutls_strerror(r));
if (credentials)
gnutls_certificate_free_credentials(credentials);
deinit_gnutls();
return NULL;
return credentials;
}
/* ================================================== */
@@ -737,9 +561,9 @@ NKSN_CreateClientCertCredentials(const char **certs, uint32_t *ids,
void
NKSN_DestroyCertCredentials(NKSN_Credentials credentials)
{
gnutls_certificate_free_credentials((gnutls_certificate_credentials_t)credentials);
TLS_DestroyCredentials(credentials);
credentials_counter--;
deinit_gnutls();
deinit_tls();
}
/* ================================================== */
@@ -789,9 +613,9 @@ NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label,
{
assert(inst->state == KE_STOPPED);
inst->tls_session = create_tls_session(inst->server, sock_fd, inst->server_name,
(gnutls_certificate_credentials_t)credentials,
priority_cache);
inst->tls_session = TLS_CreateInstance(inst->server, sock_fd, inst->server_name,
label, NKE_ALPN_NAME, credentials,
clock_updates < CNF_GetNoCertTimeCheck());
if (!inst->tls_session)
return 0;
@@ -899,19 +723,15 @@ NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm algorithm, SIV_Algorithm exporter
context.algorithm = htons(exporter_algorithm);
context.is_s2c = 0;
if (gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (context) - 1, (char *)&context,
length, (char *)c2s->key) < 0) {
if (!TLS_ExportKey(inst->tls_session, sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (context) - 1, &context, length, c2s->key)) {
DEBUG_LOG("Could not export key");
return 0;
}
context.is_s2c = 1;
if (gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (context) - 1, (char *)&context,
length, (char *)s2c->key) < 0) {
if (!TLS_ExportKey(inst->tls_session, sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (context) - 1, &context, length, s2c->key)) {
DEBUG_LOG("Could not export key");
return 0;
}

View File

@@ -244,7 +244,7 @@ RCL_AddRefclock(RefclockParameters *params)
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, 0, params->sel_options,
NULL, params->min_samples, params->max_samples,
0.0, 0.0);
0.0, 0.0, params->max_unreach);
DEBUG_LOG("refclock %s refid=%s poll=%d dpoll=%d filter=%d",
params->driver_name, UTI_RefidToString(inst->ref_id),
@@ -427,10 +427,21 @@ convert_tai_offset(struct timespec *sample_time, double *offset)
}
static int
accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double offset, double dispersion)
accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double offset, double dispersion,
int quality)
{
NTP_Sample sample;
instance->reached++;
/* Don't accumulate the sample if the driver is suggesting it should be
dropped due to low quality. The only reason it went so far was to update
the reachability. */
if (quality < 1) {
DEBUG_LOG("refclock sample ignored quality=%d", quality);
return 0;
}
sample.time = *sample_time;
sample.offset = offset;
sample.peer_delay = instance->delay;
@@ -443,14 +454,14 @@ accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double of
int
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
struct timespec *ref_time, int leap)
struct timespec *ref_time, int leap, int quality)
{
double correction, dispersion, raw_offset, offset;
struct timespec cooked_time;
if (instance->pps_forced)
return RCL_AddPulse(instance, sample_time,
1.0e-9 * (sample_time->tv_nsec - ref_time->tv_nsec));
1.0e-9 * (sample_time->tv_nsec - ref_time->tv_nsec), quality);
raw_offset = UTI_DiffTimespecsToDouble(ref_time, sample_time);
@@ -486,7 +497,7 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
return 0;
}
if (!accumulate_sample(instance, &cooked_time, offset, dispersion))
if (!accumulate_sample(instance, &cooked_time, offset, dispersion, quality))
return 0;
instance->pps_active = 0;
@@ -501,7 +512,7 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
}
int
RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second, int quality)
{
double correction, dispersion;
struct timespec cooked_time;
@@ -513,7 +524,7 @@ RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0))
return 0;
return RCL_AddCookedPulse(instance, &cooked_time, second, dispersion, correction);
return RCL_AddCookedPulse(instance, &cooked_time, second, dispersion, correction, quality);
}
static int
@@ -539,7 +550,7 @@ check_pulse_edge(RCL_Instance instance, double offset, double distance)
int
RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
double second, double dispersion, double raw_correction)
double second, double dispersion, double raw_correction, int quality)
{
double offset;
int rate;
@@ -641,7 +652,7 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
return 0;
}
if (!accumulate_sample(instance, cooked_time, offset, dispersion))
if (!accumulate_sample(instance, cooked_time, offset, dispersion, quality))
return 0;
instance->leap_status = leap;
@@ -657,12 +668,6 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
return 1;
}
void
RCL_UpdateReachability(RCL_Instance instance)
{
instance->reached++;
}
double
RCL_GetPrecision(RCL_Instance instance)
{

View File

@@ -42,6 +42,7 @@ typedef struct {
int pps_rate;
int min_samples;
int max_samples;
int max_unreach;
int sel_options;
int max_lock_age;
int stratum;
@@ -77,11 +78,12 @@ extern char *RCL_GetDriverParameter(RCL_Instance instance);
extern void RCL_CheckDriverOptions(RCL_Instance instance, const char **options);
extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
struct timespec *ref_time, int leap);
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second);
struct timespec *ref_time, int leap, int quality);
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second,
int quality);
extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
double second, double dispersion, double raw_correction);
extern void RCL_UpdateReachability(RCL_Instance instance);
double second, double dispersion, double raw_correction,
int quality);
extern double RCL_GetPrecision(RCL_Instance instance);
extern int RCL_GetDriverPoll(RCL_Instance instance);

View File

@@ -66,7 +66,7 @@ static int phc_initialise(RCL_Instance instance)
{
const char *options[] = {"nocrossts", "extpps", "pin", "channel", "clear", NULL};
struct phc_instance *phc;
int phc_fd, rising_edge;
int rising_edge;
struct stat st;
char *path, *s;
@@ -74,19 +74,20 @@ static int phc_initialise(RCL_Instance instance)
path = RCL_GetDriverParameter(instance);
phc_fd = SYS_Linux_OpenPHC(path);
if (phc_fd < 0)
LOG_FATAL("Could not open PHC");
phc = MallocNew(struct phc_instance);
phc->fd = phc_fd;
if (fstat(phc_fd, &st) < 0 || !S_ISCHR(st.st_mode))
LOG_FATAL("Could not get PHC index");
phc->dev_index = minor(st.st_rdev);
phc->mode = 0;
phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0;
UTI_ZeroTimespec(&phc->last_extts);
phc->fd = SYS_Linux_OpenPHC(path, phc->extpps ? O_RDWR : O_RDONLY);
if (phc->fd < 0)
LOG_FATAL("Could not open PHC");
if (fstat(phc->fd, &st) < 0 || !S_ISCHR(st.st_mode))
LOG_FATAL("Could not get PHC index");
phc->dev_index = minor(st.st_rdev);
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)),
RCL_GetPrecision(instance));
@@ -154,13 +155,11 @@ static void process_ext_pulse(RCL_Instance instance, struct timespec *phc_ts)
}
phc->last_extts = *phc_ts;
RCL_UpdateReachability(instance);
if (!HCL_CookTime(phc->clock, phc_ts, &local_ts, &local_err))
return;
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
UTI_DiffTimespecsToDouble(phc_ts, &local_ts));
UTI_DiffTimespecsToDouble(phc_ts, &local_ts), 1);
}
static void read_ext_pulse(int fd, int event, void *anything)
@@ -197,7 +196,7 @@ static int phc_poll(RCL_Instance instance)
struct timespec phc_ts, sys_ts, local_ts, readings[PHC_READINGS][3];
struct phc_instance *phc;
double phc_err, local_err;
int n_readings;
int n_readings, quality;
phc = (struct phc_instance *)RCL_GetDriverData(instance);
@@ -206,14 +205,13 @@ static int phc_poll(RCL_Instance instance)
if (n_readings < 1)
return 0;
if (!phc->extpps)
RCL_UpdateReachability(instance);
if (!HCL_ProcessReadings(phc->clock, n_readings, readings, &phc_ts, &sys_ts, &phc_err))
if (!HCL_ProcessReadings(phc->clock, n_readings, readings,
&phc_ts, &sys_ts, &phc_err, &quality))
return 0;
LCL_CookTime(&sys_ts, &local_ts, &local_err);
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
if (quality > 0)
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
if (phc->extpps)
return 0;
@@ -221,7 +219,7 @@ static int phc_poll(RCL_Instance instance)
DEBUG_LOG("PHC offset: %+.9f err: %.9f",
UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts), phc_err);
return RCL_AddSample(instance, &sys_ts, &phc_ts, LEAP_Normal);
return RCL_AddSample(instance, &sys_ts, &phc_ts, LEAP_Normal, quality);
}
RefclockDriver RCL_PHC_driver = {

View File

@@ -143,9 +143,7 @@ static int pps_poll(RCL_Instance instance)
pps->last_seq = seq;
RCL_UpdateReachability(instance);
return RCL_AddPulse(instance, &ts, 1.0e-9 * ts.tv_nsec);
return RCL_AddPulse(instance, &ts, 1.0e-9 * ts.tv_nsec, 1);
}
RefclockDriver RCL_PPS_driver = {

View File

@@ -58,9 +58,7 @@ static int refrtc_add_sample(RCL_Instance instance, struct timespec *now,
rtc_ts.tv_sec = rtc_sec;
rtc_ts.tv_nsec = rtc_nsec;
RCL_UpdateReachability(instance);
status = RCL_AddSample(instance, now, &rtc_ts, LEAP_Normal);
status = RCL_AddSample(instance, now, &rtc_ts, LEAP_Normal, 1);
return status;
}

View File

@@ -109,8 +109,6 @@ static int shm_poll(RCL_Instance instance)
shm->valid = 0;
RCL_UpdateReachability(instance);
receive_ts.tv_sec = t.receiveTimeStampSec;
clock_ts.tv_sec = t.clockTimeStampSec;
@@ -126,7 +124,7 @@ static int shm_poll(RCL_Instance instance)
UTI_NormaliseTimespec(&clock_ts);
UTI_NormaliseTimespec(&receive_ts);
return RCL_AddSample(instance, &receive_ts, &clock_ts, t.leap);
return RCL_AddSample(instance, &receive_ts, &clock_ts, t.leap, 1);
}
RefclockDriver RCL_SHM_driver = {

View File

@@ -129,17 +129,15 @@ static void read_sample(int sockfd, int event, void *anything)
UTI_TimevalToTimespec(&sample.tv, &sys_ts);
UTI_NormaliseTimespec(&sys_ts);
RCL_UpdateReachability(instance);
if (!UTI_IsTimeOffsetSane(&sys_ts, sample.offset))
return;
UTI_AddDoubleToTimespec(&sys_ts, sample.offset, &ref_ts);
if (sample.pulse) {
RCL_AddPulse(instance, &sys_ts, sample.offset);
RCL_AddPulse(instance, &sys_ts, sample.offset, 1);
} else {
RCL_AddSample(instance, &sys_ts, &ref_ts, sample.leap);
RCL_AddSample(instance, &sys_ts, &ref_ts, sample.leap, 1);
}
}

View File

@@ -604,18 +604,18 @@ error:
/* ================================================== */
static socklen_t
set_unix_sockaddr(struct sockaddr_un *sun, const char *addr)
set_unix_sockaddr(struct sockaddr_un *sa, const char *addr)
{
size_t len = strlen(addr);
if (len + 1 > sizeof (sun->sun_path)) {
if (len + 1 > sizeof (sa->sun_path)) {
DEBUG_LOG("Unix socket path %s too long", addr);
return 0;
}
memset(sun, 0, sizeof (*sun));
sun->sun_family = AF_UNIX;
memcpy(sun->sun_path, addr, len);
memset(sa, 0, sizeof (*sa));
sa->sun_family = AF_UNIX;
memcpy(sa->sun_path, addr, len);
return offsetof(struct sockaddr_un, sun_path) + len + 1;
}
@@ -641,12 +641,6 @@ bind_unix_address(int sock_fd, const char *addr, int flags)
return 0;
}
/* Allow access to everyone with access to the directory if requested */
if (flags & SCK_FLAG_ALL_PERMISSIONS && chmod(addr, 0666) < 0) {
DEBUG_LOG("Could not change permissions of %s : %s", addr, strerror(errno));
return 0;
}
return 1;
}

View File

@@ -34,8 +34,7 @@
#define SCK_FLAG_BLOCK 1
#define SCK_FLAG_BROADCAST 2
#define SCK_FLAG_RX_DEST_ADDR 4
#define SCK_FLAG_ALL_PERMISSIONS 8
#define SCK_FLAG_PRIV_BIND 16
#define SCK_FLAG_PRIV_BIND 8
/* Flags for receiving and sending messages */
#define SCK_FLAG_MSG_ERRQUEUE 1

View File

@@ -106,13 +106,20 @@ struct SRC_Instance_Record {
/* Number of set bits in the reachability register */
int reachability_size;
/* Updates since last reference update */
/* Number of reachability updates with cleared register */
int unreachable_run;
/* Maximum number of reachability updates with cleared register to still
allow selection */
int max_unreachable_run;
/* Number of selection updates since last reference update */
int updates;
/* Updates left before allowing combining */
/* Number of selection updates left before allowing combining again */
int distant;
/* Updates with a status requiring source replacement */
/* Number of selection updates with a status requiring source replacement */
int bad;
/* Flag indicating the status of the source */
@@ -259,7 +266,8 @@ void SRC_Finalise(void)
SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int authenticated,
int sel_options, IPAddr *addr, int min_samples,
int max_samples, double min_delay, double asymmetry)
int max_samples, double min_delay, double asymmetry,
int max_unreach)
{
SRC_Instance result;
@@ -295,6 +303,7 @@ SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int authentic
result->authenticated = authenticated;
result->conf_sel_options = sel_options;
result->sel_options = sel_options;
result->max_unreachable_run = max_unreach;
result->active = 0;
SRC_SetRefid(result, ref_id, addr);
@@ -351,6 +360,7 @@ SRC_ResetInstance(SRC_Instance instance)
instance->updates = 0;
instance->reachability = 0;
instance->reachability_size = 0;
instance->unreachable_run = 0;
instance->distant = 0;
instance->bad = 0;
instance->status = SRC_BAD_STATS;
@@ -524,6 +534,13 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
if (inst->reachability_size < SOURCE_REACH_BITS)
inst->reachability_size++;
if (inst->reachability == 0) {
if (inst->unreachable_run < INT_MAX)
inst->unreachable_run++;
} else {
inst->unreachable_run = 0;
}
/* Check if special reference update mode failed */
if (REF_GetMode() != REF_ModeNormal && special_mode_end()) {
REF_SetUnsynchronised();
@@ -545,6 +562,7 @@ SRC_ResetReachability(SRC_Instance inst)
{
inst->reachability = 0;
inst->reachability_size = 0;
inst->unreachable_run = 0;
SRC_UpdateReachability(inst, 0);
}
@@ -1059,8 +1077,11 @@ SRC_SelectSource(SRC_Instance updated_inst)
/* Reachability is not a requirement for selection. An unreachable source
can still be selected if its newest sample is not older than the oldest
sample from reachable sources. */
if (!sources[i]->reachability && max_reach_sample_ago < si->last_sample_ago) {
sample from reachable sources and the number of consecutive unreachable
updates does not exceed the configured maximum. */
if (sources[i]->reachability == 0 &&
(si->last_sample_ago > max_reach_sample_ago ||
sources[i]->unreachable_run > sources[i]->max_unreachable_run)) {
mark_source(sources[i], SRC_STALE);
continue;
}

View File

@@ -69,7 +69,8 @@ typedef enum {
extern SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int authenticated,
int sel_options, IPAddr *addr, int min_samples,
int max_samples, double min_delay, double asymmetry);
int max_samples, double min_delay, double asymmetry,
int max_unreach);
/* Function to get rid of a source when it is being unconfigured.
This may cause the current reference source to be reselected, if this

View File

@@ -49,6 +49,7 @@ typedef struct {
int max_sources;
int min_samples;
int max_samples;
int max_unreach;
int filter_length;
int interleaved;
int sel_options;
@@ -79,6 +80,7 @@ typedef struct {
#define SRC_DEFAULT_MAXSOURCES 4
#define SRC_DEFAULT_MINSAMPLES (-1)
#define SRC_DEFAULT_MAXSAMPLES (-1)
#define SRC_DEFAULT_MAXUNREACH 100000
#define SRC_DEFAULT_ASYMMETRY 1.0
#define SRC_DEFAULT_NTSPORT 4460
#define SRC_DEFAULT_CERTSET 0

View File

@@ -96,9 +96,6 @@ static int max_tick_bias;
static int hz;
static double dhz; /* And dbl prec version of same for arithmetic */
/* Flag indicating whether adjtimex() can step the clock */
static int have_setoffset;
/* The assumed rate at which the effective frequency and tick values are
updated in the kernel */
static int tick_update_hz;
@@ -298,16 +295,11 @@ get_version_specific_details(void)
get_kernel_version(&major, &minor, &patch);
DEBUG_LOG("Linux kernel major=%d minor=%d patch=%d", major, minor, patch);
if (kernelvercmp(major, minor, patch, 2, 2, 0) < 0) {
if (kernelvercmp(major, minor, patch, 2, 6, 39) < 0) {
LOG_FATAL("Kernel version not supported, sorry.");
}
if (kernelvercmp(major, minor, patch, 2, 6, 27) >= 0 &&
kernelvercmp(major, minor, patch, 2, 6, 33) < 0) {
/* In tickless kernels before 2.6.33 the frequency is updated in
a half-second interval */
tick_update_hz = 2;
} else if (kernelvercmp(major, minor, patch, 4, 19, 0) < 0) {
if (kernelvercmp(major, minor, patch, 4, 19, 0) < 0) {
/* In kernels before 4.19 the frequency is updated only on internal ticks
(CONFIG_HZ). As their rate cannot be reliably detected from the user
space, and it may not even be constant (CONFIG_NO_HZ - aka tickless),
@@ -315,13 +307,6 @@ get_version_specific_details(void)
tick_update_hz = 100;
}
/* ADJ_SETOFFSET support */
if (kernelvercmp(major, minor, patch, 2, 6, 39) < 0) {
have_setoffset = 0;
} else {
have_setoffset = 1;
}
DEBUG_LOG("hz=%d nominal_tick=%d max_tick_bias=%d tick_update_hz=%d",
hz, nominal_tick, max_tick_bias, tick_update_hz);
}
@@ -342,34 +327,6 @@ reset_adjtime_offset(void)
/* ================================================== */
static int
test_step_offset(void)
{
struct timex txc;
/* Zero maxerror and check it's reset to a maximum after ADJ_SETOFFSET.
This seems to be the only way how to verify that the kernel really
supports the ADJ_SETOFFSET mode as it doesn't return an error on unknown
mode. */
txc.modes = MOD_MAXERROR;
txc.maxerror = 0;
if (SYS_Timex_Adjust(&txc, 1) < 0 || txc.maxerror != 0)
return 0;
txc.modes = ADJ_SETOFFSET | ADJ_NANO;
txc.time.tv_sec = 0;
txc.time.tv_usec = 0;
if (SYS_Timex_Adjust(&txc, 1) < 0 || txc.maxerror < 100000)
return 0;
return 1;
}
/* ================================================== */
static void
report_time_adjust_blockers(void)
{
@@ -392,15 +349,10 @@ SYS_Linux_Initialise(void)
reset_adjtime_offset();
if (have_setoffset && !test_step_offset()) {
LOG(LOGS_INFO, "adjtimex() doesn't support ADJ_SETOFFSET");
have_setoffset = 0;
}
SYS_Timex_InitialiseWithFunctions(1.0e6 * max_tick_bias / nominal_tick,
1.0 / tick_update_hz,
read_frequency, set_frequency,
have_setoffset ? apply_step_offset : NULL,
apply_step_offset,
0.0, 0.0, NULL, NULL);
}
@@ -927,7 +879,7 @@ verify_fd_is_phc(int phc_fd)
/* ================================================== */
static int
open_phc_by_iface_name(const char *iface)
open_phc_by_iface_name(const char *iface, int flags)
{
#ifdef HAVE_LINUX_TIMESTAMPING
struct ethtool_ts_info ts_info;
@@ -970,7 +922,7 @@ open_phc_by_iface_name(const char *iface)
"/dev/ptp%d", ts_info.phc_index) >= sizeof (phc_device))
return -1;
return open(phc_device, O_RDONLY);
return open(phc_device, flags);
#else
return -1;
#endif
@@ -979,21 +931,20 @@ open_phc_by_iface_name(const char *iface)
/* ================================================== */
int
SYS_Linux_OpenPHC(const char *device)
SYS_Linux_OpenPHC(const char *device, int flags)
{
int phc_fd = -1;
if (device[0] == '/') {
phc_fd = open(device, O_RDONLY);
phc_fd = open(device, flags);
if (phc_fd >= 0)
phc_fd = verify_fd_is_phc(phc_fd);
}
if (phc_fd < 0) {
phc_fd = open_phc_by_iface_name(device);
phc_fd = open_phc_by_iface_name(device, flags);
if (phc_fd < 0) {
LOG(LOGS_ERR, "Could not open PHC of iface %s : %s",
device, strerror(errno));
LOG(LOGS_ERR, "Could not open PHC (of) %s", device);
return -1;
}
phc_fd = verify_fd_is_phc(phc_fd);

View File

@@ -39,7 +39,7 @@ extern void SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext conte
extern int SYS_Linux_CheckKernelVersion(int req_major, int req_minor);
extern int SYS_Linux_OpenPHC(const char *device);
extern int SYS_Linux_OpenPHC(const char *device, int flags);
extern int SYS_Linux_GetPHCReadings(int fd, int nocrossts, int *reading_mode, int max_readings,
struct timespec tss[][3]);

View File

@@ -100,7 +100,7 @@ Root delay : 0\.000000001 seconds
check_file_messages "20.* PPS1.*- N " 60 63 refclocks.log || test_fail
rm -f tmp/refclocks.log
min_sync_time=180
min_sync_time=80
max_sync_time=260
chronyc_start=270
client_conf="
@@ -137,7 +137,10 @@ maxupdateskew 10000"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
# This fails occasionally due to the 4th unreachable PPS update
# (made just before the SHM update) causing SHM unselection due to
# a large root distance
#check_source_selection || test_fail
check_sync || test_fail
check_chronyc_output "^Reference ID.*50505330 \(PPS0\)
Stratum.*: 1

View File

@@ -114,7 +114,7 @@ limit=1
for chronyc_conf in \
"accheck 1.2.3.4" \
"add peer 10.0.0.0 minpoll 2 maxpoll 6" \
"add server 10.0.0.0 minpoll 6 maxpoll 10 iburst burst key 1 certset 2 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 maxdelayquant 0.5 mindelay 1e-4 asymmetry 0.5 offset 1e-5 minsamples 6 maxsamples 6 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4 nts ntsport 4460 copy extfield F323 extfield F324 ipv6 ipv4" \
"add server 10.0.0.0 minpoll 6 maxpoll 10 iburst burst key 1 certset 2 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 maxdelayquant 0.5 mindelay 1e-4 asymmetry 0.5 offset 1e-5 minsamples 6 maxsamples 6 maxunreach 8 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4 nts ntsport 4460 copy extfield F323 extfield F324 ipv6 ipv4" \
"add server node1.net1.clk" \
"allow 1.2.3.4" \
"allow 1.2" \

43
test/simulation/150-maxunreach Executable file
View File

@@ -0,0 +1,43 @@
#!/usr/bin/env bash
. ./test.common
test_start "maxunreach option"
limit=5000
servers=2
client_server_options="minpoll 6 maxpoll 6 minsamples 64"
base_delay=$(cat <<-EOF | tr -d '\n'
(+ 1e-4
(* -1
(equal 0.1 from 3)
(equal 0.1 to 1)
(equal 0.1 (min time 2000) 2000))
(* 0.5
(+ (equal 0.1 from 2)
(equal 0.1 to 2))))
EOF
)
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
check_log_messages "Selected source 192.168.123.1" 1 1 || test_fail
check_log_messages "Selected source 192.168.123.2" 0 0 || test_fail
client_server_options="minpoll 6 maxpoll 6 minsamples 64 maxunreach 10"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
check_log_messages "Selected source 192.168.123.1" 1 1 || test_fail
check_log_messages "Selected source 192.168.123.2" 1 1 || test_fail
check_log_messages "00:52:..Z Selected source 192.168.123.2" 1 1 || test_fail
test_pass

View File

@@ -1,7 +1,7 @@
This is a collection of simulation tests using the clknetsim simulator
(supported on Linux only).
https://github.com/mlichvar/clknetsim
https://gitlab.com/chrony/clknetsim
The CLKNETSIM_PATH environment variable should point to the directory where
clknetsim was downloaded and compiled. If the variable is not set, the tests

View File

@@ -213,12 +213,15 @@ generate_chrony_conf() {
user=$(get_user)
ntpport=$(get_free_port)
cmdport=$(get_free_port)
while true; do
cmdport=$(get_free_port)
[ "$ntpport" -ne "$cmdport" ] && break
done
echo "0.0 10000" > "$TEST_LIBDIR/driftfile"
echo "1 MD5 abcdefghijklmnopq" > "$TEST_DIR/keys"
chown "$user:$(id -g "$user")" "$TEST_LIBDIR/driftfile" "$TEST_DIR/keys"
echo "0.0" > "$TEST_DIR/tempcomp"
chown "$user:$(id -g "$user")" "$TEST_LIBDIR/driftfile" "$TEST_DIR"/{keys,tempcomp}
(
echo "pidfile $(get_pidfile)"
@@ -390,7 +393,7 @@ run_chronyc() {
fi
$CHRONYC_WRAPPER $wrapper_options \
"$chronyc" -h "$host" $options "$@" > "$TEST_DIR/chronyc.out" && \
"$chronyc" -h "$host" -u "$(get_user)" $options "$@" > "$TEST_DIR/chronyc.out" && \
test_ok || test_error
[ $? -ne 0 ] && ret=1

View File

@@ -32,9 +32,9 @@ test_unit(void)
{
struct timespec start_hw_ts, start_local_ts, hw_ts, local_ts, ts;
struct timespec readings[MAX_READINGS][3];
int i, j, k, l, new_sample, n_readings, count, quality;
HCL_Instance clock;
double freq, jitter, interval, dj, err, sum;
int i, j, k, l, new_sample, n_readings, count;
LCL_Initialise();
TST_RegisterDummyDrivers();
@@ -84,11 +84,13 @@ test_unit(void)
UTI_ZeroTimespec(&hw_ts);
UTI_ZeroTimespec(&local_ts);
if (HCL_ProcessReadings(clock, n_readings, readings, &hw_ts, &local_ts, &err)) {
HCL_AccumulateSample(clock, &hw_ts, &local_ts, 2.0 * jitter);
new_sample = 1;
} else {
new_sample = 0;
new_sample = 0;
if (HCL_ProcessReadings(clock, n_readings, readings, &hw_ts, &local_ts, &err, &quality)) {
TEST_CHECK(quality >= 0 && quality <= 2);
if (quality > 0) {
HCL_AccumulateSample(clock, &hw_ts, &local_ts, 2.0 * jitter);
new_sample = 1;
}
}
}

View File

@@ -193,6 +193,8 @@ test_unit(void)
server_cred = NKSN_CreateServerCertCredentials(&cert, &key, 1);
client_cred = NKSN_CreateClientCertCredentials(&cert, &cert_id, 1, 0);
TEST_CHECK(server_cred);
TEST_CHECK(client_cred);
TEST_CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fds) == 0);
TEST_CHECK(fcntl(sock_fds[0], F_SETFL, O_NONBLOCK) == 0);

View File

@@ -28,7 +28,8 @@ create_source(SRC_Type type, IPAddr *addr, int authenticated, int sel_options)
return SRC_CreateNewInstance(UTI_IPToRefid(addr), type, authenticated, sel_options,
type == SRC_NTP ? addr : NULL,
SRC_DEFAULT_MINSAMPLES, SRC_DEFAULT_MAXSAMPLES, 0.0, 1.0);
SRC_DEFAULT_MINSAMPLES, SRC_DEFAULT_MAXSAMPLES, 0.0, 1.0,
SRC_DEFAULT_MAXUNREACH);
}
void

93
tls.h Normal file
View File

@@ -0,0 +1,93 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Anthony Brandon 2025
*
* 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 TLS session
*/
#ifndef GOT_TLS_H
#define GOT_TLS_H
struct TLS_Instance_Record;
typedef struct TLS_Instance_Record *TLS_Instance;
typedef void *TLS_Credentials;
typedef enum {
/* TLS operation succeeded */
TLS_SUCCESS,
/* TLS operation failed.
No more operations should be called and the session should be destroyed. */
TLS_FAILED,
/* TLS session closed by other end */
TLS_CLOSED,
/* The last TLS operation should be called again when input is ready */
TLS_AGAIN_INPUT,
/* The last TLS operation should be called again when output is ready */
TLS_AGAIN_OUTPUT,
} TLS_Status;
/* Initialize TLS */
extern int TLS_Initialise(time_t (*get_time)(time_t *t));
/* Deinitialize TLS */
extern void TLS_Finalise(void);
/* Create new TLS credentials instance */
extern TLS_Credentials TLS_CreateCredentials(const char **certs, const char **keys,
int n_certs_keys, const char **trusted_certs,
uint32_t * trusted_certs_ids, int n_trusted_certs,
uint32_t trusted_cert_set);
/* Destroy TLS credentials instance */
extern void TLS_DestroyCredentials(TLS_Credentials credentials);
/* Create new TLS session instance */
extern TLS_Instance TLS_CreateInstance(int server_mode, int sock_fd, const char *server_name,
const char *label, const char *alpn_name,
TLS_Credentials credentials, int disable_time_checks);
/* Destroy TLS instance */
extern void TLS_DestroyInstance(TLS_Instance inst);
/* Perform TLS handshake */
extern TLS_Status TLS_DoHandshake(TLS_Instance inst);
/* Send data over TLS */
extern TLS_Status TLS_Send(TLS_Instance inst, const void *data, int length, int *sent);
/* Receive data over TLS */
extern TLS_Status TLS_Receive(TLS_Instance inst, void *data, int length, int *received);
/* Check if there is data pending to read */
extern int TLS_CheckPending(TLS_Instance inst);
/* Perform TLS shutdown */
extern TLS_Status TLS_Shutdown(TLS_Instance inst);
/* Export key from TLS instance */
extern int TLS_ExportKey(TLS_Instance inst, int label_length, const char *label,
int context_length, const void *context, int key_length,
unsigned char *key);
#endif

409
tls_gnutls.c Normal file
View File

@@ -0,0 +1,409 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020-2021
* Copyright (C) Anthony Brandon 2025
*
* 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 TLS session handling using the gnutls library.
*/
#include "config.h"
#include "sysincl.h"
#include "tls.h"
#include "conf.h"
#include "logging.h"
#include "memory.h"
#include "util.h"
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
struct TLS_Instance_Record {
gnutls_session_t session;
int server;
char *label;
char *alpn_name;
};
/* ================================================== */
static gnutls_priority_t priority_cache;
/* ================================================== */
int
TLS_Initialise(time_t (*get_time)(time_t *t))
{
int r = gnutls_global_init();
if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
/* Prepare a priority cache for server and client NTS-KE sessions
(the NTS specification requires TLS1.3 or later) */
r = gnutls_priority_init2(&priority_cache,
"-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-DTLS-ALL",
NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND);
if (r < 0) {
LOG(LOGS_ERR, "Could not initialise %s : %s",
"priority cache for TLS", gnutls_strerror(r));
gnutls_global_deinit();
return 0;
}
/* Use our clock instead of the system clock in certificate verification */
gnutls_global_set_time_function(get_time);
return 1;
}
/* ================================================== */
void
TLS_Finalise(void)
{
gnutls_priority_deinit(priority_cache);
gnutls_global_deinit();
}
/* ================================================== */
TLS_Credentials
TLS_CreateCredentials(const char **certs, const char **keys, int n_certs_keys,
const char **trusted_certs, uint32_t *trusted_certs_ids,
int n_trusted_certs, uint32_t trusted_cert_set)
{
gnutls_certificate_credentials_t credentials = NULL;
int i, r;
r = gnutls_certificate_allocate_credentials(&credentials);
if (r < 0)
goto error;
if (certs && keys) {
BRIEF_ASSERT(!trusted_certs && !trusted_certs_ids);
for (i = 0; i < n_certs_keys; i++) {
if (!UTI_CheckFilePermissions(keys[i], 0771))
;
r = gnutls_certificate_set_x509_key_file(credentials, certs[i], keys[i],
GNUTLS_X509_FMT_PEM);
if (r < 0)
goto error;
}
} else {
BRIEF_ASSERT(!certs && !keys && n_certs_keys <= 0);
if (trusted_cert_set == 0 && !CNF_GetNoSystemCert()) {
r = gnutls_certificate_set_x509_system_trust(credentials);
if (r < 0)
goto error;
}
if (trusted_certs && trusted_certs_ids) {
for (i = 0; i < n_trusted_certs; i++) {
struct stat buf;
if (trusted_certs_ids[i] != trusted_cert_set)
continue;
if (stat(trusted_certs[i], &buf) == 0 && S_ISDIR(buf.st_mode))
r = gnutls_certificate_set_x509_trust_dir(credentials, trusted_certs[i],
GNUTLS_X509_FMT_PEM);
else
r = gnutls_certificate_set_x509_trust_file(credentials, trusted_certs[i],
GNUTLS_X509_FMT_PEM);
if (r < 0)
goto error;
DEBUG_LOG("Added %d trusted certs from %s", r, trusted_certs[i]);
}
}
}
return credentials;
error:
LOG(LOGS_ERR, "Could not set credentials : %s", gnutls_strerror(r));
if (credentials)
gnutls_certificate_free_credentials(credentials);
return NULL;
}
/* ================================================== */
void
TLS_DestroyCredentials(TLS_Credentials credentials)
{
gnutls_certificate_free_credentials((gnutls_certificate_credentials_t)credentials);
}
/* ================================================== */
TLS_Instance
TLS_CreateInstance(int server_mode, int sock_fd, const char *server_name, const char *label,
const char *alpn_name, TLS_Credentials credentials, int disable_time_checks)
{
gnutls_datum_t alpn;
unsigned int flags;
int r;
TLS_Instance inst = MallocNew(struct TLS_Instance_Record);
inst->session = NULL;
inst->server = server_mode;
inst->label = Strdup(label);
inst->alpn_name = Strdup(alpn_name);
r = gnutls_init(&inst->session, GNUTLS_NONBLOCK | GNUTLS_NO_TICKETS |
(server_mode ? GNUTLS_SERVER : GNUTLS_CLIENT));
if (r < 0) {
LOG(LOGS_ERR, "Could not %s TLS session : %s", "create", gnutls_strerror(r));
inst->session = NULL;
goto error;
}
if (!server_mode) {
assert(server_name);
if (!UTI_IsStringIP(server_name)) {
r = gnutls_server_name_set(inst->session, GNUTLS_NAME_DNS, server_name,
strlen(server_name));
if (r < 0)
goto error;
}
flags = 0;
if (disable_time_checks) {
flags |= GNUTLS_VERIFY_DISABLE_TIME_CHECKS | GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS;
DEBUG_LOG("Disabled time checks");
}
gnutls_session_set_verify_cert(inst->session, server_name, flags);
}
r = gnutls_priority_set(inst->session, priority_cache);
if (r < 0)
goto error;
r = gnutls_credentials_set(inst->session, GNUTLS_CRD_CERTIFICATE, credentials);
if (r < 0)
goto error;
alpn.data = (unsigned char *)inst->alpn_name;
alpn.size = strlen(inst->alpn_name);
r = gnutls_alpn_set_protocols(inst->session, &alpn, 1, 0);
if (r < 0)
goto error;
gnutls_transport_set_int(inst->session, sock_fd);
return inst;
error:
LOG(LOGS_ERR, "Could not %s TLS session : %s", "set", gnutls_strerror(r));
TLS_DestroyInstance(inst);
return NULL;
}
/* ================================================== */
void
TLS_DestroyInstance(TLS_Instance inst)
{
if (inst->session)
gnutls_deinit(inst->session);
Free(inst->label);
Free(inst->alpn_name);
Free(inst);
}
/* ================================================== */
static int
check_alpn(TLS_Instance inst)
{
gnutls_datum_t alpn;
int length = strlen(inst->alpn_name);
if (gnutls_alpn_get_selected_protocol(inst->session, &alpn) < 0 ||
alpn.size != length || memcmp(alpn.data, inst->alpn_name, length) != 0)
return 0;
return 1;
}
/* ================================================== */
TLS_Status
TLS_DoHandshake(TLS_Instance inst)
{
int r = gnutls_handshake(inst->session);
if (r < 0) {
if (gnutls_error_is_fatal(r)) {
gnutls_datum_t cert_error;
/* Get a description of verification errors */
if (r != GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR ||
gnutls_certificate_verification_status_print(
gnutls_session_get_verify_cert_status(inst->session),
gnutls_certificate_type_get(inst->session), &cert_error, 0) < 0)
cert_error.data = NULL;
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"TLS handshake with %s failed : %s%s%s", inst->label, gnutls_strerror(r),
cert_error.data ? " " : "", cert_error.data ? (const char *)cert_error.data : "");
if (cert_error.data)
gnutls_free(cert_error.data);
/* Increase the retry interval if the handshake did not fail due
to the other end closing the connection */
if (r != GNUTLS_E_PULL_ERROR && r != GNUTLS_E_PREMATURE_TERMINATION)
return TLS_FAILED;
return TLS_CLOSED;
}
return gnutls_record_get_direction(inst->session) ? TLS_AGAIN_OUTPUT : TLS_AGAIN_INPUT;
}
if (DEBUG) {
char *description = gnutls_session_get_desc(inst->session);
DEBUG_LOG("Handshake with %s completed %s", inst->label, description ? description : "");
gnutls_free(description);
}
if (!check_alpn(inst)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE not supported by %s", inst->label);
return TLS_FAILED;
}
return TLS_SUCCESS;
}
/* ================================================== */
TLS_Status
TLS_Send(TLS_Instance inst, const void *data, int length, int *sent)
{
int r;
if (length < 0)
return TLS_FAILED;
r = gnutls_record_send(inst->session, data, length);
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->label, gnutls_strerror(r));
return TLS_FAILED;
}
return TLS_AGAIN_OUTPUT;
}
*sent = r;
return TLS_SUCCESS;
}
/* ================================================== */
TLS_Status
TLS_Receive(TLS_Instance inst, void *data, int length, int *received)
{
int r;
if (length < 0)
return TLS_FAILED;
r = gnutls_record_recv(inst->session, data, 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->label, gnutls_strerror(r));
return TLS_FAILED;
}
return TLS_AGAIN_INPUT;
}
*received = r;
return TLS_SUCCESS;
}
/* ================================================== */
int
TLS_CheckPending(TLS_Instance inst)
{
return gnutls_record_check_pending(inst->session) > 0;
}
/* ================================================== */
TLS_Status
TLS_Shutdown(TLS_Instance inst)
{
int r = gnutls_bye(inst->session, GNUTLS_SHUT_RDWR);
if (r < 0) {
if (gnutls_error_is_fatal(r)) {
DEBUG_LOG("Shutdown with %s failed : %s", inst->label, gnutls_strerror(r));
return TLS_FAILED;
}
return gnutls_record_get_direction(inst->session) ? TLS_AGAIN_OUTPUT : TLS_AGAIN_INPUT;
}
return TLS_SUCCESS;
}
/* ================================================== */
int
TLS_ExportKey(TLS_Instance inst, int label_length, const char *label, int context_length,
const void *context, int key_length, unsigned char *key)
{
int r;
if (label_length < 0 || context_length < 0 || key_length < 0)
return 0;
r = gnutls_prf_rfc5705(inst->session, label_length, label, context_length, (char *)context,
key_length, (char *)key);
return r >= 0;
}

17
util.c
View File

@@ -545,12 +545,14 @@ UTI_CompareIPs(const IPAddr *a, const IPAddr *b, const IPAddr *mask)
char *
UTI_IPSockAddrToString(const IPSockAddr *sa)
{
char *result;
char buf[BUFFER_LENGTH], *result;
/* Copy to a separate buffer to avoid a compiler warning */
snprintf(buf, sizeof (buf), "%s", UTI_IPToString(&sa->ip_addr));
result = NEXT_BUFFER;
snprintf(result, BUFFER_LENGTH,
sa->ip_addr.family != IPADDR_INET6 ? "%s:%hu" : "[%s]:%hu",
UTI_IPToString(&sa->ip_addr), sa->port);
sa->ip_addr.family != IPADDR_INET6 ? "%s:%hu" : "[%s]:%hu", buf, sa->port);
return result;
}
@@ -1201,7 +1203,7 @@ create_dir(char *p, mode_t mode, uid_t uid, gid_t gid)
}
/* Set its owner */
if (chown(p, uid, gid) < 0) {
if (lchown(p, uid, gid) < 0) {
LOG(LOGS_ERR, "Could not change ownership of %s : %s", p, strerror(errno));
/* Don't leave it there with incorrect ownership */
rmdir(p);
@@ -1364,6 +1366,7 @@ FILE *
UTI_OpenFile(const char *basedir, const char *name, const char *suffix,
char mode, mode_t perm)
{
uint64_t attempts = 0, warn_attempts = 100;
const char *file_mode;
char path[PATH_MAX];
LOG_Severity severity;
@@ -1407,6 +1410,12 @@ try_again:
return NULL;
}
DEBUG_LOG("Removed %s", path);
if (++attempts == warn_attempts) {
LOG(LOGS_WARN, "Failing to replace %s (%"PRIu64" attempts)", path, attempts);
warn_attempts *= 10;
}
goto try_again;
}
LOG(severity, "Could not open %s : %s", path, strerror(errno));