Compare commits

..

31 Commits

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

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

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

Fixes: f231efb811 ("socket: add support for sending and receiving descriptors")
2020-10-05 18:56:37 +02:00
Miroslav Lichvar
df43ebe9e0 test: make 007-cmdmon test more reliable 2020-10-01 12:58:17 +02:00
Miroslav Lichvar
642173e864 client: drop unnecessary function
Replace cvt_to_sec_usec() with a UTI_DoubleToTimespec() call.
2020-10-01 12:58:17 +02:00
Miroslav Lichvar
944cf6e318 util: fix UTI_BytesToHex() to handle zero-length input 2020-10-01 12:58:17 +02:00
Miroslav Lichvar
a655eab34f nts: handle invalid algorithm in TLS key export 2020-10-01 12:58:17 +02:00
Miroslav Lichvar
f020d479e0 nts: fix server kod setting
Set the response kod value to zero even if NTS server is disabled.
2020-10-01 12:58:17 +02:00
Miroslav Lichvar
de752b28de nts: save server name in client dump file
Save the NTS-KE server name and require it to match the name of the
instance loading the file.
2020-10-01 12:58:17 +02:00
Miroslav Lichvar
f41d370e6a nts: update client state earlier
Generate a new uniq ID on each client poll to invalidate responses to
the previous request, even if a new request cannot be generated (e.g.
due to missing cookies). Reset the NAK indicator earlier in the request
sequence. Also, drop the cookie even if it's not included in the request
to prevent the client from getting stuck with a cookie that has an
invalid length. Rely on the exponentially increasing interval to avoid
frequent NTS-KE sessions due to a client bug.
2020-10-01 12:57:29 +02:00
Miroslav Lichvar
a97830d9d6 doc+examples: update http links to https 2020-09-23 15:10:43 +02:00
Miroslav Lichvar
ea4fc47cda client: improve help message
Describe all chronyc options in the help message.
2020-09-23 15:10:43 +02:00
Miroslav Lichvar
0e08ca7c89 main: improve help message
Describe all chronyd options in the help message.
2020-09-23 15:10:43 +02:00
Miroslav Lichvar
068cd3c311 doc: document long options
Document the --version and --help options in chronyd and chronyc man
page.
2020-09-23 15:10:43 +02:00
Miroslav Lichvar
455b8e4b44 test: include CMAC keys in ntp_core unit test 2020-09-23 15:10:43 +02:00
Miroslav Lichvar
d9a363606b nts: reset packet length after failed auth encryption
If encryption of the NTS authenticator field fails, don't leave
uninitialized data in the packet in case a bug causes the packet to be
sent.
2020-09-23 15:10:43 +02:00
Miroslav Lichvar
59ad433b6b ntp: improve NTS check in NAU_DestroyInstance()
Check the mode instead of the nts pointer to make it clear the pointer
is not expected to be NULL in an NTS instance (unless the NTS support is
stubbed).
2020-09-23 15:10:37 +02:00
Miroslav Lichvar
35b3a42ed9 ntp: update comments with new RFCs 2020-09-21 14:07:05 +02:00
44 changed files with 319 additions and 179 deletions

View File

@@ -764,7 +764,8 @@ typedef struct {
IPAddr ip_addr; IPAddr ip_addr;
uint8_t state_char; uint8_t state_char;
uint8_t authentication; uint8_t authentication;
uint8_t pad[2]; uint8_t leap;
uint8_t pad;
uint16_t conf_options; uint16_t conf_options;
uint16_t eff_options; uint16_t eff_options;
uint32_t last_sample_ago; uint32_t last_sample_ago;

View File

@@ -1001,36 +1001,17 @@ process_cmd_dfreq(CMD_Request *msg, char *line)
/* ================================================== */ /* ================================================== */
static void
cvt_to_sec_usec(double x, long *sec, long *usec) {
long s, us;
s = (long) x;
us = (long)(0.5 + 1.0e6 * (x - (double) s));
while (us >= 1000000) {
us -= 1000000;
s += 1;
}
while (us < 0) {
us += 1000000;
s -= 1;
}
*sec = s;
*usec = us;
}
/* ================================================== */
static void static void
process_cmd_doffset(CMD_Request *msg, char *line) process_cmd_doffset(CMD_Request *msg, char *line)
{ {
struct timeval tv;
double doffset; double doffset;
long sec, usec;
msg->command = htons(REQ_DOFFSET); msg->command = htons(REQ_DOFFSET);
if (sscanf(line, "%lf", &doffset) == 1) { if (sscanf(line, "%lf", &doffset) == 1) {
cvt_to_sec_usec(doffset, &sec, &usec); UTI_DoubleToTimeval(doffset, &tv);
msg->data.doffset.sec = htonl(sec); msg->data.doffset.sec = htonl(tv.tv_sec);
msg->data.doffset.usec = htonl(usec); msg->data.doffset.usec = htonl(tv.tv_usec);
} else { } else {
msg->data.doffset.sec = htonl(0); msg->data.doffset.sec = htonl(0);
msg->data.doffset.usec = htonl(0); msg->data.doffset.usec = htonl(0);
@@ -1882,19 +1863,19 @@ print_report(const char *format, ...)
integer = va_arg(ap, int); integer = va_arg(ap, int);
switch (integer) { switch (integer) {
case LEAP_Normal: case LEAP_Normal:
string = "Normal"; string = width != 1 ? "Normal" : "N";
break; break;
case LEAP_InsertSecond: case LEAP_InsertSecond:
string = "Insert second"; string = width != 1 ? "Insert second" : "+";
break; break;
case LEAP_DeleteSecond: case LEAP_DeleteSecond:
string = "Delete second"; string = width != 1 ? "Delete second" : "-";
break; break;
case LEAP_Unsynchronised: case LEAP_Unsynchronised:
string = "Not synchronised"; string = width != 1 ? "Not synchronised" : "?";
break; break;
default: default:
string = "Invalid"; string = width != 1 ? "Invalid" : "?";
break; break;
} }
printf("%s", string); printf("%s", string);
@@ -2049,7 +2030,7 @@ get_source_name(IPAddr *ip_addr, char *buf, int size)
/* Make sure the name is printable */ /* Make sure the name is printable */
for (i = 0; i < size && buf[i] != '\0'; i++) { for (i = 0; i < size && buf[i] != '\0'; i++) {
if (!isgraph(buf[i])) if (!isgraph((unsigned char)buf[i]))
return 0; return 0;
} }
@@ -2576,9 +2557,9 @@ process_cmd_selectdata(char *line)
printf( "| | | | |\n"); printf( "| | | | |\n");
} }
print_header("S Name/IP Address Auth COpts EOpts Last Score Interval "); print_header("S Name/IP Address Auth COpts EOpts Last Score Interval Leap");
/* "S NNNNNNNNNNNNNNNNNNNNNNNNN A OOOO- OOOO- LLLL SSSSS LLLLLLL LLLLLLL" */ /* "S NNNNNNNNNNNNNNNNNNNNNNNNN A OOOO- OOOO- LLLL SSSSS IIIIIII IIIIIII L" */
for (i = 0; i < n_sources; i++) { for (i = 0; i < n_sources; i++) {
request.command = htons(REQ_SELECT_DATA); request.command = htons(REQ_SELECT_DATA);
@@ -2596,7 +2577,7 @@ process_cmd_selectdata(char *line)
conf_options = ntohs(reply.data.select_data.conf_options); conf_options = ntohs(reply.data.select_data.conf_options);
eff_options = ntohs(reply.data.select_data.eff_options); eff_options = ntohs(reply.data.select_data.eff_options);
print_report("%c %-25s %c %c%c%c%c%c %c%c%c%c%c %I %5.1f %+S %+S\n", print_report("%c %-25s %c %c%c%c%c%c %c%c%c%c%c %I %5.1f %+S %+S %1L\n",
reply.data.select_data.state_char, reply.data.select_data.state_char,
name, name,
reply.data.select_data.authentication ? 'Y' : 'N', reply.data.select_data.authentication ? 'Y' : 'N',
@@ -2614,6 +2595,7 @@ process_cmd_selectdata(char *line)
UTI_FloatNetworkToHost(reply.data.select_data.score), UTI_FloatNetworkToHost(reply.data.select_data.score),
UTI_FloatNetworkToHost(reply.data.select_data.lo_limit), UTI_FloatNetworkToHost(reply.data.select_data.lo_limit),
UTI_FloatNetworkToHost(reply.data.select_data.hi_limit), UTI_FloatNetworkToHost(reply.data.select_data.hi_limit),
reply.data.select_data.leap,
REPORT_END); REPORT_END);
} }
@@ -3483,8 +3465,22 @@ display_gpl(void)
static void static void
print_help(const char *progname) print_help(const char *progname)
{ {
printf("Usage: %s [-h HOST] [-p PORT] [-n] [-N] [-c] [-d] [-4|-6] [-m] [COMMAND]\n", printf("Usage: %s [OPTION]... [COMMAND]...\n\n"
progname); "Options:\n"
" -4\t\tUse IPv4 addresses only\n"
" -6\t\tUse IPv6 addresses only\n"
" -n\t\tDon't resolve hostnames\n"
" -N\t\tPrint original source names\n"
" -c\t\tEnable CSV format\n"
#if DEBUG > 0
" -d\t\tEnable debug messages\n"
#endif
" -m\t\tAccept multiple commands\n"
" -h HOST\tSpecify server (%s)\n"
" -p PORT\tSpecify UDP port (%d)\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);
} }
/* ================================================== */ /* ================================================== */
@@ -3506,7 +3502,7 @@ main(int argc, char **argv)
int opt, ret = 1, multi = 0, family = IPADDR_UNSPEC; int opt, ret = 1, multi = 0, family = IPADDR_UNSPEC;
int port = DEFAULT_CANDM_PORT; int port = DEFAULT_CANDM_PORT;
/* Parse (undocumented) long command-line options */ /* Parse long command-line options */
for (optind = 1; optind < argc; optind++) { for (optind = 1; optind < argc; optind++) {
if (!strcmp("--help", argv[optind])) { if (!strcmp("--help", argv[optind])) {
print_help(progname); print_help(progname);

View File

@@ -169,7 +169,7 @@ compare_total_hits(Record *x, Record *y)
static Record * static Record *
get_record(IPAddr *ip) get_record(IPAddr *ip)
{ {
uint32_t last_hit, oldest_hit = 0; uint32_t last_hit = 0, oldest_hit = 0;
Record *record, *oldest_record; Record *record, *oldest_record;
unsigned int first, i, j; unsigned int first, i, j;

View File

@@ -306,7 +306,8 @@ transmit_reply(int sock_fd, int request_length, SCK_Message *message)
/* Don't require responses to non-link-local addresses to use the same /* Don't require responses to non-link-local addresses to use the same
interface */ interface */
if (!SCK_IsLinkLocalIPAddress(&message->remote_addr.ip.ip_addr)) if (message->addr_type == SCK_ADDR_IP &&
!SCK_IsLinkLocalIPAddress(&message->remote_addr.ip.ip_addr))
message->if_index = INVALID_IF_INDEX; message->if_index = INVALID_IF_INDEX;
if (!SCK_SendMessage(sock_fd, message, 0)) if (!SCK_SendMessage(sock_fd, message, 0))
@@ -1326,6 +1327,7 @@ handle_select_data(CMD_Request *rx_message, CMD_Reply *tx_message)
UTI_IPHostToNetwork(&report.ip_addr, &tx_message->data.select_data.ip_addr); UTI_IPHostToNetwork(&report.ip_addr, &tx_message->data.select_data.ip_addr);
tx_message->data.select_data.state_char = report.state_char; tx_message->data.select_data.state_char = report.state_char;
tx_message->data.select_data.authentication = report.authentication; tx_message->data.select_data.authentication = report.authentication;
tx_message->data.select_data.leap = report.leap;
tx_message->data.select_data.conf_options = htons(convert_select_options(report.conf_options)); tx_message->data.select_data.conf_options = htons(convert_select_options(report.conf_options));
tx_message->data.select_data.eff_options = htons(convert_select_options(report.eff_options)); tx_message->data.select_data.eff_options = htons(convert_select_options(report.eff_options));
tx_message->data.select_data.last_sample_ago = htonl(report.last_sample_ago); tx_message->data.select_data.last_sample_ago = htonl(report.last_sample_ago);

10
conf.c
View File

@@ -422,6 +422,10 @@ CNF_Finalise(void)
Free(((NTP_Source *)ARR_GetElement(ntp_sources, i))->params.name); Free(((NTP_Source *)ARR_GetElement(ntp_sources, i))->params.name);
for (i = 0; i < ARR_GetSize(ntp_source_dirs); i++) for (i = 0; i < ARR_GetSize(ntp_source_dirs); i++)
Free(*(char **)ARR_GetElement(ntp_source_dirs, i)); Free(*(char **)ARR_GetElement(ntp_source_dirs, i));
for (i = 0; i < ARR_GetSize(refclock_sources); i++) {
Free(((RefclockParameters *)ARR_GetElement(refclock_sources, i))->driver_name);
Free(((RefclockParameters *)ARR_GetElement(refclock_sources, i))->driver_parameter);
}
ARR_DestroyInstance(init_sources); ARR_DestroyInstance(init_sources);
ARR_DestroyInstance(ntp_sources); ARR_DestroyInstance(ntp_sources);
@@ -1839,10 +1843,14 @@ CNF_AddSources(void)
void void
CNF_AddRefclocks(void) CNF_AddRefclocks(void)
{ {
RefclockParameters *refclock;
unsigned int i; unsigned int i;
for (i = 0; i < ARR_GetSize(refclock_sources); i++) { for (i = 0; i < ARR_GetSize(refclock_sources); i++) {
RCL_AddRefclock((RefclockParameters *)ARR_GetElement(refclock_sources, i)); refclock = ARR_GetElement(refclock_sources, i);
RCL_AddRefclock(refclock);
Free(refclock->driver_name);
Free(refclock->driver_parameter);
} }
ARR_SetSize(refclock_sources, 0); ARR_SetSize(refclock_sources, 0);

10
configure vendored
View File

@@ -689,11 +689,11 @@ fi
if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; then if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; then
add_def HAVE_ARC4RANDOM add_def HAVE_ARC4RANDOM
fi else
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \ 'return getrandom(NULL, 256, 0);'; then
'return getrandom(NULL, 256, 0);'; then add_def HAVE_GETRANDOM
add_def HAVE_GETRANDOM fi
fi fi
RECVMMSG_CODE=' RECVMMSG_CODE='

View File

@@ -1608,9 +1608,12 @@ to the clients, which means they should use the same server for NTS-KE and NTP.
[[ntsrotate]]*ntsrotate* _interval_:: [[ntsrotate]]*ntsrotate* _interval_::
This directive specifies the rotation interval (in seconds) of the server key This directive specifies the rotation interval (in seconds) of the server key
which encrypts the NTS cookies. New keys are generated automatically. The which encrypts the NTS cookies. New keys are generated automatically from the
server keeps two previous keys to give the clients time to get new cookies _/dev/urandom_ device. The server keeps two previous keys to give the clients
encrypted by the latest key. The default interval is 604800 seconds (1 week). time to get new cookies encrypted by the latest key. The interval is measured
as the server's operating time, i.e. the actual interval can be longer if
*chronyd* is not running continuously. The default interval is 604800 seconds
(1 week).
+ +
The automatic rotation of the keys can be disabled by setting *ntsrotate* to 0. The automatic rotation of the keys can be disabled by setting *ntsrotate* to 0.
In this case the keys are assumed to be managed externally. *chronyd* will not In this case the keys are assumed to be managed externally. *chronyd* will not
@@ -2511,7 +2514,7 @@ the following methods:
stratum 1 and stratum 2 servers. You should find one or more servers that are stratum 1 and stratum 2 servers. You should find one or more servers that are
near to you. Check that their access policy allows you to use their near to you. Check that their access policy allows you to use their
facilities. facilities.
* Use public servers from the http://www.pool.ntp.org/[pool.ntp.org] project. * Use public servers from the https://www.pool.ntp.org/[pool.ntp.org] project.
Assuming that your NTP servers are called _foo.example.net_, _bar.example.net_ Assuming that your NTP servers are called _foo.example.net_, _bar.example.net_
and _baz.example.net_, your _chrony.conf_ file could contain as a minimum: and _baz.example.net_, your _chrony.conf_ file could contain as a minimum:
@@ -2803,7 +2806,7 @@ information to be saved.
=== Public NTP server === Public NTP server
*chronyd* can be configured to operate as a public NTP server, e.g. to join the *chronyd* can be configured to operate as a public NTP server, e.g. to join the
http://www.pool.ntp.org/en/join.html[pool.ntp.org] project. The configuration https://www.pool.ntp.org/en/join.html[pool.ntp.org] project. The configuration
is similar to the NTP client with permanent connection, except it needs to is similar to the NTP client with permanent connection, except it needs to
allow client access from all addresses. It is recommended to find at least four allow client access from all addresses. It is recommended to find at least four
good servers (e.g. from the pool, or on the NTP homepage). If the server has a good servers (e.g. from the pool, or on the NTP homepage). If the server has a

View File

@@ -118,10 +118,14 @@ This option is ignored and is provided only for compatibility.
*-a*:: *-a*::
This option is ignored and is provided only for compatibility. This option is ignored and is provided only for compatibility.
*-v*:: *-v*, *--version*::
With this option *chronyc* displays its version number on the terminal and With this option *chronyc* displays its version number on the terminal and
exits. exits.
*--help*::
With this option *chronyc* displays a help message on the terminal and
exits.
== COMMANDS == COMMANDS
This section describes each of the commands available within the *chronyc* This section describes each of the commands available within the *chronyc*
@@ -428,11 +432,11 @@ lines are shown as a reminder of the meanings of the columns.
An example of the output is shown below. An example of the output is shown below.
+ +
---- ----
S Name/IP Address Auth COpts EOpts Last Score Interval S Name/IP Address Auth COpts EOpts Last Score Interval Leap
==================================================================== =======================================================================
D foo.example.net Y ----- --TR- 4 1.0 -61ms +62ms D foo.example.net Y ----- --TR- 4 1.0 -61ms +62ms N
* bar.example.net N ----- ----- 0 1.0 -6846us +7305us * bar.example.net N ----- ----- 0 1.0 -6846us +7305us N
+ baz.example.net N ----- ----- 10 1.0 -7381us +7355us + baz.example.net N ----- ----- 10 1.0 -7381us +7355us N
---- ----
+ +
The columns are as follows: The columns are as follows:
@@ -504,6 +508,12 @@ be reselected and the scores will be reset to 1.
This column displays the lower and upper endpoint of the interval which was This column displays the lower and upper endpoint of the interval which was
expected to contain the true offset of the local clock considering the root expected to contain the true offset of the local clock considering the root
distance at the time of the selection. distance at the time of the selection.
*Leap*:::
This column displays the current leap status of the source.
* _N_ indicates the normal status (no leap second).
* _+_ indicates that a leap second will be inserted at the end of the month.
* _-_ indicates that a leap second will be deleted at the end of the month.
* _?_ indicates the unknown status (i.e. no valid measurement was made).
[[reselect]]*reselect*:: [[reselect]]*reselect*::
To avoid excessive switching between sources, *chronyd* can stay synchronised To avoid excessive switching between sources, *chronyd* can stay synchronised

View File

@@ -187,9 +187,12 @@ still track its offset and frequency relative to the estimated true time. This
option allows *chronyd* to be started without the capability to adjust or set option allows *chronyd* to be started without the capability to adjust or set
the system clock (e.g. in some containers) to operate as an NTP server. the system clock (e.g. in some containers) to operate as an NTP server.
*-v*:: *-v*, *--version*::
With this option *chronyd* will print version number to the terminal and exit. With this option *chronyd* will print version number to the terminal and exit.
*-h*, *--help*::
With this option *chronyd* will print a help message to the terminal and exit.
== FILES == FILES
_@SYSCONFDIR@/chrony.conf_ _@SYSCONFDIR@/chrony.conf_

View File

@@ -68,7 +68,7 @@ system time is periodically copied to the RTC. It is supported on Linux and
macOS. macOS.
If you want to use public NTP servers from the If you want to use public NTP servers from the
http://www.pool.ntp.org/[pool.ntp.org] project, the minimal _chrony.conf_ file https://www.pool.ntp.org/[pool.ntp.org] project, the minimal _chrony.conf_ file
could be: could be:
---- ----
@@ -428,8 +428,7 @@ are marked with the _*_ or _+_ symbol in the report printed by the `sources`
command. command.
When the best source (marked with the _*_ symbol) becomes unreachable (e.g. NTP When the best source (marked with the _*_ symbol) becomes unreachable (e.g. NTP
server stops responding), or the measurements are no longer accepted (e.g. a server stops responding), `chronyd` will not immediately switch
change in network routing adds a delay), `chronyd` will not immediately switch
to the second best source in an attempt to minimise the error of the clock. It to the second best source in an attempt to minimise the error of the clock. It
will let the clock run free for as long as its estimated error (in terms of will let the clock run free for as long as its estimated error (in terms of
root distance) based on previous measurements is smaller than the estimated root distance) based on previous measurements is smaller than the estimated
@@ -437,11 +436,35 @@ error of the second source, and there is still an interval which contains some
measurements from both sources. measurements from both sources.
If the first source was significantly better than the second source, it can If the first source was significantly better than the second source, it can
take many hours (up to 64 times the maximum polling interval) before the second take many hours before the second source is selected, depending on its polling
source is selected. If you do not like this behaviour, you can force a faster interval. You can force a faster reselection by increasing the clock error rate
reselection by increasing the clock error rate (`maxclockerror` directive), (`maxclockerror` directive), shortening the polling interval (`maxpoll`
shortening the polling interval (`maxpoll` option), or reducing the number of option), or reducing the number of samples (`maxsamples` option).
samples (`maxsamples` option).
=== Does selected source drop new measurements?
`chronyd` can drop a large number of successive NTP measurements if they are
not passing some of the NTP tests. The `sources` command can report for a
selected source the fully-reachable value of 377 in the Reach column and at the
same time a LastRx value that is much larger than the current polling interval.
If the source is online, this indicates that a number of measurements was
dropped. You can use the `ntpdata` command to check the NTP tests for the last
measurement. Usually, it is the test C which fails.
This can be an issue when there is a long-lasting increase in the measured
delay, e.g. due to a routing change in the network. Unfortunately, `chronyd`
does not know for how long it should wait for the delay to come back to the
original values, or whether it is a permanent increase and it should start from
scratch.
The test C is an adaptive filter. It can take many hours before it accepts
a measurement with the larger delay, and even much longer before it drops all
measurements with smaller delay, which determine an expected delay used by the
test. You can use the `reset sources` command to drop all measurements
immediately (available in chrony 4.0 and later). If this issue happens
frequently, you can effectively disable the test by setting the
`maxdelaydevratio` option to a very large value (e.g. 1000000), or speed up the
recovery by increasing the clock error rate with the `maxclockerror` directive.
=== Using a PPS reference clock? === Using a PPS reference clock?

View File

@@ -94,7 +94,7 @@ want to enable the support, specify the `--disable-asyncdns` flag to
If development files for the https://www.lysator.liu.se/~nisse/nettle/[Nettle], If development files for the https://www.lysator.liu.se/~nisse/nettle/[Nettle],
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS[NSS], or https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS[NSS], or
http://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available, https://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available,
`chronyd` will be built with support for other cryptographic hash functions `chronyd` will be built with support for other cryptographic hash functions
than MD5, which can be used for NTP authentication with a symmetric key. If you than MD5, which can be used for NTP authentication with a symmetric key. If you
don't want to enable the support, specify the `--disable-sechash` flag to don't want to enable the support, specify the `--disable-sechash` flag to

View File

@@ -1,5 +1,5 @@
# Use public servers from the pool.ntp.org project. # Use public servers from the pool.ntp.org project.
# Please consider joining the pool (http://www.pool.ntp.org/join.html). # Please consider joining the pool (https://www.pool.ntp.org/join.html).
pool pool.ntp.org iburst pool pool.ntp.org iburst
# Record the rate at which the system clock gains/losses time. # Record the rate at which the system clock gains/losses time.

34
main.c
View File

@@ -374,8 +374,34 @@ go_daemon(void)
static void static void
print_help(const char *progname) print_help(const char *progname)
{ {
printf("Usage: %s [-4|-6] [-n|-d] [-p|-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n", printf("Usage: %s [OPTION]... [DIRECTIVE]...\n\n"
progname); "Options:\n"
" -4\t\tUse IPv4 addresses only\n"
" -6\t\tUse IPv6 addresses only\n"
" -f FILE\tSpecify configuration file (%s)\n"
" -n\t\tDon't run as daemon\n"
" -d\t\tDon't run as daemon and log to stderr\n"
#if DEBUG > 0
" -d -d\t\tEnable debug messages\n"
#endif
" -l FILE\tLog to file\n"
" -L LEVEL\tSet logging threshold (0)\n"
" -p\t\tPrint configuration and exit\n"
" -q\t\tSet clock and exit\n"
" -Q\t\tLog offset and exit\n"
" -r\t\tReload dump files\n"
" -R\t\tAdapt configuration for restart\n"
" -s\t\tSet clock from RTC\n"
" -t SECONDS\tExit after elapsed time\n"
" -u USER\tSpecify user (%s)\n"
" -U\t\tDon't check for root\n"
" -F LEVEL\tSet system call filter level (0)\n"
" -P PRIORITY\tSet process priority (0)\n"
" -m\t\tLock memory\n"
" -x\t\tDon't control clock\n"
" -v, --version\tPrint version and exit\n"
" -h, --help\tPrint usage and exit\n",
progname, DEFAULT_CONF_FILE, DEFAULT_USER);
} }
/* ================================================== */ /* ================================================== */
@@ -417,7 +443,7 @@ int main
LOG_Initialise(); LOG_Initialise();
/* Parse (undocumented) long command-line options */ /* Parse long command-line options */
for (optind = 1; optind < argc; optind++) { for (optind = 1; optind < argc; optind++) {
if (!strcmp("--help", argv[optind])) { if (!strcmp("--help", argv[optind])) {
print_help(progname); print_help(progname);
@@ -599,7 +625,7 @@ int main
/* Drop root privileges if the specified user has a non-zero UID */ /* Drop root privileges if the specified user has a non-zero UID */
if (!geteuid() && (pw->pw_uid || pw->pw_gid)) if (!geteuid() && (pw->pw_uid || pw->pw_gid))
SYS_DropRoot(pw->pw_uid, pw->pw_gid); SYS_DropRoot(pw->pw_uid, pw->pw_gid, SYS_MAIN_PROCESS);
REF_Initialise(); REF_Initialise();
SST_Initialise(); SST_Initialise();

5
ntp.h
View File

@@ -116,10 +116,11 @@ typedef struct {
/* Enumeration for authentication modes of NTP packets */ /* Enumeration for authentication modes of NTP packets */
typedef enum { typedef enum {
NTP_AUTH_NONE = 0, /* No authentication */ NTP_AUTH_NONE = 0, /* No authentication */
NTP_AUTH_SYMMETRIC, /* MAC using symmetric key (RFC 1305, RFC 5905) */ NTP_AUTH_SYMMETRIC, /* NTP MAC or CMAC using a symmetric key
(RFC 1305, RFC 5905, RFC 8573) */
NTP_AUTH_MSSNTP, /* MS-SNTP authenticator field */ NTP_AUTH_MSSNTP, /* MS-SNTP authenticator field */
NTP_AUTH_MSSNTP_EXT, /* MS-SNTP extended authenticator field */ NTP_AUTH_MSSNTP_EXT, /* MS-SNTP extended authenticator field */
NTP_AUTH_NTS, /* Network Time Security (RFC ????) */ NTP_AUTH_NTS, /* Network Time Security (RFC 8915) */
} NTP_AuthMode; } NTP_AuthMode;
/* Structure describing an NTP packet */ /* Structure describing an NTP packet */

View File

@@ -175,7 +175,7 @@ NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name, const IPSockAdd
void void
NAU_DestroyInstance(NAU_Instance instance) NAU_DestroyInstance(NAU_Instance instance)
{ {
if (instance->nts) if (instance->mode == NTP_AUTH_NTS)
NNC_DestroyInstance(instance->nts); NNC_DestroyInstance(instance->nts);
Free(instance); Free(instance);
} }

View File

@@ -444,8 +444,8 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&new_addr.ip_addr)); DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&new_addr.ip_addr));
if (us->pool_id != INVALID_POOL) { if (us->pool_id != INVALID_POOL) {
/* In the pool resolving mode, try to replace all sources from /* In the pool resolving mode, try to replace a source from
the pool which don't have a real address yet */ the pool which does not have a real address yet */
for (j = 0; j < ARR_GetSize(records); j++) { for (j = 0; j < ARR_GetSize(records); j++) {
record = get_record(j); record = get_record(j);
if (!record->remote_addr || record->pool_id != us->pool_id || if (!record->remote_addr || record->pool_id != us->pool_id ||
@@ -454,7 +454,8 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
old_addr = *record->remote_addr; old_addr = *record->remote_addr;
new_addr.port = old_addr.port; new_addr.port = old_addr.port;
if (replace_source_connectable(&old_addr, &new_addr)) if (replace_source_connectable(&old_addr, &new_addr))
break; ;
break;
} }
} else { } else {
new_addr.port = us->address.port; new_addr.port = us->address.port;
@@ -671,7 +672,7 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
/* Make sure the name is at least printable and has no spaces */ /* Make sure the name is at least printable and has no spaces */
for (i = 0; name[i] != '\0'; i++) { for (i = 0; name[i] != '\0'; i++) {
if (!isgraph(name[i])) if (!isgraph((unsigned char)name[i]))
return NSR_InvalidName; return NSR_InvalidName;
} }

View File

@@ -196,7 +196,7 @@ process_response(NKC_Instance inst)
inst->server_name[length] = '\0'; inst->server_name[length] = '\0';
/* Make sure the name is printable and has no spaces */ /* Make sure the name is printable and has no spaces */
for (i = 0; i < length && isgraph(inst->server_name[i]); i++) for (i = 0; i < length && isgraph((unsigned char)inst->server_name[i]); i++)
; ;
if (i != length) { if (i != length) {
DEBUG_LOG("Invalid server name"); DEBUG_LOG("Invalid server name");

View File

@@ -556,7 +556,7 @@ error:
#define MAX_WORDS 2 #define MAX_WORDS 2
static void static int
load_keys(void) load_keys(void)
{ {
char *dump_dir, line[1024], *words[MAX_WORDS]; char *dump_dir, line[1024], *words[MAX_WORDS];
@@ -568,11 +568,11 @@ load_keys(void)
dump_dir = CNF_GetNtsDumpDir(); dump_dir = CNF_GetNtsDumpDir();
if (!dump_dir) if (!dump_dir)
return; return 0;
f = UTI_OpenFile(dump_dir, DUMP_FILENAME, NULL, 'r', 0); f = UTI_OpenFile(dump_dir, DUMP_FILENAME, NULL, 'r', 0);
if (!f) if (!f)
return; return 0;
if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 || if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 || !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 ||
@@ -607,11 +607,13 @@ load_keys(void)
fclose(f); fclose(f);
return; return 1;
error: error:
DEBUG_LOG("Could not %s server keys", "load"); DEBUG_LOG("Could not %s server keys", "load");
fclose(f); fclose(f);
return 0;
} }
/* ================================================== */ /* ================================================== */
@@ -646,7 +648,7 @@ run_helper(uid_t uid, gid_t gid, int scfilter_level)
LOG_SetMinSeverity(log_severity); LOG_SetMinSeverity(log_severity);
if (!geteuid() && (uid || gid)) if (!geteuid() && (uid || gid))
SYS_DropRoot(uid, gid); SYS_DropRoot(uid, gid, SYS_NTSKE_HELPER);
NKS_Initialise(); NKS_Initialise();
@@ -764,10 +766,12 @@ NKS_Initialise(void)
server_sock_fd4 = open_socket(IPADDR_INET4); server_sock_fd4 = open_socket(IPADDR_INET4);
server_sock_fd6 = open_socket(IPADDR_INET6); server_sock_fd6 = open_socket(IPADDR_INET6);
load_keys();
key_rotation_interval = MAX(CNF_GetNtsRotate(), 0); key_rotation_interval = MAX(CNF_GetNtsRotate(), 0);
/* Reload saved keys, or save the new keys */
if (!load_keys())
save_keys();
if (key_rotation_interval > 0) { if (key_rotation_interval > 0) {
key_delay = key_rotation_interval - (SCH_GetLastEventMonoTime() - last_server_key_ts); key_delay = key_rotation_interval - (SCH_GetLastEventMonoTime() - last_server_key_ts);
SCH_AddTimeoutByDelay(MAX(key_delay, 0.0), key_timeout, NULL); SCH_AddTimeoutByDelay(MAX(key_delay, 0.0), key_timeout, NULL);

View File

@@ -825,21 +825,27 @@ NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
int int
NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c) NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c)
{ {
c2s->length = SIV_GetKeyLength(siv); int length = SIV_GetKeyLength(siv);
s2c->length = SIV_GetKeyLength(siv);
assert(c2s->length <= sizeof (c2s->key)); if (length <= 0 || length > sizeof (c2s->key) || length > sizeof (s2c->key)) {
assert(s2c->length <= sizeof (s2c->key)); DEBUG_LOG("Invalid algorithm");
return 0;
}
if (gnutls_prf_rfc5705(inst->tls_session, if (gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL, sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (NKE_EXPORTER_CONTEXT_C2S) - 1, NKE_EXPORTER_CONTEXT_C2S, sizeof (NKE_EXPORTER_CONTEXT_C2S) - 1, NKE_EXPORTER_CONTEXT_C2S,
c2s->length, (char *)c2s->key) < 0) length, (char *)c2s->key) < 0 ||
return 0; gnutls_prf_rfc5705(inst->tls_session,
if (gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL, sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (NKE_EXPORTER_CONTEXT_S2C) - 1, NKE_EXPORTER_CONTEXT_S2C, sizeof (NKE_EXPORTER_CONTEXT_S2C) - 1, NKE_EXPORTER_CONTEXT_S2C,
s2c->length, (char *)s2c->key) < 0) length, (char *)s2c->key) < 0) {
DEBUG_LOG("Could not export key");
return 0; return 0;
}
c2s->length = length;
s2c->length = length;
return 1; return 1;
} }

View File

@@ -112,6 +112,7 @@ NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
if (!SIV_Encrypt(siv, nonce, nonce_length, packet, assoc_length, if (!SIV_Encrypt(siv, nonce, nonce_length, packet, assoc_length,
plaintext, plaintext_length, ciphertext, ciphertext_length)) { plaintext, plaintext_length, ciphertext, ciphertext_length)) {
DEBUG_LOG("SIV encrypt failed"); DEBUG_LOG("SIV encrypt failed");
info->length = assoc_length;
return 0; return 0;
} }

View File

@@ -273,8 +273,6 @@ get_cookies(NNC_Instance inst)
inst->last_nke_success = now; inst->last_nke_success = now;
inst->cookie_index = 0; inst->cookie_index = 0;
inst->nak_response = 0;
return 1; return 1;
} }
@@ -285,6 +283,11 @@ NNC_PrepareForAuth(NNC_Instance inst)
{ {
inst->auth_ready = 0; inst->auth_ready = 0;
/* Prepare data for the next request and invalidate any responses to the
previous request */
UTI_GetRandomBytes(inst->uniq_id, sizeof (inst->uniq_id));
UTI_GetRandomBytes(inst->nonce, sizeof (inst->nonce));
/* Try to reload saved keys and cookies (once for the NTS-KE address) */ /* Try to reload saved keys and cookies (once for the NTS-KE address) */
if (!inst->load_attempt) { if (!inst->load_attempt) {
load_cookies(inst); load_cookies(inst);
@@ -297,6 +300,8 @@ NNC_PrepareForAuth(NNC_Instance inst)
return 0; return 0;
} }
inst->nak_response = 0;
if (!inst->siv) if (!inst->siv)
inst->siv = SIV_CreateInstance(inst->context.algorithm); inst->siv = SIV_CreateInstance(inst->context.algorithm);
@@ -306,10 +311,6 @@ NNC_PrepareForAuth(NNC_Instance inst)
return 0; return 0;
} }
/* Prepare data for NNC_GenerateRequestAuth() */
UTI_GetRandomBytes(inst->uniq_id, sizeof (inst->uniq_id));
UTI_GetRandomBytes(inst->nonce, sizeof (inst->nonce));
inst->auth_ready = 1; inst->auth_ready = 1;
return 1; return 1;
@@ -325,14 +326,22 @@ NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
int i, req_cookies; int i, req_cookies;
void *ef_body; void *ef_body;
if (!inst->auth_ready || inst->num_cookies == 0 || !inst->siv) if (!inst->auth_ready)
return 0;
inst->auth_ready = 0;
if (inst->num_cookies <= 0 || !inst->siv)
return 0; return 0;
if (info->mode != MODE_CLIENT) if (info->mode != MODE_CLIENT)
return 0; return 0;
cookie = &inst->cookies[inst->cookie_index]; cookie = &inst->cookies[inst->cookie_index];
req_cookies = MIN(NTS_MAX_COOKIES - inst->num_cookies + 1, inst->num_cookies--;
inst->cookie_index = (inst->cookie_index + 1) % NTS_MAX_COOKIES;
req_cookies = MIN(NTS_MAX_COOKIES - inst->num_cookies,
MAX_TOTAL_COOKIE_LENGTH / (cookie->length + 4)); MAX_TOTAL_COOKIE_LENGTH / (cookie->length + 4));
if (!NEF_AddField(packet, info, NTP_EF_NTS_UNIQUE_IDENTIFIER, if (!NEF_AddField(packet, info, NTP_EF_NTS_UNIQUE_IDENTIFIER,
@@ -354,10 +363,6 @@ NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
(const unsigned char *)"", 0, NTP_MAX_V4_MAC_LENGTH + 4)) (const unsigned char *)"", 0, NTP_MAX_V4_MAC_LENGTH + 4))
return 0; return 0;
inst->num_cookies--;
inst->cookie_index = (inst->cookie_index + 1) % NTS_MAX_COOKIES;
inst->auth_ready = 0;
inst->nak_response = 0;
inst->ok_response = 0; inst->ok_response = 0;
return 1; return 1;
@@ -536,7 +541,7 @@ save_cookies(NNC_Instance inst)
FILE *f; FILE *f;
int i; int i;
if (inst->num_cookies < 1 || !UTI_IsIPReal(&inst->nts_address.ip_addr)) if (inst->num_cookies < 1 || !inst->name || !UTI_IsIPReal(&inst->nts_address.ip_addr))
return; return;
dump_dir = CNF_GetNtsDumpDir(); dump_dir = CNF_GetNtsDumpDir();
@@ -553,9 +558,10 @@ save_cookies(NNC_Instance inst)
context_time = inst->last_nke_success - SCH_GetLastEventMonoTime(); context_time = inst->last_nke_success - SCH_GetLastEventMonoTime();
context_time += UTI_TimespecToDouble(&now); context_time += UTI_TimespecToDouble(&now);
if (fprintf(f, "%s%.1f\n%s %d\n%u %d ", if (fprintf(f, "%s%s\n%.1f\n%s %d\n%u %d ",
DUMP_IDENTIFIER, context_time, UTI_IPToString(&inst->ntp_address->ip_addr), DUMP_IDENTIFIER, inst->name, context_time,
inst->ntp_address->port, inst->context_id, (int)inst->context.algorithm) < 0 || UTI_IPToString(&inst->ntp_address->ip_addr), inst->ntp_address->port,
inst->context_id, (int)inst->context.algorithm) < 0 ||
!UTI_BytesToHex(inst->context.s2c.key, inst->context.s2c.length, buf, sizeof (buf)) || !UTI_BytesToHex(inst->context.s2c.key, inst->context.s2c.length, buf, sizeof (buf)) ||
fprintf(f, "%s ", buf) < 0 || fprintf(f, "%s ", buf) < 0 ||
!UTI_BytesToHex(inst->context.c2s.key, inst->context.c2s.length, buf, sizeof (buf)) || !UTI_BytesToHex(inst->context.c2s.key, inst->context.c2s.length, buf, sizeof (buf)) ||
@@ -616,6 +622,8 @@ load_cookies(NNC_Instance inst)
inst->siv = NULL; inst->siv = NULL;
if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 || if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 ||
!inst->name || strcmp(words[0], inst->name) != 0 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 || !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 ||
sscanf(words[0], "%lf", &context_time) != 1 || sscanf(words[0], "%lf", &context_time) != 1 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 || !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 ||

View File

@@ -96,11 +96,11 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
NKE_Cookie cookie; NKE_Cookie cookie;
void *ef_body; void *ef_body;
*kod = 0;
if (!server) if (!server)
return 0; return 0;
*kod = 0;
server->num_cookies = 0; server->num_cookies = 0;
server->req_tx = packet->transmit_ts; server->req_tx = packet->transmit_ts;

View File

@@ -181,7 +181,7 @@ RCL_AddRefclock(RefclockParameters *params)
LOG_FATAL("refclock tai option requires leapsectz"); LOG_FATAL("refclock tai option requires leapsectz");
inst->data = NULL; inst->data = NULL;
inst->driver_parameter = params->driver_parameter; inst->driver_parameter = Strdup(params->driver_parameter);
inst->driver_parameter_length = 0; inst->driver_parameter_length = 0;
inst->driver_poll = params->driver_poll; inst->driver_poll = params->driver_poll;
inst->poll = params->poll; inst->poll = params->poll;
@@ -261,8 +261,6 @@ RCL_AddRefclock(RefclockParameters *params)
params->driver_name, UTI_RefidToString(inst->ref_id), params->driver_name, UTI_RefidToString(inst->ref_id),
inst->poll, inst->driver_poll, params->filter_length); inst->poll, inst->driver_poll, params->filter_length);
Free(params->driver_name);
return 1; return 1;
} }

View File

@@ -190,6 +190,7 @@ typedef struct {
IPAddr ip_addr; IPAddr ip_addr;
char state_char; char state_char;
int authentication; int authentication;
NTP_Leap leap;
int conf_options; int conf_options;
int eff_options; int eff_options;
uint32_t last_sample_ago; uint32_t last_sample_ago;

View File

@@ -799,14 +799,14 @@ SCH_MainLoop(void)
LCL_ReadRawTime(&now); LCL_ReadRawTime(&now);
LCL_CookTime(&now, &cooked, &err); LCL_CookTime(&now, &cooked, &err);
update_monotonic_time(&now, &last_select_ts_raw);
/* Check if the time didn't jump unexpectedly */ /* Check if the time didn't jump unexpectedly */
if (!check_current_time(&saved_now, &now, status == 0, &saved_tv, ptv)) { if (!check_current_time(&saved_now, &now, status == 0, &saved_tv, ptv)) {
/* Cook the time again after handling the step */ /* Cook the time again after handling the step */
LCL_CookTime(&now, &cooked, &err); LCL_CookTime(&now, &cooked, &err);
} }
update_monotonic_time(&cooked, &last_select_ts);
last_select_ts_raw = now; last_select_ts_raw = now;
last_select_ts = cooked; last_select_ts = cooked;
last_select_ts_err = err; last_select_ts_err = err;

View File

@@ -742,18 +742,10 @@ process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
SCK_Message *message) SCK_Message *message)
{ {
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
int r = 1;
if (msg->msg_iovlen != 1) { if (msg->msg_namelen <= sizeof (union sockaddr_all) &&
DEBUG_LOG("Unexpected iovlen"); msg->msg_namelen > sizeof (((struct sockaddr *)msg->msg_name)->sa_family)) {
return 0;
}
if (msg->msg_namelen > sizeof (union sockaddr_all)) {
DEBUG_LOG("Truncated source address");
return 0;
}
if (msg->msg_namelen > sizeof (((struct sockaddr *)msg->msg_name)->sa_family)) {
switch (((struct sockaddr *)msg->msg_name)->sa_family) { switch (((struct sockaddr *)msg->msg_name)->sa_family) {
case AF_INET: case AF_INET:
#ifdef FEAT_IPV6 #ifdef FEAT_IPV6
@@ -767,26 +759,38 @@ process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
message->remote_addr.path = ((struct sockaddr_un *)msg->msg_name)->sun_path; message->remote_addr.path = ((struct sockaddr_un *)msg->msg_name)->sun_path;
break; break;
default: default:
init_message_addresses(message, SCK_ADDR_UNSPEC);
DEBUG_LOG("Unexpected address"); DEBUG_LOG("Unexpected address");
return 0; r = 0;
break;
} }
} else { } else {
init_message_addresses(message, SCK_ADDR_UNSPEC); init_message_addresses(message, SCK_ADDR_UNSPEC);
if (msg->msg_namelen > sizeof (union sockaddr_all)) {
DEBUG_LOG("Truncated source address");
r = 0;
}
} }
init_message_nonaddress(message); init_message_nonaddress(message);
message->data = msg->msg_iov[0].iov_base; if (msg->msg_iovlen == 1) {
message->length = msg_length; message->data = msg->msg_iov[0].iov_base;
message->length = msg_length;
} else {
DEBUG_LOG("Unexpected iovlen");
r = 0;
}
if (msg->msg_flags & MSG_TRUNC) { if (msg->msg_flags & MSG_TRUNC) {
log_message(sock_fd, 1, message, "Truncated", NULL); log_message(sock_fd, 1, message, "Truncated", NULL);
return 0; r = 0;
} }
if (msg->msg_flags & MSG_CTRUNC) { if (msg->msg_flags & MSG_CTRUNC) {
log_message(sock_fd, 1, message, "Truncated cmsg in", NULL); log_message(sock_fd, 1, message, "Truncated cmsg in", NULL);
return 0; r = 0;
} }
for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
@@ -873,25 +877,31 @@ process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
if (err.ee_errno != ENOMSG || err.ee_info != SCM_TSTAMP_SND || if (err.ee_errno != ENOMSG || err.ee_info != SCM_TSTAMP_SND ||
err.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) { err.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
log_message(sock_fd, 1, message, "Unexpected extended error in", NULL); log_message(sock_fd, 1, message, "Unexpected extended error in", NULL);
return 0; r = 0;
} }
} }
#endif #endif
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
if (!(flags & SCK_FLAG_MSG_DESCRIPTOR) || cmsg->cmsg_len != CMSG_LEN(sizeof (int))) { if (!(flags & SCK_FLAG_MSG_DESCRIPTOR) || cmsg->cmsg_len != CMSG_LEN(sizeof (int))) {
unsigned int i; int i, fd;
DEBUG_LOG("Unexpected SCM_RIGHTS"); DEBUG_LOG("Unexpected SCM_RIGHTS");
for (i = 0; CMSG_LEN((i + 1) * sizeof (int)) <= cmsg->cmsg_len; i++) for (i = 0; CMSG_LEN((i + 1) * sizeof (int)) <= cmsg->cmsg_len; i++) {
close(((int *)CMSG_DATA(cmsg))[i]); memcpy(&fd, (char *)CMSG_DATA(cmsg) + i * sizeof (int), sizeof (fd));
return 0; close(fd);
}
r = 0;
} else {
memcpy(&message->descriptor, CMSG_DATA(cmsg), sizeof (message->descriptor));
} }
message->descriptor = *(int *)CMSG_DATA(cmsg);
} }
} }
return 1; if (!r && message->descriptor != INVALID_SOCK_FD)
close(message->descriptor);
return r;
} }
/* ================================================== */ /* ================================================== */
@@ -901,7 +911,7 @@ receive_messages(int sock_fd, int flags, int max_messages, int *num_messages)
{ {
struct MessageHeader *hdr; struct MessageHeader *hdr;
SCK_Message *messages; SCK_Message *messages;
unsigned int i, n; unsigned int i, n, n_ok;
int ret, recv_flags = 0; int ret, recv_flags = 0;
assert(initialised); assert(initialised);
@@ -945,18 +955,20 @@ receive_messages(int sock_fd, int flags, int max_messages, int *num_messages)
received_messages = n; received_messages = n;
for (i = 0; i < n; i++) { for (i = n_ok = 0; i < n; i++) {
hdr = ARR_GetElement(recv_headers, i); hdr = ARR_GetElement(recv_headers, i);
if (!process_header(&hdr->msg_hdr, hdr->msg_len, sock_fd, flags, &messages[i])) if (!process_header(&hdr->msg_hdr, hdr->msg_len, sock_fd, flags, &messages[n_ok]))
return NULL; continue;
log_message(sock_fd, 1, &messages[i], log_message(sock_fd, 1, &messages[n_ok],
flags & SCK_FLAG_MSG_ERRQUEUE ? "Received error" : "Received", NULL); flags & SCK_FLAG_MSG_ERRQUEUE ? "Received error" : "Received", NULL);
n_ok++;
} }
*num_messages = n; *num_messages = n_ok;
return messages; return n_ok > 0 ? messages : NULL;
} }
/* ================================================== */ /* ================================================== */

View File

@@ -1610,6 +1610,7 @@ SRC_GetSelectReport(int index, RPT_SelectReport *report)
report->ip_addr.family = IPADDR_UNSPEC; report->ip_addr.family = IPADDR_UNSPEC;
report->state_char = get_status_char(inst->status); report->state_char = get_status_char(inst->status);
report->authentication = inst->authenticated; report->authentication = inst->authenticated;
report->leap = inst->leap;
report->conf_options = inst->conf_sel_options; report->conf_options = inst->conf_sel_options;
report->eff_options = inst->sel_options; report->eff_options = inst->sel_options;
report->last_sample_ago = inst->sel_info.last_sample_ago; report->last_sample_ago = inst->sel_info.last_sample_ago;

12
sys.c
View File

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

10
sys.h
View File

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

View File

@@ -426,7 +426,7 @@ SYS_Linux_Finalise(void)
#ifdef FEAT_PRIVDROP #ifdef FEAT_PRIVDROP
void void
SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control) SYS_Linux_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context, int clock_control)
{ {
char cap_text[256]; char cap_text[256];
cap_t cap; cap_t cap;
@@ -450,6 +450,10 @@ SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control)
clock_control ? "cap_sys_time=ep" : "") >= sizeof (cap_text)) clock_control ? "cap_sys_time=ep" : "") >= sizeof (cap_text))
assert(0); assert(0);
/* Helpers don't need any capabilities */
if (context != SYS_MAIN_PROCESS)
cap_text[0] = '\0';
if ((cap = cap_from_text(cap_text)) == NULL) { if ((cap = cap_from_text(cap_text)) == NULL) {
LOG_FATAL("cap_from_text() failed"); LOG_FATAL("cap_from_text() failed");
} }
@@ -480,7 +484,7 @@ void check_seccomp_applicability(void)
/* ================================================== */ /* ================================================== */
void void
SYS_Linux_EnableSystemCallFilter(int level, SYS_SystemCallContext context) SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
{ {
const int syscalls[] = { const int syscalls[] = {
/* Clock */ /* Clock */

View File

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

View File

@@ -415,9 +415,10 @@ SYS_MacOSX_SetScheduler(int SchedPriority)
/* ================================================== */ /* ================================================== */
#ifdef FEAT_PRIVDROP #ifdef FEAT_PRIVDROP
void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid) void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context)
{ {
PRV_StartHelper(); if (context == SYS_MAIN_PROCESS)
PRV_StartHelper();
UTI_DropRoot(uid, gid); UTI_DropRoot(uid, gid);
} }

View File

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

View File

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

View File

@@ -28,10 +28,12 @@
#ifndef GOT_SYS_NETBSD_H #ifndef GOT_SYS_NETBSD_H
#define GOT_SYS_NETBSD_H #define GOT_SYS_NETBSD_H
#include "sys.h"
void SYS_NetBSD_Initialise(void); void SYS_NetBSD_Initialise(void);
void SYS_NetBSD_Finalise(void); void SYS_NetBSD_Finalise(void);
void SYS_NetBSD_DropRoot(uid_t uid, gid_t gid); void SYS_NetBSD_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context, int clock_control);
#endif #endif

View File

@@ -55,9 +55,10 @@ SYS_Solaris_Finalise(void)
#ifdef FEAT_PRIVDROP #ifdef FEAT_PRIVDROP
void void
SYS_Solaris_DropRoot(uid_t uid, gid_t gid) SYS_Solaris_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context)
{ {
PRV_StartHelper(); if (context == SYS_MAIN_PROCESS)
PRV_StartHelper();
UTI_DropRoot(uid, gid); UTI_DropRoot(uid, gid);
} }
#endif #endif

View File

@@ -27,10 +27,12 @@
#ifndef GOT_SYS_SOLARIS_H #ifndef GOT_SYS_SOLARIS_H
#define GOT_SYS_SOLARIS_H #define GOT_SYS_SOLARIS_H
#include "sys.h"
void SYS_Solaris_Initialise(void); void SYS_Solaris_Initialise(void);
void SYS_Solaris_Finalise(void); void SYS_Solaris_Finalise(void);
void SYS_Solaris_DropRoot(uid_t uid, gid_t gid); void SYS_Solaris_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context);
#endif #endif

View File

@@ -63,7 +63,7 @@ check_file_messages "20.*123\.1.* 111 111 1111" 99 103 measurements.log || test_
check_file_messages "20.*123\.1.* 111 001 0000" 0 0 measurements.log || test_fail check_file_messages "20.*123\.1.* 111 001 0000" 0 0 measurements.log || test_fail
check_file_messages " 2 1 .* 4460 " 350 390 log.packets || test_fail check_file_messages " 2 1 .* 4460 " 350 390 log.packets || test_fail
check_file_messages "." 6 6 ntskeys || test_fail check_file_messages "." 6 6 ntskeys || test_fail
check_file_messages "." 11 12 192.168.123.1.nts || test_fail check_file_messages "." 12 13 192.168.123.1.nts || test_fail
rm -f tmp/measurements.log rm -f tmp/measurements.log
export CLKNETSIM_START_DATE=$(date -d 'Jan 1 00:00:00 UTC 2010 + 40000 sec' +'%s') export CLKNETSIM_START_DATE=$(date -d 'Jan 1 00:00:00 UTC 2010 + 40000 sec' +'%s')
@@ -83,7 +83,7 @@ check_file_messages "20.*123\.1.* 111 001 0000" 0 0 measurements.log || test_fai
check_file_messages " 2 1 .* 4460 " 6 10 log.packets || test_fail check_file_messages " 2 1 .* 4460 " 6 10 log.packets || test_fail
check_file_messages "^9\.......e+03 2 1 .* 4460 " 6 10 log.packets || test_fail check_file_messages "^9\.......e+03 2 1 .* 4460 " 6 10 log.packets || test_fail
check_file_messages "." 6 6 ntskeys || test_fail check_file_messages "." 6 6 ntskeys || test_fail
check_file_messages "." 11 12 192.168.123.1.nts || test_fail check_file_messages "." 12 13 192.168.123.1.nts || test_fail
rm -f tmp/measurements.log rm -f tmp/measurements.log
client_conf=" client_conf="

View File

@@ -64,7 +64,7 @@ check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atm
run_chronyc "clients" || test_fail run_chronyc "clients" || test_fail
check_chronyc_output "^Hostname NTP Drop Int IntL Last Cmd Drop Int Last check_chronyc_output "^Hostname NTP Drop Int IntL Last Cmd Drop Int Last
=============================================================================== ===============================================================================
127\.0\.0\.1 [1-9] 0 - - [0-9] 0 0 - -$" \ 127\.0\.0\.1 [0-9 ]+ 0 [-0-9 ]+ - [0-9] 0 0 - -$" \
|| test_fail || test_fail
run_chronyc "ntpdata $server" || test_fail run_chronyc "ntpdata $server" || test_fail
@@ -75,7 +75,7 @@ Leap status : Normal
Version : 4 Version : 4
Mode : Server Mode : Server
Stratum : 10 Stratum : 10
Poll interval : -6 \(0 seconds\) Poll interval : (-6|[0345]) \([0-9]+ seconds\)
Precision : [0-9 +-]+ \(0\.[0-9]+ seconds\) Precision : [0-9 +-]+ \(0\.[0-9]+ seconds\)
Root delay : 0\.000000 seconds Root delay : 0\.000000 seconds
Root dispersion : 0\.000000 seconds Root dispersion : 0\.000000 seconds
@@ -96,9 +96,9 @@ Total RX : [0-9]+
Total valid RX : [0-9]+$" || test_fail Total valid RX : [0-9]+$" || test_fail
run_chronyc "selectdata" || test_fail run_chronyc "selectdata" || test_fail
check_chronyc_output "^S Name/IP Address Auth COpts EOpts Last Score Interval + check_chronyc_output "^S Name/IP Address Auth COpts EOpts Last Score Interval Leap
==================================================================== =======================================================================
M 127\.0\.0\.1 N ----- ----- 0 1\.0 \+0ns \+0ns$" || test_fail M 127\.0\.0\.1 N ----- ----- 0 1\.0 \+0ns \+0ns \?$" || test_fail
run_chronyc "serverstats" || test_fail run_chronyc "serverstats" || test_fail
check_chronyc_output "^NTP packets received : [0-9]+ check_chronyc_output "^NTP packets received : [0-9]+

View File

@@ -73,7 +73,7 @@ get_random_key_id(void)
uint32_t id; uint32_t id;
do { do {
id = random() % 6 + 2; id = random() % 8 + 2;
} while (!KEY_KeyKnown(id)); } while (!KEY_KeyKnown(id));
return id; return id;

View File

@@ -4,3 +4,5 @@
5 SHA1 HEX:B71744EA01FBF01CA30D173ECDDF901952AE356A 5 SHA1 HEX:B71744EA01FBF01CA30D173ECDDF901952AE356A
6 SHA512 HEX:DE027482F22B201FC20863F58C74095E7906089F 6 SHA512 HEX:DE027482F22B201FC20863F58C74095E7906089F
7 SHA512 HEX:DE027482F22B201FC20863F58C74095E7906089F 7 SHA512 HEX:DE027482F22B201FC20863F58C74095E7906089F
8 AES128 HEX:5D5E8A31D4B459A66D445259E147CFB5
9 AES128 HEX:5D5E8A31D4B459A66D445259E147CFB5

View File

@@ -81,6 +81,9 @@ test_unit(void)
sample.root_dispersion = sample.peer_dispersion; sample.root_dispersion = sample.peer_dispersion;
sample.stratum = 1; sample.stratum = 1;
if (random() % 2)
SRC_SetLeapStatus(srcs[j], random() % 4);
DEBUG_LOG("source %d sample %d offset %f delay %f disp %f", j, k, DEBUG_LOG("source %d sample %d offset %f delay %f disp %f", j, k,
sample.offset, sample.peer_delay, sample.peer_dispersion); sample.offset, sample.peer_delay, sample.peer_dispersion);
@@ -132,6 +135,12 @@ test_unit(void)
TEST_CHECK(!trusted || !passed || (passed_lo >= trusted_lo && passed_hi <= trusted_hi)); TEST_CHECK(!trusted || !passed || (passed_lo >= trusted_lo && passed_hi <= trusted_hi));
TEST_CHECK(!passed || !trusted || trusted_passed >= 1); TEST_CHECK(!passed || !trusted || trusted_passed >= 1);
TEST_CHECK(!passed || !required || required_passed > 0); TEST_CHECK(!passed || !required || required_passed > 0);
for (l = 0; l <= j; l++) {
TEST_CHECK(sources[l]->leap_vote ==
(sources[l]->status >= SRC_NONPREFERRED &&
(!trusted || sources[l]->sel_options & SRC_SELECT_TRUST)));
}
} }
} }

View File

@@ -330,9 +330,12 @@ void test_unit(void) {
TEST_CHECK(UTI_HexToBytes(buf, buf, sizeof (buf)) == 7); TEST_CHECK(UTI_HexToBytes(buf, buf, sizeof (buf)) == 7);
TEST_CHECK(memcmp(buf, "\xAB\x12\x34\x56\x78\x00\x01", 7) == 0); TEST_CHECK(memcmp(buf, "\xAB\x12\x34\x56\x78\x00\x01", 7) == 0);
TEST_CHECK(UTI_BytesToHex("", 0, buf, 0) == 0);
TEST_CHECK(UTI_BytesToHex("\xAB\x12\x34\x56\x78\x00\x01", 7, buf, 14) == 0); TEST_CHECK(UTI_BytesToHex("\xAB\x12\x34\x56\x78\x00\x01", 7, buf, 14) == 0);
TEST_CHECK(UTI_BytesToHex("\xAB\x12\x34\x56\x78\x00\x01", 7, buf, 15) == 1); TEST_CHECK(UTI_BytesToHex("\xAB\x12\x34\x56\x78\x00\x01", 7, buf, 15) == 1);
TEST_CHECK(strcmp(buf, "AB123456780001") == 0); TEST_CHECK(strcmp(buf, "AB123456780001") == 0);
TEST_CHECK(UTI_BytesToHex("\xAB\x12\x34\x56\x78\x00\x01", 0, buf, 15) == 1);
TEST_CHECK(strcmp(buf, "") == 0);
TEST_CHECK(snprintf(buf, sizeof (buf), "%s", "") < sizeof (buf)); TEST_CHECK(snprintf(buf, sizeof (buf), "%s", "") < sizeof (buf));
TEST_CHECK(UTI_SplitString(buf, words, 3) == 0); TEST_CHECK(UTI_SplitString(buf, words, 3) == 0);

5
util.c
View File

@@ -1417,6 +1417,11 @@ UTI_BytesToHex(const void *buf, unsigned int buf_len, char *hex, unsigned int he
{ {
unsigned int i, l; unsigned int i, l;
if (hex_len < 1)
return 0;
hex[0] = '\0';
for (i = l = 0; i < buf_len; i++, l += 2) { for (i = l = 0; i < buf_len; i++, l += 2) {
if (l + 2 >= hex_len || if (l + 2 >= hex_len ||
snprintf(hex + l, hex_len - l, "%02hhX", ((const char *)buf)[i]) != 2) snprintf(hex + l, hex_len - l, "%02hhX", ((const char *)buf)[i]) != 2)