diff -Nru chrony-3.5/addressing.h chrony-4.1/addressing.h --- chrony-3.5/addressing.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/addressing.h 2021-05-12 11:06:15.000000000 +0000 @@ -30,16 +30,19 @@ #include "sysincl.h" /* This type is used to represent an IPv4 address or IPv6 address. + Addresses which are not resolved yet can be represented with an ID. All parts are in HOST order, NOT network order. */ #define IPADDR_UNSPEC 0 #define IPADDR_INET4 1 #define IPADDR_INET6 2 +#define IPADDR_ID 3 typedef struct { union { uint32_t in4; uint8_t in6[16]; + uint32_t id; } addr; uint16_t family; uint16_t _pad; @@ -47,8 +50,10 @@ typedef struct { IPAddr ip_addr; - unsigned short port; -} NTP_Remote_Address; + uint16_t port; +} IPSockAddr; + +typedef IPSockAddr NTP_Remote_Address; #define INVALID_IF_INDEX -1 diff -Nru chrony-3.5/addrfilt.c chrony-4.1/addrfilt.c --- chrony-3.5/addrfilt.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/addrfilt.c 2021-05-12 11:06:15.000000000 +0000 @@ -247,6 +247,8 @@ set_subnet(&table->base6, ip6, 4, 0, new_state, delete_children) == ADF_SUCCESS) return ADF_SUCCESS; break; + default: + break; } return ADF_BADSUBNET; @@ -359,9 +361,9 @@ case IPADDR_INET6: split_ip6(ip_addr, ip6); return check_ip_in_node(&table->base6, ip6); + default: + return 0; } - - return 0; } /* ================================================== */ diff -Nru chrony-3.5/candm.h chrony-4.1/candm.h --- chrony-3.5/candm.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/candm.h 2021-05-12 11:06:15.000000000 +0000 @@ -101,7 +101,15 @@ #define REQ_ADD_PEER3 61 #define REQ_SHUTDOWN 62 #define REQ_ONOFFLINE 63 -#define N_REQUEST_TYPES 64 +#define REQ_ADD_SOURCE 64 +#define REQ_NTP_SOURCE_NAME 65 +#define REQ_RESET_SOURCES 66 +#define REQ_AUTH_DATA 67 +#define REQ_CLIENT_ACCESSES_BY_INDEX3 68 +#define REQ_SELECT_DATA 69 +#define REQ_RELOAD_SOURCES 70 +#define REQ_DOFFSET2 71 +#define N_REQUEST_TYPES 72 /* Structure used to exchange timespecs independent of time_t size */ typedef struct { @@ -245,6 +253,11 @@ int32_t EOR; } REQ_Ac_Check; +/* Source types in NTP source requests */ +#define REQ_ADDSRC_SERVER 1 +#define REQ_ADDSRC_PEER 2 +#define REQ_ADDSRC_POOL 3 + /* Flags used in NTP source requests */ #define REQ_ADDSRC_ONLINE 0x1 #define REQ_ADDSRC_AUTOOFFLINE 0x2 @@ -255,9 +268,12 @@ #define REQ_ADDSRC_REQUIRE 0x40 #define REQ_ADDSRC_INTERLEAVED 0x80 #define REQ_ADDSRC_BURST 0x100 +#define REQ_ADDSRC_NTS 0x200 +#define REQ_ADDSRC_COPY 0x400 typedef struct { - IPAddr ip_addr; + uint32_t type; + uint8_t name[256]; uint32_t port; int32_t minpoll; int32_t maxpoll; @@ -269,6 +285,7 @@ int32_t min_samples; int32_t max_samples; uint32_t authkey; + uint32_t nts_port; Float max_delay; Float max_delay_ratio; Float max_delay_dev_ratio; @@ -277,7 +294,8 @@ Float offset; uint32_t flags; int32_t filter_length; - uint32_t reserved[3]; + uint32_t cert_set; + uint32_t reserved[2]; int32_t EOR; } REQ_NTP_Source; @@ -292,8 +310,7 @@ } REQ_Dfreq; typedef struct { - int32_t sec; - int32_t usec; + Float doffset; int32_t EOR; } REQ_Doffset; @@ -309,6 +326,8 @@ typedef struct { uint32_t first_index; uint32_t n_clients; + uint32_t min_hits; + uint32_t reset; int32_t EOR; } REQ_ClientAccessesByIndex; @@ -335,6 +354,21 @@ int32_t EOR; } REQ_NTPData; +typedef struct { + IPAddr ip_addr; + int32_t EOR; +} REQ_NTPSourceName; + +typedef struct { + IPAddr ip_addr; + int32_t EOR; +} REQ_AuthData; + +typedef struct { + uint32_t index; + int32_t EOR; +} REQ_SelectData; + /* ================================================== */ #define PKT_TYPE_CMD_REQUEST 1 @@ -371,9 +405,10 @@ domain socket. Version 6 (no authentication) : changed format of client accesses by index - (using new request/reply types) and manual timestamp, added new fields and + (two times), delta offset, and manual timestamp, added new fields and flags to NTP source request and report, made length of manual list constant, - added new commands: ntpdata, refresh, serverstats, shutdown + added new commands: authdata, ntpdata, onoffline, refresh, reset, + selectdata, serverstats, shutdown, sourcename */ #define PROTO_VERSION_NUMBER 6 @@ -387,8 +422,8 @@ #define PROTO_VERSION_PADDING 6 /* The maximum length of padding in request packet, currently - defined by MANUAL_LIST */ -#define MAX_PADDING_LENGTH 396 + defined by CLIENT_ACCESSES_BY_INDEX3 */ +#define MAX_PADDING_LENGTH 484 /* ================================================== */ @@ -437,6 +472,9 @@ REQ_ReselectDistance reselect_distance; REQ_SmoothTime smoothtime; REQ_NTPData ntp_data; + REQ_NTPSourceName ntp_source_name; + REQ_AuthData auth_data; + REQ_SelectData select_data; } data; /* Command specific parameters */ /* Padding used to prevent traffic amplification. It only defines the @@ -473,7 +511,12 @@ #define RPY_NTP_DATA 16 #define RPY_MANUAL_TIMESTAMP2 17 #define RPY_MANUAL_LIST2 18 -#define N_REPLY_TYPES 19 +#define RPY_NTP_SOURCE_NAME 19 +#define RPY_AUTH_DATA 20 +#define RPY_CLIENT_ACCESSES_BY_INDEX3 21 +#define RPY_SERVER_STATS2 22 +#define RPY_SELECT_DATA 23 +#define N_REPLY_TYPES 24 /* Status codes */ #define STT_SUCCESS 0 @@ -497,6 +540,7 @@ #define STT_INVALIDAF 17 #define STT_BADPKTVERSION 18 #define STT_BADPKTLENGTH 19 +#define STT_INVALIDNAME 21 typedef struct { int32_t EOR; @@ -511,17 +555,12 @@ #define RPY_SD_MD_PEER 1 #define RPY_SD_MD_REF 2 -#define RPY_SD_ST_SYNC 0 -#define RPY_SD_ST_UNREACH 1 +#define RPY_SD_ST_SELECTED 0 +#define RPY_SD_ST_NONSELECTABLE 1 #define RPY_SD_ST_FALSETICKER 2 #define RPY_SD_ST_JITTERY 3 -#define RPY_SD_ST_CANDIDATE 4 -#define RPY_SD_ST_OUTLIER 5 - -#define RPY_SD_FLAG_NOSELECT 0x1 -#define RPY_SD_FLAG_PREFER 0x2 -#define RPY_SD_FLAG_TRUST 0x4 -#define RPY_SD_FLAG_REQUIRE 0x8 +#define RPY_SD_ST_UNSELECTED 4 +#define RPY_SD_ST_SELECTABLE 5 typedef struct { IPAddr ip_addr; @@ -590,14 +629,17 @@ typedef struct { IPAddr ip; uint32_t ntp_hits; + uint32_t nke_hits; uint32_t cmd_hits; uint32_t ntp_drops; + uint32_t nke_drops; uint32_t cmd_drops; int8_t ntp_interval; + int8_t nke_interval; int8_t cmd_interval; int8_t ntp_timeout_interval; - int8_t pad; uint32_t last_ntp_hit_ago; + uint32_t last_nke_hit_ago; uint32_t last_cmd_hit_ago; } RPY_ClientAccesses_Client; @@ -611,10 +653,13 @@ typedef struct { uint32_t ntp_hits; + uint32_t nke_hits; uint32_t cmd_hits; uint32_t ntp_drops; + uint32_t nke_drops; uint32_t cmd_drops; uint32_t log_drops; + uint32_t ntp_auth_hits; int32_t EOR; } RPY_ServerStats; @@ -689,6 +734,50 @@ } RPY_NTPData; typedef struct { + uint8_t name[256]; + int32_t EOR; +} RPY_NTPSourceName; + +#define RPY_AD_MD_NONE 0 +#define RPY_AD_MD_SYMMETRIC 1 +#define RPY_AD_MD_NTS 2 + +typedef struct { + uint16_t mode; + uint16_t key_type; + uint32_t key_id; + uint16_t key_length; + uint16_t ke_attempts; + uint32_t last_ke_ago; + uint16_t cookies; + uint16_t cookie_length; + uint16_t nak; + uint16_t pad; + int32_t EOR; +} RPY_AuthData; + +#define RPY_SD_OPTION_NOSELECT 0x1 +#define RPY_SD_OPTION_PREFER 0x2 +#define RPY_SD_OPTION_TRUST 0x4 +#define RPY_SD_OPTION_REQUIRE 0x8 + +typedef struct { + uint32_t ref_id; + IPAddr ip_addr; + uint8_t state_char; + uint8_t authentication; + uint8_t leap; + uint8_t pad; + uint16_t conf_options; + uint16_t eff_options; + uint32_t last_sample_ago; + Float score; + Float lo_limit; + Float hi_limit; + int32_t EOR; +} RPY_SelectData; + +typedef struct { uint8_t version; uint8_t pkt_type; uint8_t res1; @@ -717,6 +806,9 @@ RPY_Activity activity; RPY_Smoothing smoothing; RPY_NTPData ntp_data; + RPY_NTPSourceName ntp_source_name; + RPY_AuthData auth_data; + RPY_SelectData select_data; } data; /* Reply specific parameters */ } CMD_Reply; diff -Nru chrony-3.5/client.c chrony-4.1/client.c --- chrony-3.5/client.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/client.c 2021-05-12 11:06:15.000000000 +0000 @@ -4,7 +4,7 @@ ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Lonnie Abelbeck 2016, 2018 - * Copyright (C) Miroslav Lichvar 2009-2018 + * Copyright (C) Miroslav Lichvar 2009-2021 * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,51 +33,49 @@ #include "array.h" #include "candm.h" +#include "cmac.h" #include "logging.h" #include "memory.h" #include "nameserv.h" #include "getdate.h" #include "cmdparse.h" #include "pktlength.h" +#include "socket.h" #include "util.h" #ifdef FEAT_READLINE -#ifdef USE_EDITLINE #include -#else -#include -#include -#endif #endif /* ================================================== */ -union sockaddr_all { - struct sockaddr_in in4; -#ifdef FEAT_IPV6 - struct sockaddr_in6 in6; -#endif - struct sockaddr_un un; - struct sockaddr sa; +struct Address { + SCK_AddressType type; + union { + IPSockAddr ip; + char *path; + } addr; }; -static ARR_Instance sockaddrs; +static ARR_Instance server_addresses; static int sock_fd = -1; -static int quit = 0; +static volatile int quit = 0; static int on_terminal = 0; static int no_dns = 0; +static int source_names = 0; + static int csv_mode = 0; /* ================================================== */ /* Log a message. This is a minimalistic replacement of the logging.c implementation to avoid linking with it and other modules. */ -int log_debug_enabled = 0; +LOG_Severity log_min_severity = LOGS_INFO; void LOG_Message(LOG_Severity severity, #if DEBUG > 0 @@ -87,6 +85,9 @@ { va_list ap; + if (severity < log_min_severity) + return; + va_start(ap, format); vfprintf(stderr, format, ap); putc('\n', stderr); @@ -145,15 +146,15 @@ /* ================================================== */ static ARR_Instance -get_sockaddrs(const char *hostnames, int port) +get_addresses(const char *hostnames, int port) { + struct Address *addr; ARR_Instance addrs; char *hostname, *s1, *s2; IPAddr ip_addrs[DNS_MAX_ADDRESSES]; - union sockaddr_all *addr; int i; - addrs = ARR_CreateInstance(sizeof (union sockaddr_all)); + addrs = ARR_CreateInstance(sizeof (*addr)); s1 = Strdup(hostnames); /* Parse the comma-separated list of hostnames */ @@ -164,11 +165,9 @@ /* hostname starting with / is considered a path of Unix domain socket */ if (hostname[0] == '/') { - addr = (union sockaddr_all *)ARR_GetNewElement(addrs); - if (snprintf(addr->un.sun_path, sizeof (addr->un.sun_path), "%s", hostname) >= - sizeof (addr->un.sun_path)) - LOG_FATAL("Unix socket path too long"); - addr->un.sun_family = AF_UNIX; + addr = ARR_GetNewElement(addrs); + addr->type = SCK_ADDR_UNIX; + addr->addr.path = Strdup(hostname); } else { if (DNS_Name2IPAddress(hostname, ip_addrs, DNS_MAX_ADDRESSES) != DNS_Success) { DEBUG_LOG("Could not get IP address for %s", hostname); @@ -176,8 +175,10 @@ } for (i = 0; i < DNS_MAX_ADDRESSES && ip_addrs[i].family != IPADDR_UNSPEC; i++) { - addr = (union sockaddr_all *)ARR_GetNewElement(addrs); - UTI_IPAndPortToSockaddr(&ip_addrs[i], port, (struct sockaddr *)addr); + addr = ARR_GetNewElement(addrs); + addr->type = SCK_ADDR_IP; + addr->addr.ip.ip_addr = ip_addrs[i]; + addr->addr.ip.port = port; DEBUG_LOG("Resolved %s to %s", hostname, UTI_IPToString(&ip_addrs[i])); } } @@ -188,69 +189,59 @@ } /* ================================================== */ -/* Initialise the socket used to talk to the daemon */ -static int -prepare_socket(union sockaddr_all *addr) +static void +free_addresses(ARR_Instance addresses) { - socklen_t addr_len; - char *dir; + struct Address *addr; + unsigned int i; - switch (addr->sa.sa_family) { - case AF_UNIX: - addr_len = sizeof (addr->un); - break; - case AF_INET: - addr_len = sizeof (addr->in4); - break; -#ifdef FEAT_IPV6 - case AF_INET6: - addr_len = sizeof (addr->in6); - break; -#endif - default: - assert(0); - } + for (i = 0; i < ARR_GetSize(addresses); i++) { + addr = ARR_GetElement(addresses, i); - sock_fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0); - - if (sock_fd < 0) { - DEBUG_LOG("Could not create socket : %s", strerror(errno)); - return 0; + if (addr->type == SCK_ADDR_UNIX) + Free(addr->addr.path); } - if (addr->sa.sa_family == AF_UNIX) { - struct sockaddr_un sa_un; + ARR_DestroyInstance(addresses); +} - /* 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->un.sun_path); - if (snprintf(sa_un.sun_path, sizeof (sa_un.sun_path), - "%s/chronyc.%d.sock", dir, (int)getpid()) >= sizeof (sa_un.sun_path)) - LOG_FATAL("Unix socket path too long"); - Free(dir); +/* ================================================== */ +/* Initialise the socket used to talk to the daemon */ - sa_un.sun_family = AF_UNIX; - unlink(sa_un.sun_path); +static int +open_socket(struct Address *addr) +{ + char *dir, *local_addr; + size_t local_addr_len; - /* Bind the socket to the path */ - if (bind(sock_fd, (struct sockaddr *)&sa_un, sizeof (sa_un)) < 0) { - DEBUG_LOG("Could not bind socket : %s", strerror(errno)); - return 0; - } + 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); - /* Allow server without root privileges to send replies to our socket */ - if (chmod(sa_un.sun_path, 0666) < 0) { - DEBUG_LOG("Could not change socket permissions : %s", strerror(errno)); - return 0; - } + break; + default: + assert(0); } - if (connect(sock_fd, &addr->sa, addr_len) < 0) { - DEBUG_LOG("Could not connect socket : %s", strerror(errno)); + if (sock_fd < 0) return 0; - } return 1; } @@ -260,20 +251,11 @@ static void close_io(void) { - union sockaddr_all addr; - socklen_t addr_len = sizeof (addr); - if (sock_fd < 0) return; - /* Remove our Unix domain socket */ - if (getsockname(sock_fd, &addr.sa, &addr_len) < 0) - LOG_FATAL("getsockname() failed : %s", strerror(errno)); - if (addr_len <= sizeof (addr) && addr_len > sizeof (addr.sa.sa_family) && - addr.sa.sa_family == AF_UNIX) - unlink(addr.un.sun_path); - - close(sock_fd); + SCK_RemoveSocket(sock_fd); + SCK_CloseSocket(sock_fd); sock_fd = -1; } @@ -283,7 +265,7 @@ open_io(void) { static unsigned int address_index = 0; - union sockaddr_all *addr; + struct Address *addr; /* If a socket is already opened, close it and try the next address */ if (sock_fd >= 0) { @@ -292,11 +274,10 @@ } /* Find an address for which a socket can be opened and connected */ - for (; address_index < ARR_GetSize(sockaddrs); address_index++) { - addr = (union sockaddr_all *)ARR_GetElement(sockaddrs, address_index); - DEBUG_LOG("Opening connection to %s", UTI_SockaddrToString(&addr->sa)); + for (; address_index < ARR_GetSize(server_addresses); address_index++) { + addr = ARR_GetElement(server_addresses, address_index); - if (prepare_socket(addr)) + if (open_socket(addr)) return 1; close_io(); @@ -334,6 +315,9 @@ for (; i < 16; i++) mask->addr.in6[i] = 0x0; break; + case IPADDR_ID: + mask->family = IPADDR_UNSPEC; + break; default: assert(0); } @@ -342,6 +326,20 @@ /* ================================================== */ static int +parse_source_address(char *word, IPAddr *address) +{ + if (UTI_StringToIdIP(word, address)) + return 1; + + if (DNS_Name2IPAddress(word, address, 1) == DNS_Success) + return 1; + + return 0; +} + +/* ================================================== */ + +static int read_mask_address(char *line, IPAddr *mask, IPAddr *address) { unsigned int bits; @@ -367,7 +365,7 @@ } } } else { - if (DNS_Name2IPAddress(p, address, 1) == DNS_Success) { + if (parse_source_address(p, address)) { bits_to_mask(-1, address->family, mask); return 1; } else { @@ -447,7 +445,7 @@ LOG(LOGS_ERR, "Invalid syntax for address value"); ok = 0; } else { - if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) { + if (!parse_source_address(hostname, address)) { LOG(LOGS_ERR, "Could not get address for hostname"); ok = 0; } else { @@ -475,7 +473,7 @@ LOG(LOGS_ERR, "Invalid syntax for address value"); ok = 0; } else { - if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) { + if (!parse_source_address(hostname, address)) { LOG(LOGS_ERR, "Could not get address for hostname"); ok = 0; } else { @@ -958,38 +956,11 @@ /* ================================================== */ static int -accheck_getaddr(char *line, IPAddr *addr) -{ - unsigned long a, b, c, d; - IPAddr ip; - char *p; - p = line; - if (!*p) { - return 0; - } else { - if (sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d) == 4) { - addr->family = IPADDR_INET4; - addr->addr.in4 = (a<<24) | (b<<16) | (c<<8) | d; - return 1; - } else { - if (DNS_Name2IPAddress(p, &ip, 1) != DNS_Success) { - return 0; - } else { - *addr = ip; - return 1; - } - } - } -} - -/* ================================================== */ - -static int process_cmd_accheck(CMD_Request *msg, char *line) { IPAddr ip; msg->command = htons(REQ_ACCHECK); - if (accheck_getaddr(line, &ip)) { + if (DNS_Name2IPAddress(line, &ip, 1) == DNS_Success) { UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip); return 1; } else { @@ -1005,7 +976,7 @@ { IPAddr ip; msg->command = htons(REQ_CMDACCHECK); - if (accheck_getaddr(line, &ip)) { + if (DNS_Name2IPAddress(line, &ip, 1) == DNS_Success) { UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip); return 1; } else { @@ -1016,73 +987,76 @@ /* ================================================== */ -static void +static int process_cmd_dfreq(CMD_Request *msg, char *line) { double dfreq; - msg->command = htons(REQ_DFREQ); - if (sscanf(line, "%lf", &dfreq) == 1) { - msg->data.dfreq.dfreq = UTI_FloatHostToNetwork(dfreq); - } else { - msg->data.dfreq.dfreq = UTI_FloatHostToNetwork(0.0); - } -} -/* ================================================== */ + msg->command = htons(REQ_DFREQ); -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; + if (sscanf(line, "%lf", &dfreq) != 1) { + LOG(LOGS_ERR, "Invalid value"); + return 0; } - - *sec = s; - *usec = us; + + msg->data.dfreq.dfreq = UTI_FloatHostToNetwork(dfreq); + return 1; } /* ================================================== */ -static void +static int process_cmd_doffset(CMD_Request *msg, char *line) { double doffset; - long sec, usec; - msg->command = htons(REQ_DOFFSET); - if (sscanf(line, "%lf", &doffset) == 1) { - cvt_to_sec_usec(doffset, &sec, &usec); - msg->data.doffset.sec = htonl(sec); - msg->data.doffset.usec = htonl(usec); - } else { - msg->data.doffset.sec = htonl(0); - msg->data.doffset.usec = htonl(0); + + msg->command = htons(REQ_DOFFSET2); + + if (sscanf(line, "%lf", &doffset) != 1) { + LOG(LOGS_ERR, "Invalid value"); + return 0; } + + msg->data.doffset.doffset = UTI_FloatHostToNetwork(doffset); + return 1; } /* ================================================== */ static int -process_cmd_add_server_or_peer(CMD_Request *msg, char *line) +process_cmd_add_source(CMD_Request *msg, char *line) { CPS_NTP_Source data; IPAddr ip_addr; - int result = 0, status; - const char *opt_name; + int result = 0, status, type; + const char *opt_name, *word; + msg->command = htons(REQ_ADD_SOURCE); + + word = line; + line = CPS_SplitWord(line); + + if (!strcasecmp(word, "server")) { + type = REQ_ADDSRC_SERVER; + } else if (!strcasecmp(word, "peer")) { + type = REQ_ADDSRC_PEER; + } else if (!strcasecmp(word, "pool")) { + type = REQ_ADDSRC_POOL; + } else { + LOG(LOGS_ERR, "Invalid syntax for add command"); + return 0; + } + status = CPS_ParseNTPSourceAdd(line, &data); switch (status) { case 0: LOG(LOGS_ERR, "Invalid syntax for add command"); break; default: - if (DNS_Name2IPAddress(data.name, &ip_addr, 1) != DNS_Success) { + /* Verify that the address is resolvable (chronyc and chronyd are + assumed to be running on the same host) */ + if (strlen(data.name) >= sizeof (msg->data.ntp_source.name) || + DNS_Name2IPAddress(data.name, &ip_addr, 1) != DNS_Success) { LOG(LOGS_ERR, "Invalid host/IP address"); break; } @@ -1093,8 +1067,12 @@ break; } - msg->data.ntp_source.port = htonl((unsigned long) data.port); - UTI_IPHostToNetwork(&ip_addr, &msg->data.ntp_source.ip_addr); + msg->data.ntp_source.type = htonl(type); + if (strlen(data.name) >= sizeof (msg->data.ntp_source.name)) + assert(0); + strncpy((char *)msg->data.ntp_source.name, data.name, + sizeof (msg->data.ntp_source.name)); + msg->data.ntp_source.port = htonl(data.port); msg->data.ntp_source.minpoll = htonl(data.params.minpoll); msg->data.ntp_source.maxpoll = htonl(data.params.maxpoll); msg->data.ntp_source.presend_minpoll = htonl(data.params.presend_minpoll); @@ -1105,6 +1083,7 @@ msg->data.ntp_source.min_samples = htonl(data.params.min_samples); msg->data.ntp_source.max_samples = htonl(data.params.max_samples); msg->data.ntp_source.authkey = htonl(data.params.authkey); + msg->data.ntp_source.nts_port = htonl(data.params.nts_port); msg->data.ntp_source.max_delay = UTI_FloatHostToNetwork(data.params.max_delay); msg->data.ntp_source.max_delay_ratio = UTI_FloatHostToNetwork(data.params.max_delay_ratio); msg->data.ntp_source.max_delay_dev_ratio = @@ -1118,11 +1097,14 @@ (data.params.iburst ? REQ_ADDSRC_IBURST : 0) | (data.params.interleaved ? REQ_ADDSRC_INTERLEAVED : 0) | (data.params.burst ? REQ_ADDSRC_BURST : 0) | + (data.params.nts ? REQ_ADDSRC_NTS : 0) | + (data.params.copy ? REQ_ADDSRC_COPY : 0) | (data.params.sel_options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) | (data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) | (data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) | (data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0)); msg->data.ntp_source.filter_length = htonl(data.params.filter_length); + msg->data.ntp_source.cert_set = htonl(data.params.cert_set); memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved)); result = 1; @@ -1136,24 +1118,6 @@ /* ================================================== */ static int -process_cmd_add_server(CMD_Request *msg, char *line) -{ - msg->command = htons(REQ_ADD_SERVER3); - return process_cmd_add_server_or_peer(msg, line); -} - -/* ================================================== */ - -static int -process_cmd_add_peer(CMD_Request *msg, char *line) -{ - msg->command = htons(REQ_ADD_PEER3); - return process_cmd_add_server_or_peer(msg, line); -} - -/* ================================================== */ - -static int process_cmd_delete(CMD_Request *msg, char *line) { char *hostname; @@ -1168,7 +1132,7 @@ LOG(LOGS_ERR, "Invalid syntax for address"); ok = 0; } else { - if (DNS_Name2IPAddress(hostname, &address, 1) != DNS_Success) { + if (!parse_source_address(hostname, &address)) { LOG(LOGS_ERR, "Could not get address for hostname"); ok = 0; } else { @@ -1197,30 +1161,35 @@ "Wait until synchronised in specified limits\0" "\0\0" "Time sources:\0\0" - "sources [-v]\0Display information about current sources\0" - "sourcestats [-v]\0Display statistics about collected measurements\0" + "sources [-a] [-v]\0Display information about current sources\0" + "sourcestats [-a] [-v]\0Display statistics about collected measurements\0" + "selectdata [-a] [-v]\0Display information about source selection\0" "reselect\0Force reselecting synchronisation source\0" "reselectdist \0Modify reselection distance\0" "\0\0" "NTP sources:\0\0" "activity\0Check how many NTP sources are online/offline\0" + "authdata [-a] [-v]\0Display information about authentication\0" "ntpdata [
]\0Display information about last valid measurement\0" - "add server
[options]\0Add new NTP server\0" - "add peer
[options]\0Add new NTP peer\0" + "add server [options]\0Add new NTP server\0" + "add pool [options]\0Add new pool of NTP servers\0" + "add peer [options]\0Add new NTP peer\0" "delete
\0Remove server or peer\0" - "burst / [/
]\0Start rapid set of measurements\0" + "burst / [[/]
]\0Start rapid set of measurements\0" "maxdelay
\0Modify maximum valid sample delay\0" "maxdelayratio
\0Modify maximum valid delay/minimum ratio\0" "maxdelaydevratio
\0Modify maximum valid delay/deviation ratio\0" "minpoll
\0Modify minimum polling interval\0" "maxpoll
\0Modify maximum polling interval\0" "minstratum
\0Modify minimum stratum\0" - "offline [/
]\0Set sources in subnet to offline status\0" - "online [/
]\0Set sources in subnet to online status\0" + "offline [[/]
]\0Set sources in subnet to offline status\0" + "online [[/]
]\0Set sources in subnet to online status\0" "onoffline\0Set all sources to online or offline status\0" "\0according to network configuration\0" "polltarget
\0Modify poll target\0" "refresh\0Refresh IP addresses\0" + "reload sources\0Re-read *.sources files\0" + "sourcename
\0Display original name\0" "\0\0" "Manual time input:\0\0" "manual off|on|reset\0Disable/enable/reset settime command\0" @@ -1230,7 +1199,7 @@ "\0(e.g. Sep 25, 2015 16:30:05 or 16:30:05)\0" "\0\0NTP access:\0\0" "accheck
\0Check whether address is allowed\0" - "clients\0Report on clients that have accessed the server\0" + "clients [-p ] [-k] [-r]\0Report on clients that accessed the server\0" "serverstats\0Display statistics of the server\0" "allow []\0Allow access to subnet as a default\0" "allow all []\0Allow access to subnet and all children\0" @@ -1255,8 +1224,9 @@ "\0\0" "Other daemon commands:\0\0" "cyclelogs\0Close and re-open log files\0" - "dump\0Dump all measurements to save files\0" - "rekey\0Re-read keys from key file\0" + "dump\0Dump measurements and NTS keys/cookies\0" + "rekey\0Re-read keys\0" + "reset sources\0Drop all measurements\0" "shutdown\0Stop daemon\0" "\0\0" "Client commands:\0\0" @@ -1278,7 +1248,7 @@ } /* ================================================== */ -/* Tab-completion when editline/readline is available */ +/* Tab-completion when editline is available */ #ifdef FEAT_READLINE @@ -1286,8 +1256,12 @@ TAB_COMPLETE_BASE_CMDS, TAB_COMPLETE_ADD_OPTS, TAB_COMPLETE_MANUAL_OPTS, + TAB_COMPLETE_RELOAD_OPTS, + TAB_COMPLETE_RESET_OPTS, TAB_COMPLETE_SOURCES_OPTS, TAB_COMPLETE_SOURCESTATS_OPTS, + TAB_COMPLETE_AUTHDATA_OPTS, + TAB_COMPLETE_SELECTDATA_OPTS, TAB_COMPLETE_MAX_INDEX }; @@ -1298,28 +1272,33 @@ { const char *name, **names[TAB_COMPLETE_MAX_INDEX]; const char *base_commands[] = { - "accheck", "activity", "add", "allow", "burst", + "accheck", "activity", "add", "allow", "authdata", "burst", "clients", "cmdaccheck", "cmdallow", "cmddeny", "cyclelogs", "delete", "deny", "dns", "dump", "exit", "help", "keygen", "local", "makestep", "manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll", "maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline", - "polltarget", "quit", "refresh", "rekey", "reselect", "reselectdist", - "retries", "rtcdata", "serverstats", "settime", "shutdown", "smoothing", - "smoothtime", "sources", "sourcestats", + "polltarget", "quit", "refresh", "rekey", "reload", "reselect", "reselectdist", "reset", + "retries", "rtcdata", "selectdata", "serverstats", "settime", "shutdown", "smoothing", + "smoothtime", "sourcename", "sources", "sourcestats", "timeout", "tracking", "trimrtc", "waitsync", "writertc", NULL }; - const char *add_options[] = { "peer", "server", NULL }; + const char *add_options[] = { "peer", "pool", "server", NULL }; const char *manual_options[] = { "on", "off", "delete", "list", "reset", NULL }; - const char *sources_options[] = { "-v", NULL }; - const char *sourcestats_options[] = { "-v", NULL }; + const char *reset_options[] = { "sources", NULL }; + const char *reload_options[] = { "sources", NULL }; + const char *common_source_options[] = { "-a", "-v", NULL }; static int list_index, len; names[TAB_COMPLETE_BASE_CMDS] = base_commands; names[TAB_COMPLETE_ADD_OPTS] = add_options; names[TAB_COMPLETE_MANUAL_OPTS] = manual_options; - names[TAB_COMPLETE_SOURCES_OPTS] = sources_options; - names[TAB_COMPLETE_SOURCESTATS_OPTS] = sourcestats_options; + names[TAB_COMPLETE_RELOAD_OPTS] = reload_options; + names[TAB_COMPLETE_RESET_OPTS] = reset_options; + names[TAB_COMPLETE_AUTHDATA_OPTS] = common_source_options; + names[TAB_COMPLETE_SELECTDATA_OPTS] = common_source_options; + names[TAB_COMPLETE_SOURCES_OPTS] = common_source_options; + names[TAB_COMPLETE_SOURCESTATS_OPTS] = common_source_options; if (!state) { list_index = 0; @@ -1347,8 +1326,16 @@ if (!strcmp(first, "add ")) { tab_complete_index = TAB_COMPLETE_ADD_OPTS; + } else if (!strcmp(first, "authdata ")) { + tab_complete_index = TAB_COMPLETE_AUTHDATA_OPTS; } else if (!strcmp(first, "manual ")) { tab_complete_index = TAB_COMPLETE_MANUAL_OPTS; + } else if (!strcmp(first, "reload ")) { + tab_complete_index = TAB_COMPLETE_RELOAD_OPTS; + } else if (!strcmp(first, "reset ")) { + tab_complete_index = TAB_COMPLETE_RESET_OPTS; + } else if (!strcmp(first, "selectdata ")) { + tab_complete_index = TAB_COMPLETE_SELECTDATA_OPTS; } else if (!strcmp(first, "sources ")) { tab_complete_index = TAB_COMPLETE_SOURCES_OPTS; } else if (!strcmp(first, "sourcestats ")) { @@ -1426,12 +1413,8 @@ return 0; } - if (send(sock_fd, (void *)request, command_length, 0) < 0) { - DEBUG_LOG("Could not send %d bytes : %s", command_length, strerror(errno)); + if (SCK_Send(sock_fd, (void *)request, command_length, 0) < 0) return 0; - } - - DEBUG_LOG("Sent %d bytes", command_length); } UTI_TimevalToTimespec(&tv, &ts_now); @@ -1467,16 +1450,11 @@ /* Timeout must have elapsed, try a resend? */ new_attempt = 1; } else { - recv_status = recv(sock_fd, (void *)reply, sizeof(CMD_Reply), 0); + recv_status = SCK_Receive(sock_fd, reply, sizeof (*reply), 0); if (recv_status < 0) { - /* If we get connrefused here, it suggests the sendto is - going to a dead port */ - DEBUG_LOG("Could not receive : %s", strerror(errno)); new_attempt = 1; } else { - DEBUG_LOG("Received %d bytes", recv_status); - read_length = recv_status; /* Check if the header is valid */ @@ -1607,6 +1585,9 @@ case STT_INACTIVE: printf("519 Client logging is not active in the daemon"); break; + case STT_INVALIDNAME: + printf("521 Invalid name"); + break; default: printf("520 Got unexpected error from daemon"); } @@ -1887,19 +1868,19 @@ integer = va_arg(ap, int); switch (integer) { case LEAP_Normal: - string = "Normal"; + string = width != 1 ? "Normal" : "N"; break; case LEAP_InsertSecond: - string = "Insert second"; + string = width != 1 ? "Insert second" : "+"; break; case LEAP_DeleteSecond: - string = "Delete second"; + string = width != 1 ? "Delete second" : "-"; break; case LEAP_Unsynchronised: - string = "Not synchronised"; + string = width != 1 ? "Not synchronised" : "?"; break; default: - string = "Invalid"; + string = width != 1 ? "Invalid" : "?"; break; } printf("%s", string); @@ -2038,12 +2019,40 @@ /* ================================================== */ +static int +get_source_name(IPAddr *ip_addr, char *buf, int size) +{ + CMD_Request request; + CMD_Reply reply; + int i; + + request.command = htons(REQ_NTP_SOURCE_NAME); + UTI_IPHostToNetwork(ip_addr, &request.data.ntp_source_name.ip_addr); + if (!request_reply(&request, &reply, RPY_NTP_SOURCE_NAME, 0) || + reply.data.ntp_source_name.name[sizeof (reply.data.ntp_source_name.name) - 1] != '\0' || + snprintf(buf, size, "%s", (char *)reply.data.ntp_source_name.name) >= size) + return 0; + + /* Make sure the name is printable */ + for (i = 0; i < size && buf[i] != '\0'; i++) { + if (!isgraph((unsigned char)buf[i])) + return 0; + } + + return 1; +} + +/* ================================================== */ + static void format_name(char *buf, int size, int trunc_dns, int ref, uint32_t ref_id, - IPAddr *ip_addr) + int source, IPAddr *ip_addr) { if (ref) { snprintf(buf, size, "%s", UTI_RefidToString(ref_id)); + } else if (source && source_names) { + if (!get_source_name(ip_addr, buf, size)) + snprintf(buf, size, "?"); } else if (no_dns || csv_mode) { snprintf(buf, size, "%s", UTI_IPToString(ip_addr)); } else { @@ -2057,12 +2066,42 @@ /* ================================================== */ +static void +parse_sources_options(char *line, int *all, int *verbose) +{ + char *opt; + + *all = *verbose = 0; + + while (*line) { + opt = line; + line = CPS_SplitWord(line); + if (!strcmp(opt, "-a")) + *all = 1; + else if (!strcmp(opt, "-v")) + *verbose = !csv_mode; + } +} + +/* ================================================== */ + static int -check_for_verbose_flag(char *line) +process_cmd_sourcename(char *line) { - if (!csv_mode && !strcmp(line, "-v")) - return 1; - return 0; + IPAddr ip_addr; + char name[256]; + + if (!parse_source_address(line, &ip_addr)) { + LOG(LOGS_ERR, "Could not read address"); + return 0; + } + + if (!get_source_name(&ip_addr, name, sizeof (name))) + return 0; + + print_report("%s\n", name, REPORT_END); + + return 1; } /* ================================================== */ @@ -2074,24 +2113,22 @@ CMD_Reply reply; IPAddr ip_addr; uint32_t i, mode, n_sources; - char name[50], mode_ch, state_ch; - int verbose; + char name[256], mode_ch, state_ch; + int all, verbose; - /* Check whether to output verbose headers */ - verbose = check_for_verbose_flag(line); + parse_sources_options(line, &all, &verbose); request.command = htons(REQ_N_SOURCES); if (!request_reply(&request, &reply, RPY_N_SOURCES, 0)) return 0; n_sources = ntohl(reply.data.n_sources.n_sources); - print_info_field("210 Number of sources = %lu\n", (unsigned long)n_sources); if (verbose) { printf("\n"); printf(" .-- Source mode '^' = server, '=' = peer, '#' = local clock.\n"); - printf(" / .- Source state '*' = current synced, '+' = combined , '-' = not combined,\n"); - printf("| / '?' = unreachable, 'x' = time may be in error, '~' = time too variable.\n"); + printf(" / .- Source state '*' = current best, '+' = combined, '-' = not combined,\n"); + printf("| / 'x' = may be in error, '~' = too variable, '?' = unusable.\n"); printf("|| .- xxxx [ yyyy ] +/- zzzz\n"); printf("|| Reachability register (octal) -. | xxxx = adjusted offset,\n"); printf("|| Log2(Polling interval) --. | | yyyy = measured offset,\n"); @@ -2111,9 +2148,12 @@ mode = ntohs(reply.data.source_data.mode); UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &ip_addr); + if (!all && ip_addr.family == IPADDR_ID) + continue; + format_name(name, sizeof (name), 25, mode == RPY_SD_MD_REF && ip_addr.family == IPADDR_INET4, - ip_addr.addr.in4, &ip_addr); + ip_addr.addr.in4, 1, &ip_addr); switch (mode) { case RPY_SD_MD_CLIENT: @@ -2130,10 +2170,10 @@ } switch (ntohs(reply.data.source_data.state)) { - case RPY_SD_ST_SYNC: + case RPY_SD_ST_SELECTED: state_ch = '*'; break; - case RPY_SD_ST_UNREACH: + case RPY_SD_ST_NONSELECTABLE: state_ch = '?'; break; case RPY_SD_ST_FALSETICKER: @@ -2142,10 +2182,10 @@ case RPY_SD_ST_JITTERY: state_ch = '~'; break; - case RPY_SD_ST_CANDIDATE: + case RPY_SD_ST_UNSELECTED: state_ch = '+'; break; - case RPY_SD_ST_OUTLIER: + case RPY_SD_ST_SELECTABLE: state_ch = '-'; break; default: @@ -2180,18 +2220,17 @@ CMD_Request request; CMD_Reply reply; uint32_t i, n_sources; - int verbose = 0; - char name[50]; + int all, verbose; + char name[256]; IPAddr ip_addr; - verbose = check_for_verbose_flag(line); + parse_sources_options(line, &all, &verbose); request.command = htons(REQ_N_SOURCES); if (!request_reply(&request, &reply, RPY_N_SOURCES, 0)) return 0; n_sources = ntohl(reply.data.n_sources.n_sources); - print_info_field("210 Number of sources = %lu\n", (unsigned long)n_sources); if (verbose) { printf(" .- Number of sample points in measurement set.\n"); @@ -2216,8 +2255,11 @@ return 0; UTI_IPNetworkToHost(&reply.data.sourcestats.ip_addr, &ip_addr); + if (!all && ip_addr.family == IPADDR_ID) + continue; + format_name(name, sizeof (name), 25, ip_addr.family == IPADDR_UNSPEC, - ntohl(reply.data.sourcestats.ref_id), &ip_addr); + ntohl(reply.data.sourcestats.ref_id), 1, &ip_addr); print_report("%-25s %3U %3U %I %+P %P %+S %S\n", name, @@ -2243,7 +2285,7 @@ CMD_Reply reply; IPAddr ip_addr; uint32_t ref_id; - char name[50]; + char name[256]; struct timespec ref_time; request.command = htons(REQ_TRACKING); @@ -2254,7 +2296,7 @@ UTI_IPNetworkToHost(&reply.data.tracking.ip_addr, &ip_addr); format_name(name, sizeof (name), sizeof (name), - ip_addr.family == IPADDR_UNSPEC, ref_id, &ip_addr); + ip_addr.family == IPADDR_UNSPEC, ref_id, 1, &ip_addr); UTI_TimespecNetworkToHost(&reply.data.tracking.ref_time, &ref_time); @@ -2291,6 +2333,91 @@ /* ================================================== */ static int +process_cmd_authdata(char *line) +{ + CMD_Request request; + CMD_Reply reply; + IPAddr ip_addr; + uint32_t i, source_mode, n_sources; + int all, verbose; + const char *mode_str; + char name[256]; + + parse_sources_options(line, &all, &verbose); + + request.command = htons(REQ_N_SOURCES); + if (!request_reply(&request, &reply, RPY_N_SOURCES, 0)) + return 0; + + n_sources = ntohl(reply.data.n_sources.n_sources); + + if (verbose) { + printf( " .- Auth. mechanism (NTS, SK - symmetric key)\n"); + printf( " | Key length -. Cookie length (bytes) -.\n"); + printf( " | (bits) | Num. of cookies --. |\n"); + printf( " | | Key est. attempts | |\n"); + printf( " | | | | |\n"); + } + + print_header("Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen"); + + /* "NNNNNNNNNNNNNNNNNNNNNNNNNNN MMMM KKKKK AAAA LLLL LLLL AAAA NNNN CCCC LLLL" */ + + for (i = 0; i < n_sources; i++) { + request.command = htons(REQ_SOURCE_DATA); + request.data.source_data.index = htonl(i); + if (!request_reply(&request, &reply, RPY_SOURCE_DATA, 0)) + return 0; + + source_mode = ntohs(reply.data.source_data.mode); + if (source_mode != RPY_SD_MD_CLIENT && source_mode != RPY_SD_MD_PEER) + continue; + + UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &ip_addr); + if (!all && ip_addr.family == IPADDR_ID) + continue; + + request.command = htons(REQ_AUTH_DATA); + request.data.auth_data.ip_addr = reply.data.source_data.ip_addr; + if (!request_reply(&request, &reply, RPY_AUTH_DATA, 0)) + return 0; + + format_name(name, sizeof (name), 25, 0, 0, 1, &ip_addr); + + switch (ntohs(reply.data.auth_data.mode)) { + case RPY_AD_MD_NONE: + mode_str = "-"; + break; + case RPY_AD_MD_SYMMETRIC: + mode_str = "SK"; + break; + case RPY_AD_MD_NTS: + mode_str = "NTS"; + break; + default: + mode_str = "?"; + break; + } + + print_report("%-27s %4s %5U %4d %4d %I %4d %4d %4d %4d\n", + name, mode_str, + (unsigned long)ntohl(reply.data.auth_data.key_id), + ntohs(reply.data.auth_data.key_type), + ntohs(reply.data.auth_data.key_length), + (unsigned long)ntohl(reply.data.auth_data.last_ke_ago), + ntohs(reply.data.auth_data.ke_attempts), + ntohs(reply.data.auth_data.nak), + ntohs(reply.data.auth_data.cookies), + ntohs(reply.data.auth_data.cookie_length), + REPORT_END); + } + + return 1; +} + +/* ================================================== */ + +static int process_cmd_ntpdata(char *line) { CMD_Request request; @@ -2314,7 +2441,7 @@ for (i = 0; i < n_sources; i++) { if (specified_addr) { - if (DNS_Name2IPAddress(line, &remote_addr, 1) != DNS_Success) { + if (!parse_source_address(line, &remote_addr)) { LOG(LOGS_ERR, "Could not get address for hostname"); return 0; } @@ -2329,6 +2456,8 @@ continue; UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &remote_addr); + if (!UTI_IsIPReal(&remote_addr)) + continue; } request.command = htons(REQ_NTP_DATA); @@ -2405,25 +2534,107 @@ /* ================================================== */ static int +process_cmd_selectdata(char *line) +{ + CMD_Request request; + CMD_Reply reply; + uint32_t i, n_sources; + int all, verbose, conf_options, eff_options; + char name[256]; + IPAddr ip_addr; + + parse_sources_options(line, &all, &verbose); + + request.command = htons(REQ_N_SOURCES); + if (!request_reply(&request, &reply, RPY_N_SOURCES, 0)) + return 0; + + n_sources = ntohl(reply.data.n_sources.n_sources); + + if (verbose) { + printf( " .-- State: N - noselect, M - missing samples, d/D - large distance,\n"); + printf( " / ~ - jittery, w/W - waits for others, T - not trusted,\n"); + printf( "| x - falseticker, P - not preferred, U - waits for update,\n"); + printf( "| S - stale, O - orphan, + - combined, * - best.\n"); + printf( "| Effective options ------. (N - noselect, P - prefer\n"); + printf( "| Configured options -. \\ T - trust, R - require)\n"); + printf( "| Auth. enabled (Y/N) -. \\ \\ Offset interval --.\n"); + printf( "| | | | |\n"); + } + + print_header("S Name/IP Address Auth COpts EOpts Last Score Interval Leap"); + + /* "S NNNNNNNNNNNNNNNNNNNNNNNNN A OOOO- OOOO- LLLL SSSSS IIIIIII IIIIIII L" */ + + for (i = 0; i < n_sources; i++) { + request.command = htons(REQ_SELECT_DATA); + request.data.source_data.index = htonl(i); + if (!request_reply(&request, &reply, RPY_SELECT_DATA, 0)) + return 0; + + UTI_IPNetworkToHost(&reply.data.select_data.ip_addr, &ip_addr); + if (!all && ip_addr.family == IPADDR_ID) + continue; + + format_name(name, sizeof (name), 25, ip_addr.family == IPADDR_UNSPEC, + ntohl(reply.data.select_data.ref_id), 1, &ip_addr); + + conf_options = ntohs(reply.data.select_data.conf_options); + eff_options = ntohs(reply.data.select_data.eff_options); + + print_report("%c %-25s %c %c%c%c%c%c %c%c%c%c%c %I %5.1f %+S %+S %1L\n", + reply.data.select_data.state_char, + name, + reply.data.select_data.authentication ? 'Y' : 'N', + conf_options & RPY_SD_OPTION_NOSELECT ? 'N' : '-', + conf_options & RPY_SD_OPTION_PREFER ? 'P' : '-', + conf_options & RPY_SD_OPTION_TRUST ? 'T' : '-', + conf_options & RPY_SD_OPTION_REQUIRE ? 'R' : '-', + '-', + eff_options & RPY_SD_OPTION_NOSELECT ? 'N' : '-', + eff_options & RPY_SD_OPTION_PREFER ? 'P' : '-', + eff_options & RPY_SD_OPTION_TRUST ? 'T' : '-', + eff_options & RPY_SD_OPTION_REQUIRE ? 'R' : '-', + '-', + (unsigned long)ntohl(reply.data.select_data.last_sample_ago), + UTI_FloatNetworkToHost(reply.data.select_data.score), + UTI_FloatNetworkToHost(reply.data.select_data.lo_limit), + UTI_FloatNetworkToHost(reply.data.select_data.hi_limit), + reply.data.select_data.leap, + REPORT_END); + } + + return 1; +} + +/* ================================================== */ + +static int process_cmd_serverstats(char *line) { CMD_Request request; CMD_Reply reply; request.command = htons(REQ_SERVER_STATS); - if (!request_reply(&request, &reply, RPY_SERVER_STATS, 0)) + if (!request_reply(&request, &reply, RPY_SERVER_STATS2, 0)) return 0; print_report("NTP packets received : %U\n" "NTP packets dropped : %U\n" "Command packets received : %U\n" "Command packets dropped : %U\n" - "Client log records dropped : %U\n", + "Client log records dropped : %U\n" + "NTS-KE connections accepted: %U\n" + "NTS-KE connections dropped : %U\n" + "Authenticated NTP packets : %U\n", (unsigned long)ntohl(reply.data.server_stats.ntp_hits), (unsigned long)ntohl(reply.data.server_stats.ntp_drops), (unsigned long)ntohl(reply.data.server_stats.cmd_hits), (unsigned long)ntohl(reply.data.server_stats.cmd_drops), (unsigned long)ntohl(reply.data.server_stats.log_drops), + (unsigned long)ntohl(reply.data.server_stats.nke_hits), + (unsigned long)ntohl(reply.data.server_stats.nke_drops), + (unsigned long)ntohl(reply.data.server_stats.ntp_auth_hits), REPORT_END); return 1; @@ -2521,20 +2732,46 @@ CMD_Request request; CMD_Reply reply; IPAddr ip; - uint32_t i, n_clients, next_index, n_indices; + uint32_t i, n_clients, next_index, n_indices, min_hits, reset; RPY_ClientAccesses_Client *client; - char name[50]; + char header[80], name[50], *opt, *arg; + int nke; next_index = 0; + min_hits = 0; + reset = 0; + nke = 0; + + while (*line) { + opt = line; + line = CPS_SplitWord(line); + if (strcmp(opt, "-k") == 0) { + nke = 1; + } else if (strcmp(opt, "-p") == 0) { + arg = line; + line = CPS_SplitWord(line); + if (sscanf(arg, "%"SCNu32, &min_hits) != 1) { + LOG(LOGS_ERR, "Invalid syntax for clients command"); + return 0; + } + } else if (strcmp(opt, "-r") == 0) { + reset = 1; + } + } - print_header("Hostname NTP Drop Int IntL Last Cmd Drop Int Last"); + snprintf(header, sizeof (header), + "Hostname NTP Drop Int IntL Last %6s Drop Int Last", + nke ? "NTS-KE" : "Cmd"); + print_header(header); while (1) { - request.command = htons(REQ_CLIENT_ACCESSES_BY_INDEX2); + request.command = htons(REQ_CLIENT_ACCESSES_BY_INDEX3); request.data.client_accesses_by_index.first_index = htonl(next_index); request.data.client_accesses_by_index.n_clients = htonl(MAX_CLIENT_ACCESSES); + request.data.client_accesses_by_index.min_hits = htonl(min_hits); + request.data.client_accesses_by_index.reset = htonl(reset); - if (!request_reply(&request, &reply, RPY_CLIENT_ACCESSES_BY_INDEX2, 0)) + if (!request_reply(&request, &reply, RPY_CLIENT_ACCESSES_BY_INDEX3, 0)) return 0; n_clients = ntohl(reply.data.client_accesses_by_index.n_clients); @@ -2550,7 +2787,7 @@ if (ip.family == IPADDR_UNSPEC) continue; - format_name(name, sizeof (name), 25, 0, 0, &ip); + format_name(name, sizeof (name), 25, 0, 0, 0, &ip); print_report("%-25s %6U %5U %C %C %I %6U %5U %C %I\n", name, @@ -2559,10 +2796,11 @@ client->ntp_interval, client->ntp_timeout_interval, (unsigned long)ntohl(client->last_ntp_hit_ago), - (unsigned long)ntohl(client->cmd_hits), - (unsigned long)ntohl(client->cmd_drops), - client->cmd_interval, - (unsigned long)ntohl(client->last_cmd_hit_ago), + (unsigned long)ntohl(nke ? client->nke_hits : client->cmd_hits), + (unsigned long)ntohl(nke ? client->nke_drops : client->cmd_drops), + nke ? client->nke_interval : client->cmd_interval, + (unsigned long)ntohl(nke ? client->last_nke_hit_ago : + client->last_cmd_hit_ago), REPORT_END); } @@ -2767,6 +3005,36 @@ /* ================================================== */ static int +process_cmd_reload(CMD_Request *msg, char *line) +{ + if (!strcmp(line, "sources")) { + msg->command = htons(REQ_RELOAD_SOURCES); + } else { + LOG(LOGS_ERR, "Invalid syntax for reload command"); + return 0; + } + + return 1; +} + +/* ================================================== */ + +static int +process_cmd_reset(CMD_Request *msg, char *line) +{ + if (!strcmp(line, "sources")) { + msg->command = htons(REQ_RESET_SOURCES); + } else { + LOG(LOGS_ERR, "Invalid syntax for reset command"); + return 0; + } + + return 1; +} + +/* ================================================== */ + +static int process_cmd_waitsync(char *line) { CMD_Request request; @@ -2881,28 +3149,48 @@ static int process_cmd_keygen(char *line) { - char hash_name[17]; + unsigned int i, args, cmac_length, length, id = 1, bits = 160; unsigned char key[512]; - unsigned int i, length, id = 1, bits = 160; + const char *type; + char *words[3]; #ifdef FEAT_SECHASH - snprintf(hash_name, sizeof (hash_name), "SHA1"); + type = "SHA1"; #else - snprintf(hash_name, sizeof (hash_name), "MD5"); + type = "MD5"; #endif - if (sscanf(line, "%u %16s %u", &id, hash_name, &bits)) - ; + args = UTI_SplitString(line, words, 3); + if (args >= 2) + type = words[1]; - length = CLAMP(10, (bits + 7) / 8, sizeof (key)); - if (HSH_GetHashId(hash_name) < 0) { - LOG(LOGS_ERR, "Unknown hash function %s", hash_name); + if (args > 3 || + (args >= 1 && sscanf(words[0], "%u", &id) != 1) || + (args >= 3 && sscanf(words[2], "%u", &bits) != 1)) { + LOG(LOGS_ERR, "Invalid syntax for keygen command"); return 0; } +#ifdef HAVE_CMAC + cmac_length = CMC_GetKeyLength(UTI_CmacNameToAlgorithm(type)); +#else + cmac_length = 0; +#endif + + if (HSH_GetHashId(UTI_HashNameToAlgorithm(type)) >= 0) { + length = (bits + 7) / 8; + } else if (cmac_length > 0) { + length = cmac_length; + } else { + LOG(LOGS_ERR, "Unknown hash function or cipher %s", type); + return 0; + } + + length = CLAMP(10, length, sizeof (key)); + UTI_GetRandomBytesUrandom(key, length); - printf("%u %s HEX:", id, hash_name); + printf("%u %s HEX:", id, type); for (i = 0; i < length; i++) printf("%02hhX", key[i]); printf("\n"); @@ -2941,16 +3229,17 @@ } else if (!strcmp(command, "activity")) { do_normal_submit = 0; ret = process_cmd_activity(line); - } else if (!strcmp(command, "add") && !strncmp(line, "peer", 4)) { - do_normal_submit = process_cmd_add_peer(&tx_message, CPS_SplitWord(line)); - } else if (!strcmp(command, "add") && !strncmp(line, "server", 6)) { - do_normal_submit = process_cmd_add_server(&tx_message, CPS_SplitWord(line)); + } else if (!strcmp(command, "add")) { + do_normal_submit = process_cmd_add_source(&tx_message, line); } else if (!strcmp(command, "allow")) { if (!strncmp(line, "all", 3)) { do_normal_submit = process_cmd_allowall(&tx_message, CPS_SplitWord(line)); } else { do_normal_submit = process_cmd_allow(&tx_message, line); } + } else if (!strcmp(command, "authdata")) { + do_normal_submit = 0; + ret = process_cmd_authdata(line); } else if (!strcmp(command, "burst")) { do_normal_submit = process_cmd_burst(&tx_message, line); } else if (!strcmp(command, "clients")) { @@ -2982,12 +3271,12 @@ do_normal_submit = process_cmd_deny(&tx_message, line); } } else if (!strcmp(command, "dfreq")) { - process_cmd_dfreq(&tx_message, line); + do_normal_submit = process_cmd_dfreq(&tx_message, line); } else if (!strcmp(command, "dns")) { ret = process_cmd_dns(line); do_normal_submit = 0; } else if (!strcmp(command, "doffset")) { - process_cmd_doffset(&tx_message, line); + do_normal_submit = process_cmd_doffset(&tx_message, line); } else if (!strcmp(command, "dump")) { process_cmd_dump(&tx_message, line); } else if (!strcmp(command, "exit")) { @@ -3047,16 +3336,23 @@ process_cmd_refresh(&tx_message, line); } else if (!strcmp(command, "rekey")) { process_cmd_rekey(&tx_message, line); + } else if (!strcmp(command, "reload")) { + do_normal_submit = process_cmd_reload(&tx_message, line); } else if (!strcmp(command, "reselect")) { process_cmd_reselect(&tx_message, line); } else if (!strcmp(command, "reselectdist")) { do_normal_submit = process_cmd_reselectdist(&tx_message, line); + } else if (!strcmp(command, "reset")) { + do_normal_submit = process_cmd_reset(&tx_message, line); } else if (!strcmp(command, "retries")) { ret = process_cmd_retries(line); do_normal_submit = 0; } else if (!strcmp(command, "rtcdata")) { do_normal_submit = 0; ret = process_cmd_rtcreport(line); + } else if (!strcmp(command, "selectdata")) { + do_normal_submit = 0; + ret = process_cmd_selectdata(line); } else if (!strcmp(command, "serverstats")) { do_normal_submit = 0; ret = process_cmd_serverstats(line); @@ -3070,6 +3366,9 @@ ret = process_cmd_smoothing(line); } else if (!strcmp(command, "smoothtime")) { do_normal_submit = process_cmd_smoothtime(&tx_message, line); + } else if (!strcmp(command, "sourcename")) { + do_normal_submit = 0; + ret = process_cmd_sourcename(line); } else if (!strcmp(command, "sources")) { do_normal_submit = 0; ret = process_cmd_sources(line); @@ -3159,7 +3458,7 @@ display_gpl(void) { printf("chrony version %s\n" - "Copyright (C) 1997-2003, 2007, 2009-2019 Richard P. Curnow and others\n" + "Copyright (C) 1997-2003, 2007, 2009-2021 Richard P. Curnow and others\n" "chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n" "you are welcome to redistribute it under certain conditions. See the\n" "GNU General Public License version 2 for details.\n\n", @@ -3171,8 +3470,22 @@ static void print_help(const char *progname) { - printf("Usage: %s [-h HOST] [-p PORT] [-n] [-c] [-d] [-4|-6] [-m] [COMMAND]\n", - progname); + printf("Usage: %s [OPTION]... [COMMAND]...\n\n" + "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); } /* ================================================== */ @@ -3194,7 +3507,7 @@ int opt, ret = 1, multi = 0, family = IPADDR_UNSPEC; int port = DEFAULT_CANDM_PORT; - /* Parse (undocumented) long command-line options */ + /* Parse long command-line options */ for (optind = 1; optind < argc; optind++) { if (!strcmp("--help", argv[optind])) { print_help(progname); @@ -3208,7 +3521,7 @@ optind = 1; /* Parse short command-line options */ - while ((opt = getopt(argc, argv, "+46acdf:h:mnp:v")) != -1) { + while ((opt = getopt(argc, argv, "+46acdf:h:mnNp:v")) != -1) { switch (opt) { case '4': case '6': @@ -3222,7 +3535,9 @@ csv_mode = 1; break; case 'd': - log_debug_enabled = 1; +#if DEBUG > 0 + log_min_severity = LOGS_DEBUG; +#endif break; case 'h': hostnames = optarg; @@ -3233,6 +3548,9 @@ case 'n': no_dns = 1; break; + case 'N': + source_names = 1; + break; case 'p': port = atoi(optarg); break; @@ -3261,7 +3579,8 @@ UTI_SetQuitSignalsHandler(signal_handler, 0); - sockaddrs = get_sockaddrs(hostnames, port); + SCK_Initialise(IPADDR_UNSPEC); + server_addresses = get_addresses(hostnames, port); if (!open_io()) LOG_FATAL("Could not open connection to daemon"); @@ -3281,8 +3600,8 @@ } close_io(); - - ARR_DestroyInstance(sockaddrs); + free_addresses(server_addresses); + SCK_Finalise(); return !ret; } diff -Nru chrony-3.5/clientlog.c chrony-4.1/clientlog.c --- chrony-3.5/clientlog.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/clientlog.c 2021-05-12 11:06:15.000000000 +0000 @@ -44,20 +44,17 @@ #include "util.h" #include "logging.h" +#define MAX_SERVICES 3 + typedef struct { IPAddr ip_addr; - uint32_t last_ntp_hit; - uint32_t last_cmd_hit; - uint32_t ntp_hits; - uint32_t cmd_hits; - uint16_t ntp_drops; - uint16_t cmd_drops; - uint16_t ntp_tokens; - uint16_t cmd_tokens; - int8_t ntp_rate; - int8_t cmd_rate; + uint32_t last_hit[MAX_SERVICES]; + uint32_t hits[MAX_SERVICES]; + uint16_t drops[MAX_SERVICES]; + uint16_t tokens[MAX_SERVICES]; + int8_t rate[MAX_SERVICES]; int8_t ntp_timeout_rate; - uint8_t flags; + uint8_t drop_flags; NTP_int64 ntp_rx_ts; NTP_int64 ntp_tx_ts; } Record; @@ -104,15 +101,12 @@ #define MIN_LIMIT_BURST 1 #define MAX_LIMIT_BURST 255 -static uint16_t max_ntp_tokens; -static uint16_t max_cmd_tokens; -static uint16_t ntp_tokens_per_packet; -static uint16_t cmd_tokens_per_packet; +static uint16_t max_tokens[MAX_SERVICES]; +static uint16_t tokens_per_hit[MAX_SERVICES]; /* Reduction of token rates to avoid overflow of 16-bit counters. Negative shift is used for coarse limiting with intervals shorter than -TS_FRAC. */ -static int ntp_token_shift; -static int cmd_token_shift; +static int token_shift[MAX_SERVICES]; /* Rates at which responses are randomly allowed (in log2) when the buckets don't have enough tokens. This is necessary in order to @@ -122,23 +116,18 @@ #define MIN_LEAK_RATE 1 #define MAX_LEAK_RATE 4 -static int ntp_leak_rate; -static int cmd_leak_rate; - -/* Flag indicating whether the last response was dropped */ -#define FLAG_NTP_DROPPED 0x1 +static int leak_rate[MAX_SERVICES]; -/* NTP limit interval in log2 */ -static int ntp_limit_interval; +/* Limit intervals in log2 */ +static int limit_interval[MAX_SERVICES]; /* Flag indicating whether facility is turned on or not */ static int active; /* Global statistics */ -static uint32_t total_ntp_hits; -static uint32_t total_cmd_hits; -static uint32_t total_ntp_drops; -static uint32_t total_cmd_drops; +static uint32_t total_hits[MAX_SERVICES]; +static uint32_t total_drops[MAX_SERVICES]; +static uint32_t total_ntp_auth_hits; static uint32_t total_record_drops; #define NSEC_PER_SEC 1000000000U @@ -161,12 +150,28 @@ /* ================================================== */ +static int +compare_total_hits(Record *x, Record *y) +{ + uint32_t x_hits, y_hits; + int i; + + for (i = 0, x_hits = y_hits = 0; i < MAX_SERVICES; i++) { + x_hits += x->hits[i]; + y_hits += y->hits[i]; + } + + return x_hits > y_hits ? 1 : -1; +} + +/* ================================================== */ + static Record * get_record(IPAddr *ip) { - unsigned int first, i; - time_t last_hit, oldest_hit = 0; + uint32_t last_hit = 0, oldest_hit = 0; Record *record, *oldest_record; + unsigned int first, i, j; if (!active || (ip->family != IPADDR_INET4 && ip->family != IPADDR_INET6)) return NULL; @@ -184,12 +189,13 @@ if (record->ip_addr.family == IPADDR_UNSPEC) break; - last_hit = compare_ts(record->last_ntp_hit, record->last_cmd_hit) > 0 ? - record->last_ntp_hit : record->last_cmd_hit; + for (j = 0; j < MAX_SERVICES; j++) { + if (j == 0 || compare_ts(last_hit, record->last_hit[j]) < 0) + last_hit = record->last_hit[j]; + } if (!oldest_record || compare_ts(oldest_hit, last_hit) > 0 || - (oldest_hit == last_hit && record->ntp_hits + record->cmd_hits < - oldest_record->ntp_hits + oldest_record->cmd_hits)) { + (oldest_hit == last_hit && compare_total_hits(oldest_record, record) > 0)) { oldest_record = record; oldest_hit = last_hit; } @@ -211,14 +217,18 @@ } record->ip_addr = *ip; - record->last_ntp_hit = record->last_cmd_hit = INVALID_TS; - record->ntp_hits = record->cmd_hits = 0; - record->ntp_drops = record->cmd_drops = 0; - record->ntp_tokens = max_ntp_tokens; - record->cmd_tokens = max_cmd_tokens; - record->ntp_rate = record->cmd_rate = INVALID_RATE; + for (i = 0; i < MAX_SERVICES; i++) + record->last_hit[i] = INVALID_TS; + for (i = 0; i < MAX_SERVICES; i++) + record->hits[i] = 0; + for (i = 0; i < MAX_SERVICES; i++) + record->drops[i] = 0; + for (i = 0; i < MAX_SERVICES; i++) + record->tokens[i] = max_tokens[i]; + for (i = 0; i < MAX_SERVICES; i++) + record->rate[i] = INVALID_RATE; record->ntp_timeout_rate = INVALID_RATE; - record->flags = 0; + record->drop_flags = 0; UTI_ZeroNtp64(&record->ntp_rx_ts); UTI_ZeroNtp64(&record->ntp_tx_ts); @@ -306,31 +316,43 @@ void CLG_Initialise(void) { - int interval, burst, leak_rate; + int i, interval, burst, lrate; + + for (i = 0; i < MAX_SERVICES; i++) { + max_tokens[i] = 0; + tokens_per_hit[i] = 0; + token_shift[i] = 0; + leak_rate[i] = 0; + limit_interval[i] = MIN_LIMIT_INTERVAL; + + switch (i) { + case CLG_NTP: + if (!CNF_GetNTPRateLimit(&interval, &burst, &lrate)) + continue; + break; + case CLG_NTSKE: + if (!CNF_GetNtsRateLimit(&interval, &burst, &lrate)) + continue; + break; + case CLG_CMDMON: + if (!CNF_GetCommandRateLimit(&interval, &burst, &lrate)) + continue; + break; + default: + assert(0); + } - max_ntp_tokens = max_cmd_tokens = 0; - ntp_tokens_per_packet = cmd_tokens_per_packet = 0; - ntp_token_shift = cmd_token_shift = 0; - ntp_leak_rate = cmd_leak_rate = 0; - ntp_limit_interval = MIN_LIMIT_INTERVAL; - - if (CNF_GetNTPRateLimit(&interval, &burst, &leak_rate)) { - set_bucket_params(interval, burst, &max_ntp_tokens, &ntp_tokens_per_packet, - &ntp_token_shift); - ntp_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE); - ntp_limit_interval = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL); - } - - if (CNF_GetCommandRateLimit(&interval, &burst, &leak_rate)) { - set_bucket_params(interval, burst, &max_cmd_tokens, &cmd_tokens_per_packet, - &cmd_token_shift); - cmd_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE); + set_bucket_params(interval, burst, &max_tokens[i], &tokens_per_hit[i], &token_shift[i]); + leak_rate[i] = CLAMP(MIN_LEAK_RATE, lrate, MAX_LEAK_RATE); + limit_interval[i] = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL); } active = !CNF_GetNoClientLog(); if (!active) { - if (ntp_leak_rate || cmd_leak_rate) - LOG_FATAL("ratelimit cannot be used with noclientlog"); + for (i = 0; i < MAX_SERVICES; i++) { + if (leak_rate[i] != 0) + LOG_FATAL("Rate limiting cannot be enabled with noclientlog"); + } return; } @@ -339,6 +361,7 @@ table where two copies exist at the same time. */ max_slots = CNF_GetClientLogLimit() / (sizeof (Record) * SLOT_SIZE * 3 / 2); max_slots = CLAMP(MIN_SLOTS, max_slots, MAX_SLOTS); + DEBUG_LOG("Max records %u", 1U << ((int)round(log(max_slots) / log(2)) + SLOT_BITS)); slots = 0; records = NULL; @@ -380,30 +403,33 @@ /* ================================================== */ static void -update_record(struct timespec *now, uint32_t *last_hit, uint32_t *hits, - uint16_t *tokens, uint32_t max_tokens, int token_shift, int8_t *rate) +update_record(CLG_Service service, Record *record, struct timespec *now) { - uint32_t interval, now_ts, prev_hit, new_tokens; - int interval2; + uint32_t interval, now_ts, prev_hit, tokens; + int interval2, tshift, mtokens; + int8_t *rate; now_ts = get_ts_from_timespec(now); - prev_hit = *last_hit; - *last_hit = now_ts; - (*hits)++; + prev_hit = record->last_hit[service]; + record->last_hit[service] = now_ts; + record->hits[service]++; interval = now_ts - prev_hit; if (prev_hit == INVALID_TS || (int32_t)interval < 0) return; - if (token_shift >= 0) - new_tokens = (now_ts >> token_shift) - (prev_hit >> token_shift); - else if (now_ts - prev_hit > max_tokens) - new_tokens = max_tokens; + tshift = token_shift[service]; + mtokens = max_tokens[service]; + + if (tshift >= 0) + tokens = (now_ts >> tshift) - (prev_hit >> tshift); + else if (now_ts - prev_hit > mtokens) + tokens = mtokens; else - new_tokens = (now_ts - prev_hit) << -token_shift; - *tokens = MIN(*tokens + new_tokens, max_tokens); + tokens = (now_ts - prev_hit) << -tshift; + record->tokens[service] = MIN(record->tokens[service] + tokens, mtokens); /* Convert the interval to scaled and rounded log2 */ if (interval) { @@ -418,6 +444,11 @@ interval2 = -RATE_SCALE * (TS_FRAC + 1); } + /* For the NTP service, update one of the two rates depending on whether + the previous request of the client had a reply or it timed out */ + rate = service == CLG_NTP && record->drop_flags & (1U << service) ? + &record->ntp_timeout_rate : &record->rate[service]; + /* Update the rate in a rough approximation of exponential moving average */ if (*rate == INVALID_RATE) { *rate = -interval2; @@ -457,50 +488,33 @@ /* ================================================== */ -int -CLG_LogNTPAccess(IPAddr *client, struct timespec *now) +static void +check_service_number(CLG_Service service) { - Record *record; - - total_ntp_hits++; - - record = get_record(client); - if (record == NULL) - return -1; - - /* Update one of the two rates depending on whether the previous request - of the client had a reply or it timed out */ - update_record(now, &record->last_ntp_hit, &record->ntp_hits, - &record->ntp_tokens, max_ntp_tokens, ntp_token_shift, - record->flags & FLAG_NTP_DROPPED ? - &record->ntp_timeout_rate : &record->ntp_rate); - - DEBUG_LOG("NTP hits %"PRIu32" rate %d trate %d tokens %d", - record->ntp_hits, record->ntp_rate, record->ntp_timeout_rate, - record->ntp_tokens); - - return get_index(record); + assert(service >= 0 && service <= MAX_SERVICES); } /* ================================================== */ int -CLG_LogCommandAccess(IPAddr *client, struct timespec *now) +CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now) { Record *record; - total_cmd_hits++; + check_service_number(service); + + total_hits[service]++; record = get_record(client); if (record == NULL) return -1; - update_record(now, &record->last_cmd_hit, &record->cmd_hits, - &record->cmd_tokens, max_cmd_tokens, cmd_token_shift, - &record->cmd_rate); + update_record(service, record, now); - DEBUG_LOG("Cmd hits %"PRIu32" rate %d tokens %d", - record->cmd_hits, record->cmd_rate, record->cmd_tokens); + DEBUG_LOG("service %d hits %"PRIu32" rate %d trate %d tokens %d", + (int)service, record->hits[service], record->rate[service], + service == CLG_NTP ? record->ntp_timeout_rate : INVALID_RATE, + record->tokens[service]); return get_index(record); } @@ -530,71 +544,53 @@ /* ================================================== */ int -CLG_LimitNTPResponseRate(int index) +CLG_LimitServiceRate(CLG_Service service, int index) { Record *record; int drop; - if (!ntp_tokens_per_packet) + check_service_number(service); + + if (tokens_per_hit[service] == 0) return 0; record = ARR_GetElement(records, index); - record->flags &= ~FLAG_NTP_DROPPED; + record->drop_flags &= ~(1U << service); - if (record->ntp_tokens >= ntp_tokens_per_packet) { - record->ntp_tokens -= ntp_tokens_per_packet; + if (record->tokens[service] >= tokens_per_hit[service]) { + record->tokens[service] -= tokens_per_hit[service]; return 0; } - drop = limit_response_random(ntp_leak_rate); + drop = limit_response_random(leak_rate[service]); - /* Poorly implemented clients may send new requests at even a higher rate + /* Poorly implemented NTP clients can send requests at a higher rate when they are not getting replies. If the request rate seems to be more than twice as much as when replies are sent, give up on rate limiting to reduce the amount of traffic. Invert the sense of the leak to respond to most of the requests, but still keep the estimated rate updated. */ - if (record->ntp_timeout_rate != INVALID_RATE && - record->ntp_timeout_rate > record->ntp_rate + RATE_SCALE) + if (service == CLG_NTP && record->ntp_timeout_rate != INVALID_RATE && + record->ntp_timeout_rate > record->rate[service] + RATE_SCALE) drop = !drop; if (!drop) { - record->ntp_tokens = 0; + record->tokens[service] = 0; return 0; } - record->flags |= FLAG_NTP_DROPPED; - record->ntp_drops++; - total_ntp_drops++; + record->drop_flags |= 1U << service; + record->drops[service]++; + total_drops[service]++; return 1; } /* ================================================== */ -int -CLG_LimitCommandResponseRate(int index) +void +CLG_LogAuthNtpRequest(void) { - Record *record; - - if (!cmd_tokens_per_packet) - return 0; - - record = ARR_GetElement(records, index); - - if (record->cmd_tokens >= cmd_tokens_per_packet) { - record->cmd_tokens -= cmd_tokens_per_packet; - return 0; - } - - if (!limit_response_random(cmd_leak_rate)) { - record->cmd_tokens = 0; - return 0; - } - - record->cmd_drops++; - total_cmd_drops++; - - return 1; + total_ntp_auth_hits++; } /* ================================================== */ @@ -614,7 +610,7 @@ int CLG_GetNtpMinPoll(void) { - return ntp_limit_interval; + return limit_interval[CLG_NTP]; } /* ================================================== */ @@ -653,10 +649,12 @@ /* ================================================== */ int -CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timespec *now) +CLG_GetClientAccessReportByIndex(int index, int reset, uint32_t min_hits, + RPT_ClientAccessByIndex_Report *report, struct timespec *now) { Record *record; uint32_t now_ts; + int i, r; if (!active || index < 0 || index >= ARR_GetSize(records)) return 0; @@ -666,20 +664,44 @@ if (record->ip_addr.family == IPADDR_UNSPEC) return 0; - now_ts = get_ts_from_timespec(now); + if (min_hits == 0) { + r = 1; + } else { + for (i = r = 0; i < MAX_SERVICES; i++) { + if (record->hits[i] >= min_hits) { + r = 1; + break; + } + } + } - report->ip_addr = record->ip_addr; - report->ntp_hits = record->ntp_hits; - report->cmd_hits = record->cmd_hits; - report->ntp_drops = record->ntp_drops; - report->cmd_drops = record->cmd_drops; - report->ntp_interval = get_interval(record->ntp_rate); - report->cmd_interval = get_interval(record->cmd_rate); - report->ntp_timeout_interval = get_interval(record->ntp_timeout_rate); - report->last_ntp_hit_ago = get_last_ago(now_ts, record->last_ntp_hit); - report->last_cmd_hit_ago = get_last_ago(now_ts, record->last_cmd_hit); + if (r) { + now_ts = get_ts_from_timespec(now); - return 1; + report->ip_addr = record->ip_addr; + report->ntp_hits = record->hits[CLG_NTP]; + report->nke_hits = record->hits[CLG_NTSKE]; + report->cmd_hits = record->hits[CLG_CMDMON]; + report->ntp_drops = record->drops[CLG_NTP]; + report->nke_drops = record->drops[CLG_NTSKE]; + report->cmd_drops = record->drops[CLG_CMDMON]; + report->ntp_interval = get_interval(record->rate[CLG_NTP]); + report->nke_interval = get_interval(record->rate[CLG_NTSKE]); + report->cmd_interval = get_interval(record->rate[CLG_CMDMON]); + report->ntp_timeout_interval = get_interval(record->ntp_timeout_rate); + report->last_ntp_hit_ago = get_last_ago(now_ts, record->last_hit[CLG_NTP]); + report->last_nke_hit_ago = get_last_ago(now_ts, record->last_hit[CLG_NTSKE]); + report->last_cmd_hit_ago = get_last_ago(now_ts, record->last_hit[CLG_CMDMON]); + } + + if (reset) { + for (i = 0; i < MAX_SERVICES; i++) { + record->hits[i] = 0; + record->drops[i] = 0; + } + } + + return r; } /* ================================================== */ @@ -687,9 +709,12 @@ void CLG_GetServerStatsReport(RPT_ServerStatsReport *report) { - report->ntp_hits = total_ntp_hits; - report->cmd_hits = total_cmd_hits; - report->ntp_drops = total_ntp_drops; - report->cmd_drops = total_cmd_drops; + report->ntp_hits = total_hits[CLG_NTP]; + report->nke_hits = total_hits[CLG_NTSKE]; + report->cmd_hits = total_hits[CLG_CMDMON]; + report->ntp_drops = total_drops[CLG_NTP]; + report->nke_drops = total_drops[CLG_NTSKE]; + report->cmd_drops = total_drops[CLG_CMDMON]; report->log_drops = total_record_drops; + report->ntp_auth_hits = total_ntp_auth_hits; } diff -Nru chrony-3.5/clientlog.h chrony-4.1/clientlog.h --- chrony-3.5/clientlog.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/clientlog.h 2021-05-12 11:06:15.000000000 +0000 @@ -31,20 +31,27 @@ #include "sysincl.h" #include "reports.h" +typedef enum { + CLG_NTP = 0, + CLG_NTSKE, + CLG_CMDMON, +} CLG_Service; + extern void CLG_Initialise(void); extern void CLG_Finalise(void); extern int CLG_GetClientIndex(IPAddr *client); -extern int CLG_LogNTPAccess(IPAddr *client, struct timespec *now); -extern int CLG_LogCommandAccess(IPAddr *client, struct timespec *now); -extern int CLG_LimitNTPResponseRate(int index); -extern int CLG_LimitCommandResponseRate(int index); +extern int CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now); +extern int CLG_LimitServiceRate(CLG_Service service, int index); +extern void CLG_LogAuthNtpRequest(void); extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts); extern int CLG_GetNtpMinPoll(void); /* And some reporting functions, for use by chronyc. */ extern int CLG_GetNumberOfIndices(void); -extern int CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timespec *now); +extern int CLG_GetClientAccessReportByIndex(int index, int reset, uint32_t min_hits, + RPT_ClientAccessByIndex_Report *report, + struct timespec *now); extern void CLG_GetServerStatsReport(RPT_ServerStatsReport *report); #endif /* GOT_CLIENTLOG_H */ diff -Nru chrony-3.5/cmac.h chrony-4.1/cmac.h --- chrony-3.5/cmac.h 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/cmac.h 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,48 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + Header file for CMAC. + + */ + +#ifndef GOT_CMAC_H +#define GOT_CMAC_H + +/* Avoid overlapping with the hash enumeration */ +typedef enum { + CMC_INVALID = 0, + CMC_AES128 = 13, + CMC_AES256 = 14, +} CMC_Algorithm; + +typedef struct CMC_Instance_Record *CMC_Instance; + +extern int CMC_GetKeyLength(CMC_Algorithm algorithm); +extern CMC_Instance CMC_CreateInstance(CMC_Algorithm algorithm, const unsigned char *key, + int length); +extern int CMC_Hash(CMC_Instance inst, const void *in, int in_len, + unsigned char *out, int out_len); +extern void CMC_DestroyInstance(CMC_Instance inst); + +#endif + diff -Nru chrony-3.5/cmac_nettle.c chrony-4.1/cmac_nettle.c --- chrony-3.5/cmac_nettle.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/cmac_nettle.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,117 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + Support for AES128 and AES256 CMAC in Nettle. + + */ + +#include "config.h" + +#include "sysincl.h" + +#include + +#include "cmac.h" +#include "memory.h" + +struct CMC_Instance_Record { + int key_length; + union { + struct cmac_aes128_ctx aes128; + struct cmac_aes256_ctx aes256; + } context; +}; + +/* ================================================== */ + +int +CMC_GetKeyLength(CMC_Algorithm algorithm) +{ + if (algorithm == CMC_AES128) + return AES128_KEY_SIZE; + else if (algorithm == CMC_AES256) + return AES256_KEY_SIZE; + return 0; +} + +/* ================================================== */ + +CMC_Instance +CMC_CreateInstance(CMC_Algorithm algorithm, const unsigned char *key, int length) +{ + CMC_Instance inst; + + if (length <= 0 || length != CMC_GetKeyLength(algorithm)) + return NULL; + + inst = MallocNew(struct CMC_Instance_Record); + inst->key_length = length; + + switch (length) { + case AES128_KEY_SIZE: + cmac_aes128_set_key(&inst->context.aes128, key); + break; + case AES256_KEY_SIZE: + cmac_aes256_set_key(&inst->context.aes256, key); + break; + default: + assert(0); + } + + return inst; +} + +/* ================================================== */ + +int +CMC_Hash(CMC_Instance inst, const void *in, int in_len, unsigned char *out, int out_len) +{ + if (in_len < 0 || out_len < 0) + return 0; + + if (out_len > CMAC128_DIGEST_SIZE) + out_len = CMAC128_DIGEST_SIZE; + + switch (inst->key_length) { + case AES128_KEY_SIZE: + cmac_aes128_update(&inst->context.aes128, in_len, in); + cmac_aes128_digest(&inst->context.aes128, out_len, out); + break; + case AES256_KEY_SIZE: + cmac_aes256_update(&inst->context.aes256, in_len, in); + cmac_aes256_digest(&inst->context.aes256, out_len, out); + break; + default: + assert(0); + } + + return out_len; +} + +/* ================================================== */ + +void +CMC_DestroyInstance(CMC_Instance inst) +{ + Free(inst); +} diff -Nru chrony-3.5/cmdmon.c chrony-4.1/cmdmon.c --- chrony-3.5/cmdmon.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/cmdmon.c 2021-05-12 11:06:15.000000000 +0000 @@ -3,7 +3,7 @@ ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 - * Copyright (C) Miroslav Lichvar 2009-2016, 2018 + * Copyright (C) Miroslav Lichvar 2009-2016, 2018-2021 * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -38,11 +38,13 @@ #include "ntp_sources.h" #include "ntp_core.h" #include "smooth.h" +#include "socket.h" #include "sources.h" #include "sourcestats.h" #include "reference.h" #include "manual.h" #include "memory.h" +#include "nts_ke_server.h" #include "local.h" #include "addrfilt.h" #include "conf.h" @@ -53,21 +55,15 @@ /* ================================================== */ -union sockaddr_all { - struct sockaddr_in in4; -#ifdef FEAT_IPV6 - struct sockaddr_in6 in6; -#endif - struct sockaddr_un un; - struct sockaddr sa; -}; +#define INVALID_SOCK_FD (-5) /* File descriptors for command and monitoring sockets */ static int sock_fdu; static int sock_fd4; -#ifdef FEAT_IPV6 static int sock_fd6; -#endif + +/* Flag indicating the IPv4 socket is bound to an address */ +static int bound_sock_fd4; /* Flag indicating whether this module has been initialised or not */ static int initialised = 0; @@ -140,6 +136,14 @@ PERMIT_AUTH, /* ADD_PEER3 */ PERMIT_AUTH, /* SHUTDOWN */ PERMIT_AUTH, /* ONOFFLINE */ + PERMIT_AUTH, /* ADD_SOURCE */ + PERMIT_OPEN, /* NTP_SOURCE_NAME */ + PERMIT_AUTH, /* RESET_SOURCES */ + PERMIT_AUTH, /* AUTH_DATA */ + PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX3 */ + PERMIT_AUTH, /* SELECT_DATA */ + PERMIT_AUTH, /* RELOAD_SOURCES */ + PERMIT_AUTH, /* DOFFSET2 */ }; /* ================================================== */ @@ -155,99 +159,48 @@ /* ================================================== */ static int -prepare_socket(int family, int port_number) +open_socket(int family) { - int sock_fd; - socklen_t my_addr_len; - union sockaddr_all my_addr; - IPAddr bind_address; - int on_off = 1; - - sock_fd = socket(family, SOCK_DGRAM, 0); - if (sock_fd < 0) { - LOG(LOGS_ERR, "Could not open %s command socket : %s", - UTI_SockaddrFamilyToString(family), strerror(errno)); - return -1; - } - - /* Close on exec */ - UTI_FdSetCloexec(sock_fd); - - if (family != AF_UNIX) { - /* Allow reuse of port number */ - if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on_off, sizeof(on_off)) < 0) { - LOG(LOGS_ERR, "Could not set reuseaddr socket options"); - /* Don't quit - we might survive anyway */ - } - -#ifdef IP_FREEBIND - /* Allow binding to address that doesn't exist yet */ - if (setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) { - LOG(LOGS_ERR, "Could not set free bind socket option"); - } -#endif + const char *local_path, *iface; + IPSockAddr local_addr; + int sock_fd, port; -#ifdef FEAT_IPV6 - if (family == AF_INET6) { -#ifdef IPV6_V6ONLY - /* Receive IPv6 packets only */ - if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) { - LOG(LOGS_ERR, "Could not request IPV6_V6ONLY socket option"); + switch (family) { + case IPADDR_INET4: + case IPADDR_INET6: + port = CNF_GetCommandPort(); + if (port == 0 || !SCK_IsIpFamilyEnabled(family)) + return INVALID_SOCK_FD; + + CNF_GetBindCommandAddress(family, &local_addr.ip_addr); + local_addr.port = port; + iface = CNF_GetBindCommandInterface(); + + sock_fd = SCK_OpenUdpSocket(NULL, &local_addr, iface, SCK_FLAG_RX_DEST_ADDR); + if (sock_fd < 0) { + LOG(LOGS_ERR, "Could not open command socket on %s", + UTI_IPSockAddrToString(&local_addr)); + return INVALID_SOCK_FD; } -#endif - } -#endif - } - memset(&my_addr, 0, sizeof (my_addr)); + if (family == IPADDR_INET4) + bound_sock_fd4 = local_addr.ip_addr.addr.in4 != INADDR_ANY; - switch (family) { - case AF_INET: - my_addr_len = sizeof (my_addr.in4); - my_addr.in4.sin_family = family; - my_addr.in4.sin_port = htons((unsigned short)port_number); - - CNF_GetBindCommandAddress(IPADDR_INET4, &bind_address); - - if (bind_address.family == IPADDR_INET4) - my_addr.in4.sin_addr.s_addr = htonl(bind_address.addr.in4); - else - my_addr.in4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - break; -#ifdef FEAT_IPV6 - case AF_INET6: - my_addr_len = sizeof (my_addr.in6); - my_addr.in6.sin6_family = family; - my_addr.in6.sin6_port = htons((unsigned short)port_number); - - CNF_GetBindCommandAddress(IPADDR_INET6, &bind_address); - - if (bind_address.family == IPADDR_INET6) - memcpy(my_addr.in6.sin6_addr.s6_addr, bind_address.addr.in6, - sizeof (my_addr.in6.sin6_addr.s6_addr)); - else - my_addr.in6.sin6_addr = in6addr_loopback; break; -#endif - case AF_UNIX: - my_addr_len = sizeof (my_addr.un); - my_addr.un.sun_family = family; - if (snprintf(my_addr.un.sun_path, sizeof (my_addr.un.sun_path), "%s", - CNF_GetBindCommandPath()) >= sizeof (my_addr.un.sun_path)) - LOG_FATAL("Unix socket path too long"); - unlink(my_addr.un.sun_path); + case IPADDR_UNSPEC: + local_path = CNF_GetBindCommandPath(); + + sock_fd = SCK_OpenUnixDatagramSocket(NULL, local_path, 0); + if (sock_fd < 0) { + LOG(LOGS_ERR, "Could not open command socket on %s", local_path); + return INVALID_SOCK_FD; + } + break; default: assert(0); } - if (bind(sock_fd, &my_addr.sa, my_addr_len) < 0) { - LOG(LOGS_ERR, "Could not bind %s command socket : %s", - UTI_SockaddrFamilyToString(family), strerror(errno)); - close(sock_fd); - return -1; - } - /* Register handler for read events on the socket */ SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_from_cmd_socket, NULL); @@ -290,39 +243,21 @@ /* ================================================== */ void -CAM_Initialise(int family) +CAM_Initialise(void) { - int port_number; - assert(!initialised); assert(sizeof (permissions) / sizeof (permissions[0]) == N_REQUEST_TYPES); do_size_checks(); initialised = 1; - sock_fdu = -1; - port_number = CNF_GetCommandPort(); - if (port_number && (family == IPADDR_UNSPEC || family == IPADDR_INET4)) - sock_fd4 = prepare_socket(AF_INET, port_number); - else - sock_fd4 = -1; -#ifdef FEAT_IPV6 - if (port_number && (family == IPADDR_UNSPEC || family == IPADDR_INET6)) - sock_fd6 = prepare_socket(AF_INET6, port_number); - else - sock_fd6 = -1; -#endif + bound_sock_fd4 = 0; - if (port_number && sock_fd4 < 0 -#ifdef FEAT_IPV6 - && sock_fd6 < 0 -#endif - ) { - LOG_FATAL("Could not open any command socket"); - } + sock_fdu = INVALID_SOCK_FD; + sock_fd4 = open_socket(IPADDR_INET4); + sock_fd6 = open_socket(IPADDR_INET6); access_auth_table = ADF_CreateTable(); - } /* ================================================== */ @@ -330,24 +265,24 @@ void CAM_Finalise(void) { - if (sock_fdu >= 0) { + if (sock_fdu != INVALID_SOCK_FD) { SCH_RemoveFileHandler(sock_fdu); - close(sock_fdu); - unlink(CNF_GetBindCommandPath()); + SCK_RemoveSocket(sock_fdu); + SCK_CloseSocket(sock_fdu); + sock_fdu = INVALID_SOCK_FD; } - sock_fdu = -1; - if (sock_fd4 >= 0) { + + if (sock_fd4 != INVALID_SOCK_FD) { SCH_RemoveFileHandler(sock_fd4); - close(sock_fd4); + SCK_CloseSocket(sock_fd4); + sock_fd4 = INVALID_SOCK_FD; } - sock_fd4 = -1; -#ifdef FEAT_IPV6 - if (sock_fd6 >= 0) { + + if (sock_fd6 != INVALID_SOCK_FD) { SCH_RemoveFileHandler(sock_fd6); - close(sock_fd6); + SCK_CloseSocket(sock_fd6); + sock_fd6 = INVALID_SOCK_FD; } - sock_fd6 = -1; -#endif ADF_DestroyTable(access_auth_table); @@ -361,51 +296,37 @@ { /* This is separated from CAM_Initialise() as it needs to be called when the process has already dropped the root privileges */ - if (CNF_GetBindCommandPath()[0]) - sock_fdu = prepare_socket(AF_UNIX, 0); + if (CNF_GetBindCommandPath()) + sock_fdu = open_socket(IPADDR_UNSPEC); } /* ================================================== */ static void -transmit_reply(CMD_Reply *msg, union sockaddr_all *where_to) +transmit_reply(int sock_fd, int request_length, SCK_Message *message) { - int status; - int tx_message_length; - int sock_fd; - socklen_t addrlen; - - switch (where_to->sa.sa_family) { - case AF_INET: - sock_fd = sock_fd4; - addrlen = sizeof (where_to->in4); - break; -#ifdef FEAT_IPV6 - case AF_INET6: - sock_fd = sock_fd6; - addrlen = sizeof (where_to->in6); - break; -#endif - case AF_UNIX: - sock_fd = sock_fdu; - addrlen = sizeof (where_to->un); - break; - default: - assert(0); - } + message->length = PKL_ReplyLength((CMD_Reply *)message->data); - tx_message_length = PKL_ReplyLength(msg); - status = sendto(sock_fd, (void *) msg, tx_message_length, 0, - &where_to->sa, addrlen); - - if (status < 0) { - DEBUG_LOG("Could not send to %s fd %d : %s", - UTI_SockaddrToString(&where_to->sa), sock_fd, strerror(errno)); + if (request_length < message->length) { + DEBUG_LOG("Response longer than request req_len=%d res_len=%d", + request_length, message->length); return; } - DEBUG_LOG("Sent %d bytes to %s fd %d", status, - UTI_SockaddrToString(&where_to->sa), sock_fd); + /* Don't require responses to non-link-local addresses to use the same + interface */ + if (message->addr_type == SCK_ADDR_IP && + !SCK_IsLinkLocalIPAddress(&message->remote_addr.ip.ip_addr)) + message->if_index = INVALID_IF_INDEX; + +#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR) + /* On FreeBSD a local IPv4 address cannot be specified on bound socket */ + if (message->local_addr.ip.family == IPADDR_INET4 && (sock_fd != sock_fd4 || bound_sock_fd4)) + message->local_addr.ip.family = IPADDR_UNSPEC; +#endif + + if (!SCK_SendMessage(sock_fd, message, 0)) + return; } /* ================================================== */ @@ -414,6 +335,8 @@ handle_dump(CMD_Request *rx_message, CMD_Reply *tx_message) { SRC_DumpSources(); + NSR_DumpAuthData(); + NKS_DumpKeys(); } /* ================================================== */ @@ -671,11 +594,8 @@ tx_message->data.source_data.stratum = htons(report.stratum); tx_message->data.source_data.poll = htons(report.poll); switch (report.state) { - case RPT_SYNC: - tx_message->data.source_data.state = htons(RPY_SD_ST_SYNC); - break; - case RPT_UNREACH: - tx_message->data.source_data.state = htons(RPY_SD_ST_UNREACH); + case RPT_NONSELECTABLE: + tx_message->data.source_data.state = htons(RPY_SD_ST_NONSELECTABLE); break; case RPT_FALSETICKER: tx_message->data.source_data.state = htons(RPY_SD_ST_FALSETICKER); @@ -683,11 +603,14 @@ case RPT_JITTERY: tx_message->data.source_data.state = htons(RPY_SD_ST_JITTERY); break; - case RPT_CANDIDATE: - tx_message->data.source_data.state = htons(RPY_SD_ST_CANDIDATE); + case RPT_SELECTABLE: + tx_message->data.source_data.state = htons(RPY_SD_ST_SELECTABLE); + break; + case RPT_UNSELECTED: + tx_message->data.source_data.state = htons(RPY_SD_ST_UNSELECTED); break; - case RPT_OUTLIER: - tx_message->data.source_data.state = htons(RPY_SD_ST_OUTLIER); + case RPT_SELECTED: + tx_message->data.source_data.state = htons(RPY_SD_ST_SELECTED); break; } switch (report.mode) { @@ -701,11 +624,7 @@ tx_message->data.source_data.mode = htons(RPY_SD_MD_REF); break; } - tx_message->data.source_data.flags = - htons((report.sel_options & SRC_SELECT_PREFER ? RPY_SD_FLAG_PREFER : 0) | - (report.sel_options & SRC_SELECT_NOSELECT ? RPY_SD_FLAG_NOSELECT : 0) | - (report.sel_options & SRC_SELECT_TRUST ? RPY_SD_FLAG_TRUST : 0) | - (report.sel_options & SRC_SELECT_REQUIRE ? RPY_SD_FLAG_REQUIRE : 0)); + tx_message->data.source_data.flags = htons(0); tx_message->data.source_data.reachability = htons(report.reachability); tx_message->data.source_data.since_sample = htonl(report.latest_meas_ago); tx_message->data.source_data.orig_latest_meas = UTI_FloatHostToNetwork(report.orig_latest_meas); @@ -722,6 +641,7 @@ handle_rekey(CMD_Request *rx_message, CMD_Reply *tx_message) { KEY_Reload(); + NKS_ReloadKeys(); } /* ================================================== */ @@ -783,14 +703,41 @@ /* ================================================== */ static void -handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_message) +handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message) { - NTP_Remote_Address rem_addr; + NTP_Source_Type type; SourceParameters params; NSR_Status status; + char *name; + int pool, port; - UTI_IPNetworkToHost(&rx_message->data.ntp_source.ip_addr, &rem_addr.ip_addr); - rem_addr.port = (unsigned short)(ntohl(rx_message->data.ntp_source.port)); + switch (ntohl(rx_message->data.ntp_source.type)) { + case REQ_ADDSRC_SERVER: + type = NTP_SERVER; + pool = 0; + break; + case REQ_ADDSRC_PEER: + type = NTP_PEER; + pool = 0; + break; + case REQ_ADDSRC_POOL: + type = NTP_SERVER; + pool = 1; + break; + default: + tx_message->status = htons(STT_INVALID); + return; + } + + name = (char *)rx_message->data.ntp_source.name; + + /* Make sure the name is terminated */ + if (name[sizeof (rx_message->data.ntp_source.name) - 1] != '\0') { + tx_message->status = htons(STT_INVALIDNAME); + return; + } + + port = ntohl(rx_message->data.ntp_source.port); params.minpoll = ntohl(rx_message->data.ntp_source.minpoll); params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll); params.presend_minpoll = ntohl(rx_message->data.ntp_source.presend_minpoll); @@ -802,6 +749,8 @@ params.max_samples = ntohl(rx_message->data.ntp_source.max_samples); 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); + params.cert_set = ntohl(rx_message->data.ntp_source.cert_set); params.max_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay); params.max_delay_ratio = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio); @@ -817,25 +766,32 @@ params.iburst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_IBURST ? 1 : 0; params.interleaved = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_INTERLEAVED ? 1 : 0; params.burst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_BURST ? 1 : 0; + params.nts = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NTS ? 1 : 0; + params.copy = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_COPY ? 1 : 0; params.sel_options = (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) | (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) | (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) | (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0); - status = NSR_AddSource(&rem_addr, type, ¶ms); + status = NSR_AddSourceByName(name, port, pool, type, ¶ms, NULL); switch (status) { case NSR_Success: break; + case NSR_UnresolvedName: + /* Try to resolve the name now */ + NSR_ResolveSources(); + break; case NSR_AlreadyInUse: tx_message->status = htons(STT_SOURCEALREADYKNOWN); break; case NSR_TooManySources: tx_message->status = htons(STT_TOOMANYSOURCES); break; - case NSR_InvalidAF: - tx_message->status = htons(STT_INVALIDAF); + case NSR_InvalidName: + tx_message->status = htons(STT_INVALIDNAME); break; + case NSR_InvalidAF: case NSR_NoSuchSource: assert(0); break; @@ -847,13 +803,12 @@ static void handle_del_source(CMD_Request *rx_message, CMD_Reply *tx_message) { - NTP_Remote_Address rem_addr; NSR_Status status; + IPAddr ip_addr; - UTI_IPNetworkToHost(&rx_message->data.del_source.ip_addr, &rem_addr.ip_addr); - rem_addr.port = 0; + UTI_IPNetworkToHost(&rx_message->data.del_source.ip_addr, &ip_addr); - status = NSR_RemoveSource(&rem_addr); + status = NSR_RemoveSource(&ip_addr); switch (status) { case NSR_Success: break; @@ -863,6 +818,8 @@ case NSR_TooManySources: case NSR_AlreadyInUse: case NSR_InvalidAF: + case NSR_InvalidName: + case NSR_UnresolvedName: assert(0); break; } @@ -901,13 +858,14 @@ static void handle_doffset(CMD_Request *rx_message, CMD_Reply *tx_message) { - long sec, usec; double doffset; - sec = (int32_t)ntohl(rx_message->data.doffset.sec); - usec = (int32_t)ntohl(rx_message->data.doffset.usec); - doffset = (double) sec + 1.0e-6 * (double) usec; - LOG(LOGS_INFO, "Accumulated delta offset of %.6f seconds", doffset); - LCL_AccumulateOffset(doffset, 0.0); + + doffset = UTI_FloatNetworkToHost(rx_message->data.doffset.doffset); + if (!LCL_AccumulateOffset(doffset, 0.0)) { + tx_message->status = htons(STT_FAILED); + } else { + LOG(LOGS_INFO, "Accumulated delta offset of %.6f seconds", doffset); + } } /* ================================================== */ @@ -1065,7 +1023,7 @@ RPT_ClientAccessByIndex_Report report; RPY_ClientAccesses_Client *client; int n_indices; - uint32_t i, j, req_first_index, req_n_clients; + uint32_t i, j, req_first_index, req_n_clients, req_min_hits, req_reset; struct timespec now; SCH_GetLastEventTime(&now, NULL, NULL); @@ -1074,6 +1032,8 @@ req_n_clients = ntohl(rx_message->data.client_accesses_by_index.n_clients); if (req_n_clients > MAX_CLIENT_ACCESSES) req_n_clients = MAX_CLIENT_ACCESSES; + req_min_hits = ntohl(rx_message->data.client_accesses_by_index.min_hits); + req_reset = ntohl(rx_message->data.client_accesses_by_index.reset); n_indices = CLG_GetNumberOfIndices(); if (n_indices < 0) { @@ -1081,24 +1041,28 @@ return; } - tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX2); + tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX3); tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices); for (i = req_first_index, j = 0; i < (uint32_t)n_indices && j < req_n_clients; i++) { - if (!CLG_GetClientAccessReportByIndex(i, &report, &now)) + if (!CLG_GetClientAccessReportByIndex(i, req_reset, req_min_hits, &report, &now)) continue; client = &tx_message->data.client_accesses_by_index.clients[j++]; UTI_IPHostToNetwork(&report.ip_addr, &client->ip); client->ntp_hits = htonl(report.ntp_hits); + client->nke_hits = htonl(report.nke_hits); client->cmd_hits = htonl(report.cmd_hits); client->ntp_drops = htonl(report.ntp_drops); + client->nke_drops = htonl(report.nke_drops); client->cmd_drops = htonl(report.cmd_drops); client->ntp_interval = report.ntp_interval; + client->nke_interval = report.nke_interval; client->cmd_interval = report.cmd_interval; client->ntp_timeout_interval = report.ntp_timeout_interval; client->last_ntp_hit_ago = htonl(report.last_ntp_hit_ago); + client->last_nke_hit_ago = htonl(report.last_nke_hit_ago); client->last_cmd_hit_ago = htonl(report.last_cmd_hit_ago); } @@ -1200,12 +1164,15 @@ RPT_ServerStatsReport report; CLG_GetServerStatsReport(&report); - tx_message->reply = htons(RPY_SERVER_STATS); + tx_message->reply = htons(RPY_SERVER_STATS2); tx_message->data.server_stats.ntp_hits = htonl(report.ntp_hits); + tx_message->data.server_stats.nke_hits = htonl(report.nke_hits); tx_message->data.server_stats.cmd_hits = htonl(report.cmd_hits); tx_message->data.server_stats.ntp_drops = htonl(report.ntp_drops); + tx_message->data.server_stats.nke_drops = htonl(report.nke_drops); tx_message->data.server_stats.cmd_drops = htonl(report.cmd_drops); tx_message->data.server_stats.log_drops = htonl(report.log_drops); + tx_message->data.server_stats.ntp_auth_hits = htonl(report.ntp_auth_hits); } /* ================================================== */ @@ -1262,97 +1229,216 @@ } /* ================================================== */ -/* Read a packet and process it */ static void -read_from_cmd_socket(int sock_fd, int event, void *anything) +handle_ntp_source_name(CMD_Request *rx_message, CMD_Reply *tx_message) { - CMD_Request rx_message; - CMD_Reply tx_message; - int status, read_length, expected_length, rx_message_length; - int localhost, allowed, log_index; - union sockaddr_all where_from; - socklen_t from_length; - IPAddr remote_ip; - unsigned short remote_port, rx_command; - struct timespec now, cooked_now; + IPAddr addr; + char *name; - rx_message_length = sizeof(rx_message); - from_length = sizeof(where_from); + UTI_IPNetworkToHost(&rx_message->data.ntp_source_name.ip_addr, &addr); + name = NSR_GetName(&addr); - status = recvfrom(sock_fd, (char *)&rx_message, rx_message_length, 0, - &where_from.sa, &from_length); + if (!name) { + tx_message->status = htons(STT_NOSUCHSOURCE); + return; + } + + tx_message->reply = htons(RPY_NTP_SOURCE_NAME); + + /* Avoid compiler warning */ + if (strlen(name) >= sizeof (tx_message->data.ntp_source_name.name)) + memcpy(tx_message->data.ntp_source_name.name, name, + sizeof (tx_message->data.ntp_source_name.name)); + else + strncpy((char *)tx_message->data.ntp_source_name.name, name, + sizeof (tx_message->data.ntp_source_name.name)); +} + +/* ================================================== */ + +static void +handle_reload_sources(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + CNF_ReloadSources(); +} + +/* ================================================== */ + +static void +handle_reset_sources(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + struct timespec cooked_now, now; + + SRC_ResetSources(); + SCH_GetLastEventTime(&cooked_now, NULL, &now); + LCL_NotifyExternalTimeStep(&now, &cooked_now, 0.0, 0.0); +} + +/* ================================================== */ + +static void +handle_auth_data(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + RPT_AuthReport report; + IPAddr ip_addr; - if (status < 0) { - LOG(LOGS_WARN, "Error [%s] reading from control socket %d", - strerror(errno), sock_fd); + UTI_IPNetworkToHost(&rx_message->data.auth_data.ip_addr, &ip_addr); + + if (!NSR_GetAuthReport(&ip_addr, &report)) { + tx_message->status = htons(STT_NOSUCHSOURCE); return; } - if (from_length > sizeof (where_from) || - from_length <= sizeof (where_from.sa.sa_family)) { - DEBUG_LOG("Read command packet without source address"); + tx_message->reply = htons(RPY_AUTH_DATA); + + switch (report.mode) { + case NTP_AUTH_NONE: + tx_message->data.auth_data.mode = htons(RPY_AD_MD_NONE); + break; + case NTP_AUTH_SYMMETRIC: + tx_message->data.auth_data.mode = htons(RPY_AD_MD_SYMMETRIC); + break; + case NTP_AUTH_NTS: + tx_message->data.auth_data.mode = htons(RPY_AD_MD_NTS); + break; + default: + break; + } + + tx_message->data.auth_data.key_type = htons(report.key_type); + tx_message->data.auth_data.key_id = htonl(report.key_id); + tx_message->data.auth_data.key_length = htons(report.key_length); + tx_message->data.auth_data.ke_attempts = htons(report.ke_attempts); + tx_message->data.auth_data.last_ke_ago = htonl(report.last_ke_ago); + tx_message->data.auth_data.cookies = htons(report.cookies); + tx_message->data.auth_data.cookie_length = htons(report.cookie_length); + tx_message->data.auth_data.nak = htons(report.nak); +} + +/* ================================================== */ + +static uint16_t +convert_select_options(int options) +{ + return (options & SRC_SELECT_PREFER ? RPY_SD_OPTION_PREFER : 0) | + (options & SRC_SELECT_NOSELECT ? RPY_SD_OPTION_NOSELECT : 0) | + (options & SRC_SELECT_TRUST ? RPY_SD_OPTION_TRUST : 0) | + (options & SRC_SELECT_REQUIRE ? RPY_SD_OPTION_REQUIRE : 0); +} + +/* ================================================== */ + +static void +handle_select_data(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + RPT_SelectReport report; + + if (!SRC_GetSelectReport(ntohl(rx_message->data.select_data.index), &report)) { + tx_message->status = htons(STT_NOSUCHSOURCE); return; } - read_length = status; + tx_message->reply = htons(RPY_SELECT_DATA); + + tx_message->data.select_data.ref_id = htonl(report.ref_id); + UTI_IPHostToNetwork(&report.ip_addr, &tx_message->data.select_data.ip_addr); + tx_message->data.select_data.state_char = report.state_char; + tx_message->data.select_data.authentication = report.authentication; + tx_message->data.select_data.leap = report.leap; + tx_message->data.select_data.conf_options = htons(convert_select_options(report.conf_options)); + tx_message->data.select_data.eff_options = htons(convert_select_options(report.eff_options)); + tx_message->data.select_data.last_sample_ago = htonl(report.last_sample_ago); + tx_message->data.select_data.score = UTI_FloatHostToNetwork(report.score); + tx_message->data.select_data.hi_limit = UTI_FloatHostToNetwork(report.hi_limit); + tx_message->data.select_data.lo_limit = UTI_FloatHostToNetwork(report.lo_limit); +} + +/* ================================================== */ +/* Read a packet and process it */ + +static void +read_from_cmd_socket(int sock_fd, int event, void *anything) +{ + SCK_Message *sck_message; + CMD_Request rx_message; + CMD_Reply tx_message; + IPAddr loopback_addr, remote_ip; + int read_length, expected_length; + int localhost, allowed, log_index; + uint16_t rx_command; + struct timespec now, cooked_now; + + sck_message = SCK_ReceiveMessage(sock_fd, 0); + if (!sck_message) + return; + + read_length = sck_message->length; /* Get current time cheaply */ SCH_GetLastEventTime(&cooked_now, NULL, &now); - UTI_SockaddrToIPAndPort(&where_from.sa, &remote_ip, &remote_port); + /* Check if it's from localhost (127.0.0.1, ::1, or Unix domain), + or an authorised address */ + switch (sck_message->addr_type) { + case SCK_ADDR_IP: + assert(sock_fd == sock_fd4 || sock_fd == sock_fd6); + remote_ip = sck_message->remote_addr.ip.ip_addr; + SCK_GetLoopbackIPAddress(remote_ip.family, &loopback_addr); + localhost = UTI_CompareIPs(&remote_ip, &loopback_addr, NULL) == 0; + + if (!localhost && !ADF_IsAllowed(access_auth_table, &remote_ip)) { + DEBUG_LOG("Unauthorised host %s", + UTI_IPSockAddrToString(&sck_message->remote_addr.ip)); + return; + } + + assert(remote_ip.family != IPADDR_UNSPEC); - /* Check if it's from localhost (127.0.0.1, ::1, or Unix domain) */ - switch (remote_ip.family) { - case IPADDR_INET4: - assert(sock_fd == sock_fd4); - localhost = remote_ip.addr.in4 == INADDR_LOOPBACK; - break; -#ifdef FEAT_IPV6 - case IPADDR_INET6: - assert(sock_fd == sock_fd6); - localhost = !memcmp(remote_ip.addr.in6, &in6addr_loopback, - sizeof (in6addr_loopback)); break; -#endif - case IPADDR_UNSPEC: - /* This should be the Unix domain socket */ - if (where_from.sa.sa_family != AF_UNIX) - return; + case SCK_ADDR_UNIX: assert(sock_fd == sock_fdu); + remote_ip.family = IPADDR_UNSPEC; localhost = 1; break; default: - assert(0); + DEBUG_LOG("Unexpected address type"); + return; } - DEBUG_LOG("Received %d bytes from %s fd %d", - status, UTI_SockaddrToString(&where_from.sa), sock_fd); - - if (!(localhost || ADF_IsAllowed(access_auth_table, &remote_ip))) { - /* The client is not allowed access, so don't waste any more time - on him. Note that localhost is always allowed access - regardless of the defined access rules - otherwise, we could - shut ourselves out completely! */ + if (read_length < offsetof(CMD_Request, data) || + read_length < offsetof(CMD_Reply, data) || + read_length > sizeof (CMD_Request)) { + /* We don't know how to process anything like this or an error reply + would be larger than the request */ + DEBUG_LOG("Unexpected length"); return; } - if (read_length < offsetof(CMD_Request, data) || - read_length < offsetof(CMD_Reply, data) || - rx_message.pkt_type != PKT_TYPE_CMD_REQUEST || + memcpy(&rx_message, sck_message->data, read_length); + + if (rx_message.pkt_type != PKT_TYPE_CMD_REQUEST || rx_message.res1 != 0 || rx_message.res2 != 0) { - - /* We don't know how to process anything like this or an error reply - would be larger than the request */ DEBUG_LOG("Command packet dropped"); return; } + log_index = CLG_LogServiceAccess(CLG_CMDMON, &remote_ip, &cooked_now); + + /* Don't reply to all requests from hosts other than localhost if the rate + is excessive */ + if (!localhost && log_index >= 0 && CLG_LimitServiceRate(CLG_CMDMON, log_index)) { + DEBUG_LOG("Command packet discarded to limit response rate"); + return; + } + expected_length = PKL_CommandLength(&rx_message); rx_command = ntohs(rx_message.command); memset(&tx_message, 0, sizeof (tx_message)); + sck_message->data = &tx_message; + sck_message->length = 0; tx_message.version = PROTO_VERSION_NUMBER; tx_message.pkt_type = PKT_TYPE_CMD_REPLY; @@ -1367,7 +1453,7 @@ if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) { tx_message.status = htons(STT_BADPKTVERSION); - transmit_reply(&tx_message, &where_from); + transmit_reply(sock_fd, read_length, sck_message); } return; } @@ -1377,7 +1463,7 @@ DEBUG_LOG("Command packet has invalid command %d", rx_command); tx_message.status = htons(STT_INVALID); - transmit_reply(&tx_message, &where_from); + transmit_reply(sock_fd, read_length, sck_message); return; } @@ -1386,21 +1472,12 @@ expected_length); tx_message.status = htons(STT_BADPKTLENGTH); - transmit_reply(&tx_message, &where_from); + transmit_reply(sock_fd, read_length, sck_message); return; } /* OK, we have a valid message. Now dispatch on message type and process it. */ - log_index = CLG_LogCommandAccess(&remote_ip, &cooked_now); - - /* Don't reply to all requests from hosts other than localhost if the rate - is excessive */ - if (!localhost && log_index >= 0 && CLG_LimitCommandResponseRate(log_index)) { - DEBUG_LOG("Command packet discarded to limit response rate"); - return; - } - if (rx_command >= N_REQUEST_TYPES) { /* This should be already handled */ assert(0); @@ -1408,7 +1485,7 @@ /* Check level of authority required to issue the command. All commands from the Unix domain socket (which is accessible only by the root and chrony user/group) are allowed. */ - if (where_from.sa.sa_family == AF_UNIX) { + if (remote_ip.family == IPADDR_UNSPEC) { assert(sock_fd == sock_fdu); allowed = 1; } else { @@ -1547,12 +1624,8 @@ handle_cmdaccheck(&rx_message, &tx_message); break; - case REQ_ADD_SERVER3: - handle_add_source(NTP_SERVER, &rx_message, &tx_message); - break; - - case REQ_ADD_PEER3: - handle_add_source(NTP_PEER, &rx_message, &tx_message); + case REQ_ADD_SOURCE: + handle_add_source(&rx_message, &tx_message); break; case REQ_DEL_SOURCE: @@ -1567,7 +1640,7 @@ handle_dfreq(&rx_message, &tx_message); break; - case REQ_DOFFSET: + case REQ_DOFFSET2: handle_doffset(&rx_message, &tx_message); break; @@ -1599,7 +1672,7 @@ handle_cyclelogs(&rx_message, &tx_message); break; - case REQ_CLIENT_ACCESSES_BY_INDEX2: + case REQ_CLIENT_ACCESSES_BY_INDEX3: handle_client_accesses_by_index(&rx_message, &tx_message); break; @@ -1655,6 +1728,26 @@ handle_onoffline(&rx_message, &tx_message); break; + case REQ_NTP_SOURCE_NAME: + handle_ntp_source_name(&rx_message, &tx_message); + break; + + case REQ_RESET_SOURCES: + handle_reset_sources(&rx_message, &tx_message); + break; + + case REQ_AUTH_DATA: + handle_auth_data(&rx_message, &tx_message); + break; + + case REQ_SELECT_DATA: + handle_select_data(&rx_message, &tx_message); + break; + + case REQ_RELOAD_SOURCES: + handle_reload_sources(&rx_message, &tx_message); + break; + default: DEBUG_LOG("Unhandled command %d", rx_command); tx_message.status = htons(STT_FAILED); @@ -1666,19 +1759,7 @@ } /* Transmit the response */ - { - /* Include a simple way to lose one message in three to test resend */ - - static int do_it=1; - - if (do_it) { - transmit_reply(&tx_message, &where_from); - } - -#if 0 - do_it = ((do_it + 1) % 3); -#endif - } + transmit_reply(sock_fd, read_length, sck_message); } /* ================================================== */ diff -Nru chrony-3.5/cmdmon.h chrony-4.1/cmdmon.h --- chrony-3.5/cmdmon.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/cmdmon.h 2021-05-12 11:06:15.000000000 +0000 @@ -29,7 +29,7 @@ #include "addressing.h" -extern void CAM_Initialise(int family); +extern void CAM_Initialise(void); extern void CAM_Finalise(void); diff -Nru chrony-3.5/cmdparse.c chrony-4.1/cmdparse.c --- chrony-3.5/cmdparse.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/cmdparse.c 2021-05-12 11:06:15.000000000 +0000 @@ -62,7 +62,11 @@ src->params.filter_length = 0; src->params.interleaved = 0; src->params.sel_options = 0; + src->params.nts = 0; + src->params.nts_port = SRC_DEFAULT_NTSPORT; + src->params.copy = 0; src->params.authkey = INACTIVE_AUTHKEY; + src->params.cert_set = SRC_DEFAULT_CERTSET; src->params.max_delay = SRC_DEFAULT_MAXDELAY; src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO; src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO; @@ -88,6 +92,8 @@ src->params.auto_offline = 1; } else if (!strcasecmp(cmd, "burst")) { src->params.burst = 1; + } else if (!strcasecmp(cmd, "copy")) { + src->params.copy = 1; } else if (!strcasecmp(cmd, "iburst")) { src->params.iburst = 1; } else if (!strcasecmp(cmd, "offline")) { @@ -100,6 +106,9 @@ src->params.sel_options |= SRC_SELECT_REQUIRE; } else if (!strcasecmp(cmd, "trust")) { src->params.sel_options |= SRC_SELECT_TRUST; + } else if (!strcasecmp(cmd, "certset")) { + if (sscanf(line, "%"SCNu32"%n", &src->params.cert_set, &n) != 1) + return 0; } else if (!strcasecmp(cmd, "key")) { if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 || src->params.authkey == INACTIVE_AUTHKEY) @@ -140,11 +149,16 @@ } else if (!strcasecmp(cmd, "minstratum")) { if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1) return 0; + } else if (!strcasecmp(cmd, "nts")) { + src->params.nts = 1; + } else if (!strcasecmp(cmd, "ntsport")) { + if (sscanf(line, "%d%n", &src->params.nts_port, &n) != 1) + return 0; } else if (!strcasecmp(cmd, "offset")) { if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1) return 0; } else if (!strcasecmp(cmd, "port")) { - if (sscanf(line, "%hu%n", &src->port, &n) != 1) + if (sscanf(line, "%d%n", &src->port, &n) != 1) return 0; } else if (!strcasecmp(cmd, "polltarget")) { if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1) @@ -261,7 +275,7 @@ /* ================================================== */ int -CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key) +CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key) { char *s1, *s2, *s3, *s4; @@ -278,10 +292,10 @@ return 0; if (*s3) { - *hash = s2; + *type = s2; *key = s3; } else { - *hash = "MD5"; + *type = "MD5"; *key = s2; } diff -Nru chrony-3.5/cmdparse.h chrony-4.1/cmdparse.h --- chrony-3.5/cmdparse.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/cmdparse.h 2021-05-12 11:06:15.000000000 +0000 @@ -32,7 +32,7 @@ typedef struct { char *name; - unsigned short port; + int port; SourceParameters params; } CPS_NTP_Source; @@ -49,6 +49,6 @@ extern char *CPS_SplitWord(char *line); /* Parse a key from keyfile */ -extern int CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key); +extern int CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key); #endif /* GOT_CMDPARSE_H */ diff -Nru chrony-3.5/conf.c chrony-4.1/conf.c --- chrony-3.5/conf.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/conf.c 2021-05-12 11:06:15.000000000 +0000 @@ -3,7 +3,7 @@ ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 - * Copyright (C) Miroslav Lichvar 2009-2017 + * Copyright (C) Miroslav Lichvar 2009-2017, 2020 * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,8 +33,10 @@ #include "conf.h" #include "ntp_sources.h" #include "ntp_core.h" +#include "nts_ke.h" #include "refclock.h" #include "cmdmon.h" +#include "socket.h" #include "srcparams.h" #include "logging.h" #include "nameserv.h" @@ -43,6 +45,12 @@ #include "util.h" /* ================================================== */ + +#define MAX_LINE_LENGTH 2048 +#define MAX_CONF_DIRS 10 +#define MAX_INCLUDE_LEVEL 10 + +/* ================================================== */ /* Forward prototypes */ static int parse_string(char *line, char **result); @@ -51,11 +59,13 @@ static int parse_null(char *line); static void parse_allow_deny(char *line, ARR_Instance restrictions, int allow); +static void parse_authselectmode(char *); static void parse_bindacqaddress(char *); static void parse_bindaddress(char *); static void parse_bindcmdaddress(char *); static void parse_broadcast(char *); static void parse_clientloglimit(char *); +static void parse_confdir(char *); static void parse_fallbackdrift(char *); static void parse_hwtimestamp(char *); static void parse_include(char *); @@ -66,16 +76,20 @@ static void parse_mailonchange(char *); static void parse_makestep(char *); static void parse_maxchange(char *); +static void parse_ntsserver(char *, ARR_Instance files); +static void parse_ntstrustedcerts(char *); static void parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak); static void parse_refclock(char *); static void parse_smoothtime(char *); -static void parse_source(char *line, NTP_Source_Type type, int pool); +static void parse_source(char *line, char *type, int fatal); +static void parse_sourcedir(char *); static void parse_tempcomp(char *); /* ================================================== */ /* Configuration variables */ +static int print_config = 0; static int restarted = 0; static char *rtc_device; static int acquisition_port = -1; @@ -88,7 +102,9 @@ static double max_clock_error = 1.0; /* in ppm */ static double max_drift = 500000.0; /* in ppm */ static double max_slew_rate = 1e6 / 12.0; /* in ppm */ +static double clock_precision = 0.0; /* in seconds */ +static SRC_AuthSelectMode authselect_mode = SRC_AUTHSELECT_MIX; static double max_distance = 3.0; static double max_jitter = 1.0; static double reselect_distance = 1e-4; @@ -105,8 +121,8 @@ static int do_log_refclocks = 0; static int do_log_tempcomp = 0; static int log_banner = 32; -static char *logdir; -static char *dumpdir; +static char *logdir = NULL; +static char *dumpdir = NULL; static int enable_local=0; static int local_stratum; @@ -180,21 +196,33 @@ the loopback address will be used */ static IPAddr bind_cmd_address4, bind_cmd_address6; +/* Interface names to bind the NTP server, NTP client, and command socket */ +static char *bind_ntp_iface = NULL; +static char *bind_acq_iface = NULL; +static char *bind_cmd_iface = NULL; + /* Path to the Unix domain command socket. */ -static char *bind_cmd_path; +static char *bind_cmd_path = NULL; + +/* Differentiated Services Code Point (DSCP) in transmitted NTP packets */ +static int ntp_dscp = 0; /* Path to Samba (ntp_signd) socket. */ static char *ntp_signd_socket = NULL; /* Filename to use for storing pid of running chronyd, to prevent multiple * chronyds being started. */ -static char *pidfile; +static char *pidfile = NULL; /* Rate limiting parameters */ static int ntp_ratelimit_enabled = 0; static int ntp_ratelimit_interval = 3; static int ntp_ratelimit_burst = 8; static int ntp_ratelimit_leak = 2; +static int nts_ratelimit_enabled = 0; +static int nts_ratelimit_interval = 6; +static int nts_ratelimit_burst = 8; +static int nts_ratelimit_leak = 2; static int cmd_ratelimit_enabled = 0; static int cmd_ratelimit_interval = -4; static int cmd_ratelimit_burst = 8; @@ -223,6 +251,25 @@ /* Name of the user to which will be dropped root privileges. */ static char *user; +/* NTS server and client configuration */ +static char *nts_dump_dir = NULL; +static char *nts_ntp_server = NULL; +static ARR_Instance nts_server_cert_files; /* array of (char *) */ +static ARR_Instance nts_server_key_files; /* array of (char *) */ +static int nts_server_port = NKE_PORT; +static int nts_server_processes = 1; +static int nts_server_connections = 100; +static int nts_refresh = 2419200; /* 4 weeks */ +static int nts_rotate = 604800; /* 1 week */ +static ARR_Instance nts_trusted_certs_paths; /* array of (char *) */ +static ARR_Instance nts_trusted_certs_ids; /* array of uint32_t */ + +/* Number of clock updates needed to enable certificate time checks */ +static int no_cert_time_check = 0; + +/* Flag disabling use of system trusted certificates */ +static int no_system_cert = 0; + /* Array of CNF_HwTsInterface */ static ARR_Instance hwts_interfaces; @@ -234,6 +281,10 @@ /* Array of NTP_Source */ static ARR_Instance ntp_sources; +/* Array of (char *) */ +static ARR_Instance ntp_source_dirs; +/* Array of uint32_t corresponding to ntp_sources (for sourcedirs reload) */ +static ARR_Instance ntp_source_ids; /* Array of RefclockParameters */ static ARR_Instance refclock_sources; @@ -250,8 +301,7 @@ static ARR_Instance cmd_restrictions; typedef struct { - IPAddr addr; - unsigned short port; + NTP_Remote_Address addr; int interval; } NTP_Broadcast_Destination; @@ -265,6 +315,8 @@ static const char *processed_file; static const char *processed_command; +static int include_level = 0; + /* ================================================== */ static void @@ -331,26 +383,36 @@ init_sources = ARR_CreateInstance(sizeof (IPAddr)); ntp_sources = ARR_CreateInstance(sizeof (NTP_Source)); + ntp_source_dirs = ARR_CreateInstance(sizeof (char *)); + ntp_source_ids = ARR_CreateInstance(sizeof (uint32_t)); refclock_sources = ARR_CreateInstance(sizeof (RefclockParameters)); broadcasts = ARR_CreateInstance(sizeof (NTP_Broadcast_Destination)); ntp_restrictions = ARR_CreateInstance(sizeof (AllowDeny)); cmd_restrictions = ARR_CreateInstance(sizeof (AllowDeny)); - dumpdir = Strdup(""); - logdir = Strdup(""); + nts_server_cert_files = ARR_CreateInstance(sizeof (char *)); + nts_server_key_files = ARR_CreateInstance(sizeof (char *)); + nts_trusted_certs_paths = ARR_CreateInstance(sizeof (char *)); + nts_trusted_certs_ids = ARR_CreateInstance(sizeof (uint32_t)); + rtc_device = Strdup(DEFAULT_RTC_DEVICE); hwclock_file = Strdup(DEFAULT_HWCLOCK_FILE); user = Strdup(DEFAULT_USER); if (client_only) { cmd_port = ntp_port = 0; - bind_cmd_path = Strdup(""); - pidfile = Strdup(""); } else { bind_cmd_path = Strdup(DEFAULT_COMMAND_SOCKET); pidfile = Strdup(DEFAULT_PID_FILE); } + + SCK_GetAnyLocalIPAddress(IPADDR_INET4, &bind_address4); + SCK_GetAnyLocalIPAddress(IPADDR_INET6, &bind_address6); + SCK_GetAnyLocalIPAddress(IPADDR_INET4, &bind_acq_address4); + SCK_GetAnyLocalIPAddress(IPADDR_INET6, &bind_acq_address6); + SCK_GetLoopbackIPAddress(IPADDR_INET4, &bind_cmd_address4); + SCK_GetLoopbackIPAddress(IPADDR_INET6, &bind_cmd_address6); } /* ================================================== */ @@ -366,21 +428,43 @@ for (i = 0; i < ARR_GetSize(ntp_sources); i++) Free(((NTP_Source *)ARR_GetElement(ntp_sources, i))->params.name); + for (i = 0; i < ARR_GetSize(ntp_source_dirs); i++) + Free(*(char **)ARR_GetElement(ntp_source_dirs, i)); + 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); + } + for (i = 0; i < ARR_GetSize(nts_server_cert_files); i++) + Free(*(char **)ARR_GetElement(nts_server_cert_files, i)); + for (i = 0; i < ARR_GetSize(nts_server_key_files); i++) + Free(*(char **)ARR_GetElement(nts_server_key_files, i)); + for (i = 0; i < ARR_GetSize(nts_trusted_certs_paths); i++) + Free(*(char **)ARR_GetElement(nts_trusted_certs_paths, i)); ARR_DestroyInstance(init_sources); ARR_DestroyInstance(ntp_sources); + ARR_DestroyInstance(ntp_source_dirs); + ARR_DestroyInstance(ntp_source_ids); ARR_DestroyInstance(refclock_sources); ARR_DestroyInstance(broadcasts); ARR_DestroyInstance(ntp_restrictions); ARR_DestroyInstance(cmd_restrictions); + ARR_DestroyInstance(nts_server_cert_files); + ARR_DestroyInstance(nts_server_key_files); + ARR_DestroyInstance(nts_trusted_certs_paths); + ARR_DestroyInstance(nts_trusted_certs_ids); + Free(drift_file); Free(dumpdir); Free(hwclock_file); Free(keys_file); Free(leapsec_tz); Free(logdir); + Free(bind_ntp_iface); + Free(bind_acq_iface); + Free(bind_cmd_iface); Free(bind_cmd_path); Free(ntp_signd_socket); Free(pidfile); @@ -390,6 +474,16 @@ Free(mail_user_on_change); Free(tempcomp_sensor_file); Free(tempcomp_point_file); + Free(nts_dump_dir); + Free(nts_ntp_server); +} + +/* ================================================== */ + +void +CNF_EnablePrint(void) +{ + print_config = 1; } /* ================================================== */ @@ -399,23 +493,22 @@ CNF_ReadFile(const char *filename) { FILE *in; - char line[2048]; + char line[MAX_LINE_LENGTH + 1]; int i; - in = fopen(filename, "r"); - if (!in) { - LOG_FATAL("Could not open configuration file %s : %s", - filename, strerror(errno)); - return; - } + include_level++; + if (include_level > MAX_INCLUDE_LEVEL) + LOG_FATAL("Maximum include level reached"); - DEBUG_LOG("Reading %s", filename); + in = UTI_OpenFile(NULL, filename, NULL, 'R', 0); for (i = 1; fgets(line, sizeof(line), in); i++) { CNF_ParseLine(filename, i, line); } fclose(in); + + include_level--; } /* ================================================== */ @@ -430,31 +523,50 @@ processed_file = filename; line_number = number; + /* Detect truncated line */ + if (strlen(line) >= MAX_LINE_LENGTH) + other_parse_error("String too long"); + /* Remove extra white-space and comments */ CPS_NormalizeLine(line); /* Skip blank lines */ - if (!*line) + if (!*line) { + processed_file = NULL; return; + } /* We have a real line, now try to match commands */ processed_command = command = line; p = CPS_SplitWord(line); + if (print_config && strcasecmp(command, "include") && strcasecmp(command, "confdir")) + printf("%s%s%s\n", command, p[0] != '\0' ? " " : "", p); + if (!strcasecmp(command, "acquisitionport")) { parse_int(p, &acquisition_port); } else if (!strcasecmp(command, "allow")) { parse_allow_deny(p, ntp_restrictions, 1); + } else if (!strcasecmp(command, "authselectmode")) { + parse_authselectmode(p); } else if (!strcasecmp(command, "bindacqaddress")) { parse_bindacqaddress(p); + } else if (!strcasecmp(command, "bindacqdevice")) { + parse_string(p, &bind_acq_iface); } else if (!strcasecmp(command, "bindaddress")) { parse_bindaddress(p); } else if (!strcasecmp(command, "bindcmdaddress")) { parse_bindcmdaddress(p); + } else if (!strcasecmp(command, "bindcmddevice")) { + parse_string(p, &bind_cmd_iface); + } else if (!strcasecmp(command, "binddevice")) { + parse_string(p, &bind_ntp_iface); } else if (!strcasecmp(command, "broadcast")) { parse_broadcast(p); } else if (!strcasecmp(command, "clientloglimit")) { parse_clientloglimit(p); + } else if (!strcasecmp(command, "clockprecision")) { + parse_double(p, &clock_precision); } else if (!strcasecmp(command, "cmdallow")) { parse_allow_deny(p, cmd_restrictions, 1); } else if (!strcasecmp(command, "cmddeny")) { @@ -466,12 +578,16 @@ &cmd_ratelimit_burst, &cmd_ratelimit_leak); } else if (!strcasecmp(command, "combinelimit")) { parse_double(p, &combine_limit); + } else if (!strcasecmp(command, "confdir")) { + parse_confdir(p); } else if (!strcasecmp(command, "corrtimeratio")) { parse_double(p, &correction_time_ratio); } else if (!strcasecmp(command, "deny")) { parse_allow_deny(p, ntp_restrictions, 0); } else if (!strcasecmp(command, "driftfile")) { parse_string(p, &drift_file); + } else if (!strcasecmp(command, "dscp")) { + parse_int(p, &ntp_dscp); } else if (!strcasecmp(command, "dumpdir")) { parse_string(p, &dumpdir); } else if (!strcasecmp(command, "dumponexit")) { @@ -520,6 +636,8 @@ parse_double(p, &max_drift); } else if (!strcasecmp(command, "maxjitter")) { parse_double(p, &max_jitter); + } else if (!strcasecmp(command, "maxntsconnections")) { + parse_int(p, &nts_server_connections); } else if (!strcasecmp(command, "maxsamples")) { parse_int(p, &max_samples); } else if (!strcasecmp(command, "maxslewrate")) { @@ -530,16 +648,42 @@ parse_int(p, &min_samples); } else if (!strcasecmp(command, "minsources")) { parse_int(p, &min_sources); + } else if (!strcasecmp(command, "nocerttimecheck")) { + parse_int(p, &no_cert_time_check); } else if (!strcasecmp(command, "noclientlog")) { no_client_log = parse_null(p); + } else if (!strcasecmp(command, "nosystemcert")) { + no_system_cert = parse_null(p); } else if (!strcasecmp(command, "ntpsigndsocket")) { parse_string(p, &ntp_signd_socket); + } else if (!strcasecmp(command, "ntsratelimit")) { + parse_ratelimit(p, &nts_ratelimit_enabled, &nts_ratelimit_interval, + &nts_ratelimit_burst, &nts_ratelimit_leak); + } else if (!strcasecmp(command, "ntscachedir") || + !strcasecmp(command, "ntsdumpdir")) { + parse_string(p, &nts_dump_dir); + } else if (!strcasecmp(command, "ntsntpserver")) { + parse_string(p, &nts_ntp_server); + } else if (!strcasecmp(command, "ntsport")) { + parse_int(p, &nts_server_port); + } else if (!strcasecmp(command, "ntsprocesses")) { + parse_int(p, &nts_server_processes); + } else if (!strcasecmp(command, "ntsrefresh")) { + parse_int(p, &nts_refresh); + } else if (!strcasecmp(command, "ntsrotate")) { + parse_int(p, &nts_rotate); + } else if (!strcasecmp(command, "ntsservercert")) { + parse_ntsserver(p, nts_server_cert_files); + } else if (!strcasecmp(command, "ntsserverkey")) { + parse_ntsserver(p, nts_server_key_files); + } else if (!strcasecmp(command, "ntstrustedcerts")) { + parse_ntstrustedcerts(p); } else if (!strcasecmp(command, "peer")) { - parse_source(p, NTP_PEER, 0); + parse_source(p, command, 1); } else if (!strcasecmp(command, "pidfile")) { parse_string(p, &pidfile); } else if (!strcasecmp(command, "pool")) { - parse_source(p, NTP_SERVER, 1); + parse_source(p, command, 1); } else if (!strcasecmp(command, "port")) { parse_int(p, &ntp_port); } else if (!strcasecmp(command, "ratelimit")) { @@ -562,9 +706,11 @@ } else if (!strcasecmp(command, "sched_priority")) { parse_int(p, &sched_priority); } else if (!strcasecmp(command, "server")) { - parse_source(p, NTP_SERVER, 0); + parse_source(p, command, 1); } else if (!strcasecmp(command, "smoothtime")) { parse_smoothtime(p); + } else if (!strcasecmp(command, "sourcedir")) { + parse_sourcedir(p); } else if (!strcasecmp(command, "stratumweight")) { parse_double(p, &stratum_weight); } else if (!strcasecmp(command, "tempcomp")) { @@ -577,8 +723,10 @@ !strcasecmp(command, "linux_hz")) { LOG(LOGS_WARN, "%s directive is no longer supported", command); } else { - other_parse_error("Invalid command"); + other_parse_error("Invalid directive"); } + + processed_file = processed_command = NULL; } /* ================================================== */ @@ -630,15 +778,31 @@ /* ================================================== */ static void -parse_source(char *line, NTP_Source_Type type, int pool) +parse_source(char *line, char *type, int fatal) { NTP_Source source; - source.type = type; - source.pool = pool; + if (strcasecmp(type, "peer") == 0) { + source.type = NTP_PEER; + source.pool = 0; + } else if (strcasecmp(type, "pool") == 0) { + source.type = NTP_SERVER; + source.pool = 1; + } else if (strcasecmp(type, "server") == 0) { + source.type = NTP_SERVER; + source.pool = 0; + } else { + if (fatal) + command_parse_error(); + return; + } + + /* Avoid comparing uninitialized data in compare_sources() */ + memset(&source.params, 0, sizeof (source.params)); if (!CPS_ParseNTPSourceAdd(line, &source.params)) { - command_parse_error(); + if (fatal) + command_parse_error(); return; } @@ -649,6 +813,17 @@ /* ================================================== */ static void +parse_sourcedir(char *line) +{ + char *s; + + s = Strdup(line); + ARR_AppendElement(ntp_source_dirs, &s); +} + +/* ================================================== */ + +static void parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak) { int n, val; @@ -1000,6 +1175,41 @@ /* ================================================== */ static void +parse_ntsserver(char *line, ARR_Instance files) +{ + char *file = NULL; + + parse_string(line, &file); + ARR_AppendElement(files, &file); +} + +/* ================================================== */ + +static void +parse_ntstrustedcerts(char *line) +{ + uint32_t id; + char *path; + + if (get_number_of_args(line) == 2) { + path = CPS_SplitWord(line); + if (sscanf(line, "%"SCNu32, &id) != 1) + command_parse_error(); + } else { + check_number_of_args(line, 1); + path = line; + id = 0; + } + + path = Strdup(path); + + ARR_AppendElement(nts_trusted_certs_paths, &path); + ARR_AppendElement(nts_trusted_certs_ids, &id); +} + +/* ================================================== */ + +static void parse_allow_deny(char *line, ARR_Instance restrictions, int allow) { char *p; @@ -1101,6 +1311,23 @@ /* ================================================== */ static void +parse_authselectmode(char *line) +{ + if (!strcasecmp(line, "require")) + authselect_mode = SRC_AUTHSELECT_REQUIRE; + else if (!strcasecmp(line, "prefer")) + authselect_mode = SRC_AUTHSELECT_PREFER; + else if (!strcasecmp(line, "mix")) + authselect_mode = SRC_AUTHSELECT_MIX; + else if (!strcasecmp(line, "ignore")) + authselect_mode = SRC_AUTHSELECT_IGNORE; + else + command_parse_error(); +} + +/* ================================================== */ + +static void parse_bindacqaddress(char *line) { IPAddr ip; @@ -1147,8 +1374,10 @@ if (line[0] == '/') { parse_string(line, &bind_cmd_path); /* / disables the socket */ - if (!strcmp(bind_cmd_path, "/")) - bind_cmd_path[0] = '\0'; + if (strcmp(bind_cmd_path, "/") == 0) { + Free(bind_cmd_path); + bind_cmd_path = NULL; + } } else if (UTI_StringToIP(line, &ip)) { if (ip.family == IPADDR_INET4) bind_cmd_address4 = ip; @@ -1201,8 +1430,8 @@ } destination = (NTP_Broadcast_Destination *)ARR_GetNewElement(broadcasts); - destination->addr = ip; - destination->port = port; + destination->addr.ip_addr = ip; + destination->addr.port = port; destination->interval = interval; } @@ -1345,6 +1574,86 @@ /* ================================================== */ +static const char * +get_basename(const char *path) +{ + const char *b = strrchr(path, '/'); + return b ? b + 1 : path; +} + +/* ================================================== */ + +static int +compare_basenames(const void *a, const void *b) +{ + return strcmp(get_basename(*(const char * const *)a), + get_basename(*(const char * const *)b)); +} + +/* ================================================== */ + +static int +search_dirs(char *line, const char *suffix, void (*file_handler)(const char *path)) +{ + char *dirs[MAX_CONF_DIRS], buf[MAX_LINE_LENGTH], *path; + size_t i, j, k, locations, n_dirs; + glob_t gl; + + n_dirs = UTI_SplitString(line, dirs, MAX_CONF_DIRS); + if (n_dirs < 1 || n_dirs > MAX_CONF_DIRS) + return 0; + + /* Get the paths of all config files in the specified directories */ + for (i = 0; i < n_dirs; i++) { + if (snprintf(buf, sizeof (buf), "%s/*%s", dirs[i], suffix) >= sizeof (buf)) + assert(0); + if (glob(buf, GLOB_NOSORT | (i > 0 ? GLOB_APPEND : 0), NULL, &gl) != 0) + ; + } + + if (gl.gl_pathc > 0) { + /* Sort the paths by filenames */ + qsort(gl.gl_pathv, gl.gl_pathc, sizeof (gl.gl_pathv[0]), compare_basenames); + + for (i = 0; i < gl.gl_pathc; i += locations) { + /* Count directories containing files with this name */ + for (j = i + 1, locations = 1; j < gl.gl_pathc; j++, locations++) { + if (compare_basenames(&gl.gl_pathv[i], &gl.gl_pathv[j]) != 0) + break; + } + + /* Read the first file of this name in the order of the directive */ + for (j = 0; j < n_dirs; j++) { + for (k = 0; k < locations; k++) { + path = gl.gl_pathv[i + k]; + if (strncmp(path, dirs[j], strlen(dirs[j])) == 0 && + strlen(dirs[j]) + 1 + strlen(get_basename(path)) == strlen(path)) { + file_handler(path); + break; + } + } + if (k < locations) + break; + } + } + } + + globfree(&gl); + + return 1; +} + +/* ================================================== */ + +static void +parse_confdir(char *line) +{ + if (!search_dirs(line, ".conf", CNF_ReadFile)) + command_parse_error(); +} + +/* ================================================== */ + static void parse_include(char *line) { @@ -1374,13 +1683,147 @@ /* ================================================== */ +static void +load_source_file(const char *filename) +{ + char line[MAX_LINE_LENGTH + 1]; + FILE *f; + + f = UTI_OpenFile(NULL, filename, NULL, 'r', 0); + if (!f) + return; + + while (fgets(line, sizeof (line), f)) { + /* Require lines to be terminated */ + if (line[0] == '\0' || line[strlen(line) - 1] != '\n') + break; + + CPS_NormalizeLine(line); + if (line[0] == '\0') + continue; + + parse_source(CPS_SplitWord(line), line, 0); + } + + fclose(f); +} + +/* ================================================== */ + +static int +compare_sources(const void *a, const void *b) +{ + const NTP_Source *sa = a, *sb = b; + int d; + + if (!sa->params.name) + return -1; + if (!sb->params.name) + return 1; + if ((d = strcmp(sa->params.name, sb->params.name)) != 0) + return d; + if ((d = (int)(sa->type) - (int)(sb->type)) != 0) + return d; + if ((d = sa->pool - sb->pool) != 0) + return d; + if ((d = sa->params.port - sb->params.port) != 0) + return d; + return memcmp(&sa->params.params, &sb->params.params, sizeof (sa->params.params)); +} + +/* ================================================== */ + +static void +reload_source_dirs(void) +{ + NTP_Source *prev_sources, *new_sources, *source; + unsigned int i, j, prev_size, new_size, unresolved; + uint32_t *prev_ids, *new_ids; + char buf[MAX_LINE_LENGTH]; + NSR_Status s; + int d; + + prev_size = ARR_GetSize(ntp_source_ids); + if (prev_size > 0 && ARR_GetSize(ntp_sources) != prev_size) + assert(0); + + /* Save the current sources and their configuration IDs */ + prev_ids = MallocArray(uint32_t, prev_size); + memcpy(prev_ids, ARR_GetElements(ntp_source_ids), prev_size * sizeof (prev_ids[0])); + prev_sources = MallocArray(NTP_Source, prev_size); + memcpy(prev_sources, ARR_GetElements(ntp_sources), prev_size * sizeof (prev_sources[0])); + + /* Load the sources again */ + ARR_SetSize(ntp_sources, 0); + for (i = 0; i < ARR_GetSize(ntp_source_dirs); i++) { + if (snprintf(buf, sizeof (buf), "%s", + *(char **)ARR_GetElement(ntp_source_dirs, i)) >= sizeof (buf)) + assert(0); + search_dirs(buf, ".sources", load_source_file); + } + + /* Add new and remove existing sources according to the new configuration. + Avoid removing and adding the same source again to keep its state. */ + + new_size = ARR_GetSize(ntp_sources); + new_sources = ARR_GetElements(ntp_sources); + ARR_SetSize(ntp_source_ids, new_size); + new_ids = ARR_GetElements(ntp_source_ids); + unresolved = 0; + + qsort(new_sources, new_size, sizeof (new_sources[0]), compare_sources); + + for (i = j = 0; i < prev_size || j < new_size; ) { + if (i < prev_size && j < new_size) + d = compare_sources(&prev_sources[i], &new_sources[j]); + else + d = i < prev_size ? -1 : 1; + + if (d < 0) { + /* Remove the missing source */ + if (prev_sources[i].params.name[0] != '\0') + NSR_RemoveSourcesById(prev_ids[i]); + i++; + } else if (d > 0) { + /* Add a newly configured source */ + source = &new_sources[j]; + s = NSR_AddSourceByName(source->params.name, source->params.port, source->pool, + source->type, &source->params.params, &new_ids[j]); + + if (s == NSR_UnresolvedName) { + unresolved++; + } else if (s != NSR_Success) { + LOG(LOGS_ERR, "Could not add source %s", source->params.name); + + /* Mark the source as not present */ + source->params.name[0] = '\0'; + } + j++; + } else { + /* Keep the existing source */ + new_ids[j] = prev_ids[i]; + i++, j++; + } + } + + for (i = 0; i < prev_size; i++) + Free(prev_sources[i].params.name); + Free(prev_sources); + Free(prev_ids); + + if (unresolved > 0) + NSR_ResolveSources(); +} + +/* ================================================== */ + void CNF_CreateDirs(uid_t uid, gid_t gid) { char *dir; /* Create a directory for the Unix domain command socket */ - if (bind_cmd_path[0]) { + if (bind_cmd_path) { dir = UTI_PathToDir(bind_cmd_path); UTI_CreateDirAndParents(dir, 0770, uid, gid); @@ -1389,16 +1832,19 @@ domain sockets are ignored on some systems (e.g. Solaris). */ if (!UTI_CheckDirPermissions(dir, 0770, uid, gid)) { LOG(LOGS_WARN, "Disabled command socket %s", bind_cmd_path); - bind_cmd_path[0] = '\0'; + Free(bind_cmd_path); + bind_cmd_path = NULL; } Free(dir); } - if (logdir[0]) - UTI_CreateDirAndParents(logdir, 0755, uid, gid); - if (dumpdir[0]) - UTI_CreateDirAndParents(dumpdir, 0755, uid, gid); + if (logdir) + UTI_CreateDirAndParents(logdir, 0750, uid, gid); + if (dumpdir) + UTI_CreateDirAndParents(dumpdir, 0750, uid, gid); + if (nts_dump_dir) + UTI_CreateDirAndParents(nts_dump_dir, 0750, uid, gid); } /* ================================================== */ @@ -1415,13 +1861,13 @@ /* Get the default NTP params */ CPS_ParseNTPSourceAdd(dummy_hostname, &cps_source); - /* Add the address as an offline iburst server */ + /* Add the address as a server specified with the iburst option */ ntp_addr.ip_addr = *(IPAddr *)ARR_GetElement(init_sources, i); ntp_addr.port = cps_source.port; cps_source.params.iburst = 1; - cps_source.params.connectivity = SRC_OFFLINE; - NSR_AddSource(&ntp_addr, NTP_SERVER, &cps_source.params); + if (NSR_AddSource(&ntp_addr, NTP_SERVER, &cps_source.params, NULL) != NSR_Success) + LOG(LOGS_ERR, "Could not add source %s", UTI_IPToString(&ntp_addr.ip_addr)); } ARR_SetSize(init_sources, 0); @@ -1434,15 +1880,22 @@ { NTP_Source *source; unsigned int i; + NSR_Status s; for (i = 0; i < ARR_GetSize(ntp_sources); i++) { source = (NTP_Source *)ARR_GetElement(ntp_sources, i); - NSR_AddSourceByName(source->params.name, source->params.port, - source->pool, source->type, &source->params.params); + + s = NSR_AddSourceByName(source->params.name, source->params.port, source->pool, + source->type, &source->params.params, NULL); + if (s != NSR_Success && s != NSR_UnresolvedName) + LOG(LOGS_ERR, "Could not add source %s", source->params.name); + Free(source->params.name); } ARR_SetSize(ntp_sources, 0); + + reload_source_dirs(); } /* ================================================== */ @@ -1450,10 +1903,14 @@ void CNF_AddRefclocks(void) { + RefclockParameters *refclock; unsigned int 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); @@ -1469,8 +1926,7 @@ for (i = 0; i < ARR_GetSize(broadcasts); i++) { destination = (NTP_Broadcast_Destination *)ARR_GetElement(broadcasts, i); - NCR_AddBroadcastDestination(&destination->addr, destination->port, - destination->interval); + NCR_AddBroadcastDestination(&destination->addr, destination->interval); } ARR_SetSize(broadcasts, 0); @@ -1478,6 +1934,14 @@ /* ================================================== */ +void +CNF_ReloadSources(void) +{ + reload_source_dirs(); +} + +/* ================================================== */ + int CNF_GetNTPPort(void) { @@ -1639,6 +2103,14 @@ /* ================================================== */ +SRC_AuthSelectMode +CNF_GetAuthSelectMode(void) +{ + return authselect_mode; +} + +/* ================================================== */ + double CNF_GetMaxSlewRate(void) { @@ -1648,6 +2120,14 @@ /* ================================================== */ double +CNF_GetClockPrecision(void) +{ + return clock_precision; +} + +/* ================================================== */ + +double CNF_GetMaxDistance(void) { return max_distance; @@ -1857,6 +2337,30 @@ /* ================================================== */ char * +CNF_GetBindNtpInterface(void) +{ + return bind_ntp_iface; +} + +/* ================================================== */ + +char * +CNF_GetBindAcquisitionInterface(void) +{ + return bind_acq_iface; +} + +/* ================================================== */ + +char * +CNF_GetBindCommandInterface(void) +{ + return bind_cmd_iface; +} + +/* ================================================== */ + +char * CNF_GetBindCommandPath(void) { return bind_cmd_path; @@ -1877,6 +2381,14 @@ /* ================================================== */ +int +CNF_GetNtpDscp(void) +{ + return ntp_dscp; +} + +/* ================================================== */ + char * CNF_GetNtpSigndSocket(void) { @@ -1935,6 +2447,16 @@ /* ================================================== */ +int CNF_GetNtsRateLimit(int *interval, int *burst, int *leak) +{ + *interval = nts_ratelimit_interval; + *burst = nts_ratelimit_burst; + *leak = nts_ratelimit_leak; + return nts_ratelimit_enabled; +} + +/* ================================================== */ + int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak) { *interval = cmd_ratelimit_interval; @@ -2034,3 +2556,103 @@ *iface = (CNF_HwTsInterface *)ARR_GetElement(hwts_interfaces, index); return 1; } + +/* ================================================== */ + +char * +CNF_GetNtsDumpDir(void) +{ + return nts_dump_dir; +} + +/* ================================================== */ + +char * +CNF_GetNtsNtpServer(void) +{ + return nts_ntp_server; +} + +/* ================================================== */ + +int +CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys) +{ + *certs = ARR_GetElements(nts_server_cert_files); + *keys = ARR_GetElements(nts_server_key_files); + + if (ARR_GetSize(nts_server_cert_files) != ARR_GetSize(nts_server_key_files)) + LOG_FATAL("Uneven number of NTS certs and keys"); + + return ARR_GetSize(nts_server_cert_files); +} + +/* ================================================== */ + +int +CNF_GetNtsServerPort(void) +{ + return nts_server_port; +} + +/* ================================================== */ + +int +CNF_GetNtsServerProcesses(void) +{ + return nts_server_processes; +} + +/* ================================================== */ + +int +CNF_GetNtsServerConnections(void) +{ + return nts_server_connections; +} + +/* ================================================== */ + +int +CNF_GetNtsRefresh(void) +{ + return nts_refresh; +} + +/* ================================================== */ + +int +CNF_GetNtsRotate(void) +{ + return nts_rotate; +} + +/* ================================================== */ + +int +CNF_GetNtsTrustedCertsPaths(const char ***paths, uint32_t **ids) +{ + *paths = ARR_GetElements(nts_trusted_certs_paths); + *ids = ARR_GetElements(nts_trusted_certs_ids); + + if (ARR_GetSize(nts_trusted_certs_paths) != ARR_GetSize(nts_trusted_certs_ids)) + assert(0); + + return ARR_GetSize(nts_trusted_certs_paths); +} + +/* ================================================== */ + +int +CNF_GetNoSystemCert(void) +{ + return no_system_cert; +} + +/* ================================================== */ + +int +CNF_GetNoCertTimeCheck(void) +{ + return no_cert_time_check; +} diff -Nru chrony-3.5/conf.h chrony-4.1/conf.h --- chrony-3.5/conf.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/conf.h 2021-05-12 11:06:15.000000000 +0000 @@ -30,10 +30,13 @@ #include "addressing.h" #include "reference.h" +#include "sources.h" extern void CNF_Initialise(int restarted, int client_only); extern void CNF_Finalise(void); +extern void CNF_EnablePrint(void); + extern char *CNF_GetRtcDevice(void); extern void CNF_ReadFile(const char *filename); @@ -46,6 +49,8 @@ extern void CNF_AddBroadcasts(void); extern void CNF_AddRefclocks(void); +extern void CNF_ReloadSources(void); + extern int CNF_GetAcquisitionPort(void); extern int CNF_GetNTPPort(void); extern char *CNF_GetDriftFile(void); @@ -74,7 +79,11 @@ extern void CNF_GetBindAddress(int family, IPAddr *addr); extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr); extern void CNF_GetBindCommandAddress(int family, IPAddr *addr); +extern char *CNF_GetBindNtpInterface(void); +extern char *CNF_GetBindAcquisitionInterface(void); +extern char *CNF_GetBindCommandInterface(void); extern char *CNF_GetBindCommandPath(void); +extern int CNF_GetNtpDscp(void); extern char *CNF_GetNtpSigndSocket(void); extern char *CNF_GetPidFile(void); extern REF_LeapMode CNF_GetLeapSecMode(void); @@ -86,7 +95,9 @@ extern double CNF_GetMaxDrift(void); extern double CNF_GetCorrectionTimeRatio(void); extern double CNF_GetMaxSlewRate(void); +extern double CNF_GetClockPrecision(void); +extern SRC_AuthSelectMode CNF_GetAuthSelectMode(void); extern double CNF_GetMaxDistance(void); extern double CNF_GetMaxJitter(void); extern double CNF_GetReselectDistance(void); @@ -101,6 +112,7 @@ extern int CNF_GetLockMemory(void); extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak); +extern int CNF_GetNtsRateLimit(int *interval, int *burst, int *leak); extern int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak); extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only); extern void CNF_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2); @@ -139,4 +151,16 @@ extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface); +extern char *CNF_GetNtsDumpDir(void); +extern char *CNF_GetNtsNtpServer(void); +extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys); +extern int CNF_GetNtsServerPort(void); +extern int CNF_GetNtsServerProcesses(void); +extern int CNF_GetNtsServerConnections(void); +extern int CNF_GetNtsRefresh(void); +extern int CNF_GetNtsRotate(void); +extern int CNF_GetNtsTrustedCertsPaths(const char ***paths, uint32_t **ids); +extern int CNF_GetNoSystemCert(void); +extern int CNF_GetNoCertTimeCheck(void); + #endif /* GOT_CONF_H */ diff -Nru chrony-3.5/configure chrony-4.1/configure --- chrony-3.5/configure 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/configure 2021-05-12 11:06:15.000000000 +0000 @@ -5,7 +5,7 @@ # # Copyright (C) Richard P. Curnow 1997-2003 # Copyright (C) Bryan Christianson 2016 -# Copyright (C) Miroslav Lichvar 2009, 2012-2018 +# Copyright (C) Miroslav Lichvar 2009, 2012-2021 # Copyright (C) Stefan R. Filipek 2019 # # ======================================================================= @@ -33,13 +33,13 @@ echo "int main(int argc, char **argv) {" echo "$code" echo "return 0; }" - ) > docheck.c + ) > conftest.c - echo "docheck.c:" >> config.log - cat docheck.c >> config.log - echo $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o docheck docheck.c $ldflags \ + echo "conftest.c:" >> config.log + cat conftest.c >> config.log + echo $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o conftest conftest.c $ldflags \ $MYLDFLAGS >> config.log - $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o docheck docheck.c $ldflags \ + $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o conftest conftest.c $ldflags \ $MYLDFLAGS >> config.log 2>&1 if [ $? -eq 0 ] @@ -50,11 +50,39 @@ echo "No" result=1 fi - rm -f docheck.c docheck + rm -f conftest.c conftest echo >> config.log return $result } #}}} +#{{{ test_executable +test_executable () { + name=$1 + executable=$2 + options=$3 + + printf "%s" "Checking for $name : " + + echo $executable $options >> config.log + $executable $options >> config.log 2>&1 + + if [ $? -eq 0 ] + then + echo "Yes" + result=0 + else + echo "No" + result=1 + fi + echo >> config.log + return $result +} +#}}} +#{{{ pkg_config +pkg_config () { + $PKG_CONFIG "$@" 2>> config.log +} +#}}} #{{{ usage usage () { cat < LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory + PKG_CONFIG path to pkg-config utility + PKG_CONFIG_PATH + directories to add to pkg-config's search path + PKG_CONFIG_LIBDIR + path overriding pkg-config's built-in search path Use these variables to override the choices made by \`configure' or to help it to find libraries and programs with nonstandard names/locations. @@ -154,13 +185,6 @@ fi } #}}} -#{{{ pkg_config -pkg_config () { - type pkg-config > /dev/null 2> /dev/null || return 1 - - pkg-config $@ 2> /dev/null -} -#}}} #{{{ get_features get_features () { ff=1 @@ -186,23 +210,24 @@ VERSION=`uname -r` MACHINE=`uname -m` +LIBS="" EXTRA_LIBS="" EXTRA_CLI_LIBS="" EXTRA_OBJECTS="" -EXTRA_DEFS="" -SYSDEFS="" +EXTRA_CLI_OBJECTS="" feat_debug=0 feat_cmdmon=1 feat_ntp=1 feat_refclock=1 feat_readline=1 -try_readline=1 try_editline=1 feat_sechash=1 try_nettle=1 try_nss=1 try_tomcrypt=1 +feat_nts=1 +try_gnutls=1 feat_rtc=1 try_rtc=0 feat_droproot=1 @@ -211,9 +236,6 @@ feat_scfilter=0 try_seccomp=-1 priv_ops="" -readline_lib="" -readline_inc="" -ncurses_lib="" feat_ipv6=1 feat_phc=1 try_phc=0 @@ -244,21 +266,9 @@ --disable-readline ) feat_readline=0 ;; - --without-readline ) - try_readline=0 - ;; --without-editline ) try_editline=0 ;; - --with-readline-library=* ) - readline_lib=-L`echo $option | sed -e 's/^.*=//;'` - ;; - --with-readline-includes=* ) - readline_inc=-I`echo $option | sed -e 's/^.*=//;'` - ;; - --with-ncurses-library=* ) - ncurses_lib=-L`echo $option | sed -e 's/^.*=//;'` - ;; --prefix=* | --install_prefix=* ) SETPREFIX=`echo $option | sed -e 's/[^=]*=//;'` ;; @@ -373,6 +383,12 @@ --without-tomcrypt ) try_tomcrypt=0 ;; + --disable-nts ) + feat_nts=0 + ;; + --without-gnutls ) + try_gnutls=0 + ;; --host-system=* ) OPERATINGSYSTEM=`echo $option | sed -e 's/^.*=//;'` ;; @@ -432,8 +448,7 @@ ;; Darwin) EXTRA_OBJECTS="sys_macosx.o" - EXTRA_LIBS="-lresolv" - EXTRA_CLI_LIBS="-lresolv" + LIBS="$LIBS -lresolv" add_def MACOSX if [ $feat_droproot = "1" ]; then add_def FEAT_PRIVDROP @@ -452,8 +467,7 @@ ;; SunOS) EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o sys_posix.o" - EXTRA_LIBS="-lsocket -lnsl -lresolv" - EXTRA_CLI_LIBS="-lsocket -lnsl -lresolv" + LIBS="$LIBS -lsocket -lnsl -lresolv" try_setsched=1 try_lockmem=1 add_def SOLARIS @@ -485,7 +499,7 @@ if [ $feat_ntp = "1" ]; then add_def FEAT_NTP - EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_core.o ntp_io.o ntp_sources.o" + EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_auth.o ntp_core.o ntp_ext.o ntp_io.o ntp_sources.o" if [ $feat_ntp_signd = "1" ]; then add_def FEAT_SIGND EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_signd.o" @@ -553,6 +567,16 @@ MYCFLAGS="$MYCFLAGS -Wmissing-prototypes -Wall" fi +if [ "x$PKG_CONFIG" = "x" ]; then + PKG_CONFIG=pkg-config +fi + +if ! test_executable "pkg-config" $PKG_CONFIG --version; then + try_nettle=0 + try_nss=0 + try_gnutls=0 +fi + if test_code '64-bit time_t' 'time.h' '' '' ' char x[sizeof(time_t) > 4 ? 1 : -1] = {0}; return x[0];' @@ -592,11 +616,9 @@ fi MATHCODE='return (int) pow(2.0, log(sqrt((double)argc)));' -if test_code 'math' 'math.h' '' '' "$MATHCODE"; then - LIBS="" -else +if ! test_code 'math' 'math.h' '' '' "$MATHCODE"; then if test_code 'math in -lm' 'math.h' '' '-lm' "$MATHCODE"; then - LIBS="-lm" + LIBS="$LIBS -lm" else echo "error: could not compile/link a program which uses sqrt(), log(), pow()" exit 1 @@ -611,10 +633,11 @@ fi if [ $feat_ipv6 = "1" ] && \ - test_code 'IPv6 support' 'arpa/inet.h sys/socket.h netinet/in.h' '' "$EXTRA_LIBS" ' + test_code 'IPv6 support' 'arpa/inet.h sys/socket.h netinet/in.h' '' "$LIBS" ' struct sockaddr_in6 n; char p[100]; n.sin6_addr = in6addr_any; + n.sin6_scope_id = 0; return !inet_ntop(AF_INET6, &n.sin6_addr.s6_addr, p, sizeof(p));' then add_def FEAT_IPV6 @@ -632,6 +655,20 @@ fi fi +if ! test_code 'O_NOFOLLOW flag' 'sys/types.h sys/stat.h fcntl.h' '' "$LIBS" \ + 'return open("/dev/null", O_NOFOLLOW);' +then + if test_code 'O_NOFOLLOW flag with _GNU_SOURCE' 'sys/types.h sys/stat.h fcntl.h' \ + '-D_GNU_SOURCE' "$LIBS" \ + 'return open("/dev/null", O_NOFOLLOW);' + then + add_def _GNU_SOURCE + else + echo "error: open() does not support O_NOFOLLOW flag" + exit 1 + fi +fi + if [ $try_clock_gettime = "1" ]; then if test_code 'clock_gettime()' 'time.h' '' '' \ 'clock_gettime(CLOCK_REALTIME, NULL);' @@ -647,15 +684,17 @@ fi fi -if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$EXTRA_LIBS" \ +if ! test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$LIBS" \ 'return getaddrinfo(0, 0, 0, 0);' then - add_def HAVE_GETADDRINFO + echo "error: getaddrinfo() not found" + exit 1 fi if [ $feat_asyncdns = "1" ] && \ - test_code 'pthread' 'pthread.h' '-pthread' '' \ - 'return (int)pthread_create((void *)1, NULL, (void *)1, NULL);' + test_code 'pthread' 'pthread.h' '-pthread' '' ' + pthread_t thread; + return (int)pthread_create(&thread, NULL, (void *)1, NULL);' then add_def FEAT_ASYNCDNS add_def USE_PTHREAD_ASYNCDNS @@ -665,22 +704,22 @@ if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; then add_def HAVE_ARC4RANDOM -fi - -if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \ - 'return getrandom(NULL, 256, 0);'; then - add_def HAVE_GETRANDOM +else + if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \ + 'return getrandom(NULL, 256, 0);'; then + add_def HAVE_GETRANDOM + fi fi RECVMMSG_CODE=' struct mmsghdr hdr; return !recvmmsg(0, &hdr, 1, MSG_DONTWAIT, 0);' if [ $try_recvmmsg = "1" ]; then - if test_code 'recvmmsg()' 'sys/socket.h' '' "$EXTRA_LIBS" "$RECVMMSG_CODE"; then + if test_code 'recvmmsg()' 'sys/socket.h' '' "$LIBS" "$RECVMMSG_CODE"; then add_def HAVE_RECVMMSG else if test_code 'recvmmsg() with _GNU_SOURCE' 'sys/socket.h' '-D_GNU_SOURCE' \ - "$EXTRA_LIBS" "$RECVMMSG_CODE" + "$LIBS" "$RECVMMSG_CODE" then add_def _GNU_SOURCE add_def HAVE_RECVMMSG @@ -731,6 +770,7 @@ pps_handle_t h = 0; pps_info_t i; struct timespec ts; + ts.tv_sec = ts.tv_nsec = 0; return time_pps_fetch(h, PPS_TSFMT_TSPEC, &i, &ts);' then add_def FEAT_PPS @@ -759,10 +799,12 @@ 'seccomp_init(SCMP_ACT_KILL);' then add_def FEAT_SCFILTER - # NAME2IPADDRESS shouldn't be enabled with other operations as the helper - # process works on one request at the time and the async resolver could - # block the main thread - priv_ops="NAME2IPADDRESS RELOADDNS" + if [ $feat_ntp = "1" ]; then + # NAME2IPADDRESS shouldn't be enabled together with a privops operation + # used by the main thread as the helper process works on one request at + # a time and the async resolver would block the main thread + priv_ops="NAME2IPADDRESS RELOADDNS" + fi EXTRA_LIBS="$EXTRA_LIBS -lseccomp" fi @@ -817,6 +859,7 @@ 'setrlimit(RLIMIT_MEMLOCK, ...)' \ 'sys/resource.h' '' '' ' struct rlimit rlim; + rlim.rlim_max = rlim.rlim_cur = RLIM_INFINITY; setrlimit(RLIMIT_MEMLOCK, &rlim);' then add_def HAVE_SETRLIMIT_MEMLOCK @@ -830,37 +873,11 @@ READLINE_LINK="" if [ $feat_readline = "1" ]; then if [ $try_editline = "1" ]; then - if test_code editline 'stdio.h editline/readline.h' \ - "$readline_inc" "$readline_lib -ledit" \ - 'add_history(readline("prompt"));' - then - add_def FEAT_READLINE - add_def USE_EDITLINE - MYCPPFLAGS="$MYCPPFLAGS $readline_inc" - READLINE_LINK="$readline_lib -ledit" - fi - fi - - if [ "x$READLINE_LINK" = "x" ] && [ $try_readline = "1" ]; then - if test_code readline 'stdio.h readline/readline.h readline/history.h' \ - "$readline_inc" "$readline_lib -lreadline" \ - 'add_history(readline("prompt"));' - then - add_def FEAT_READLINE - MYCPPFLAGS="$MYCPPFLAGS $readline_inc" - READLINE_LINK="$readline_lib -lreadline" - fi - fi - - if [ "x$READLINE_LINK" = "x" ] && [ $try_readline = "1" ]; then - if test_code 'readline with -lncurses' \ - 'stdio.h readline/readline.h readline/history.h' \ - "$readline_inc" "$readline_lib $ncurses_lib -lreadline -lncurses" \ + if test_code editline 'stdio.h editline/readline.h' '' '-ledit' \ 'add_history(readline("prompt"));' then add_def FEAT_READLINE - MYCPPFLAGS="$MYCPPFLAGS $readline_inc" - READLINE_LINK="$readline_lib $ncurses_lib -lreadline -lncurses" + READLINE_LINK="-ledit" fi fi @@ -879,22 +896,28 @@ then HASH_OBJ="hash_nettle.o" HASH_LINK="$test_link" - LIBS="$LIBS $HASH_LINK" MYCPPFLAGS="$MYCPPFLAGS $test_cflags" add_def FEAT_SECHASH + + if test_code 'CMAC in nettle' 'nettle/cmac.h' "$test_cflags" "$test_link" \ + 'cmac128_update(NULL, NULL, NULL, 0, NULL);' + then + add_def HAVE_CMAC + EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_nettle.o" + EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS cmac_nettle.o" + fi fi fi if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nss = "1" ]; then test_cflags="`pkg_config --cflags nss`" - test_link="`pkg_config --libs-only-L nss` -lfreebl3" + test_link="`pkg_config --libs-only-L nss` -lfreebl3 -lnssutil3" if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \ "$test_cflags" "$test_link" \ 'NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512));' then HASH_OBJ="hash_nss.o" HASH_LINK="$test_link" - LIBS="$LIBS $HASH_LINK" MYCPPFLAGS="$MYCPPFLAGS $test_cflags" add_def FEAT_SECHASH fi @@ -906,12 +929,58 @@ then HASH_OBJ="hash_tomcrypt.o" HASH_LINK="-ltomcrypt" - LIBS="$LIBS $HASH_LINK" MYCPPFLAGS="$MYCPPFLAGS -I/usr/include/tomcrypt" add_def FEAT_SECHASH fi fi +EXTRA_OBJECTS="$EXTRA_OBJECTS $HASH_OBJ" +EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ" +LIBS="$LIBS $HASH_LINK" + +if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then + test_cflags="`pkg_config --cflags gnutls`" + test_link="`pkg_config --libs gnutls`" + if test_code 'gnutls' 'gnutls/gnutls.h' \ + "$test_cflags" "$test_link" ' + return gnutls_init(NULL, 0) + GNUTLS_TLS1_3 + + gnutls_priority_init2(NULL, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) + + gnutls_prf_rfc5705(NULL, 0, "", 0, "", 16, NULL);' + then + if test_code 'SIV in nettle' \ + 'nettle/siv-cmac.h' "" "$LIBS" \ + 'siv_cmac_aes128_set_key(NULL, NULL);' + then + EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o" + add_def HAVE_SIV + add_def HAVE_NETTLE_SIV_CMAC + else + if test_code 'SIV in gnutls' 'gnutls/gnutls.h' \ + "$test_cflags" "$test_link" ' + return gnutls_aead_cipher_init(NULL, GNUTLS_CIPHER_AES_128_SIV, NULL);' + then + EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o" + add_def HAVE_SIV + else + if test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \ + 'aes128_set_encrypt_key(NULL, NULL);' + then + EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o" + add_def HAVE_SIV + fi + fi + fi + + if grep '#define HAVE_SIV' config.h > /dev/null; then + EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o" + EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ntp_auth.o nts_ntp_client.o nts_ntp_server.o" + LIBS="$LIBS $test_link" + MYCPPFLAGS="$MYCPPFLAGS $test_cflags" + add_def FEAT_NTS + fi + fi +fi + if [ $use_pthread = "1" ]; then MYCFLAGS="$MYCFLAGS -pthread" fi @@ -981,7 +1050,7 @@ common_features="`get_features SECHASH IPV6 DEBUG`" chronyc_features="`get_features READLINE`" -chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SIGND ASYNCDNS`" +chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SIGND ASYNCDNS NTS`" add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\"" add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\"" echo "Features : $chronyd_features $chronyc_features $common_features" @@ -997,15 +1066,15 @@ for f in Makefile doc/Makefile test/unit/Makefile do echo Creating $f - sed -e "s%@EXTRA_OBJECTS@%${EXTRA_OBJECTS}%;\ + sed -e "s%@EXTRA_OBJS@%${EXTRA_OBJECTS}%;\ + s%@EXTRA_CLI_OBJS@%${EXTRA_CLI_OBJECTS}%;\ s%@CC@%${MYCC}%;\ s%@CFLAGS@%${MYCFLAGS}%;\ s%@CPPFLAGS@%${MYCPPFLAGS}%;\ - s%@LIBS@%${LIBS}%;\ s%@LDFLAGS@%${MYLDFLAGS}%;\ + s%@LIBS@%${LIBS}%;\ s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\ s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\ - s%@HASH_OBJ@%${HASH_OBJ}%;\ s%@SYSCONFDIR@%${SYSCONFDIR}%;\ s%@BINDIR@%${BINDIR}%;\ s%@SBINDIR@%${SBINDIR}%;\ diff -Nru chrony-3.5/debian/changelog chrony-4.1/debian/changelog --- chrony-3.5/debian/changelog 2020-04-20 13:58:52.000000000 +0000 +++ chrony-4.1/debian/changelog 2021-10-21 12:48:46.000000000 +0000 @@ -1,3 +1,696 @@ +chrony (2:4.1-1~simonarons) focal; urgency=medium + + * Import to focal. + + -- Simon Arons Thu, 21 Oct 2021 12:48:46 +0000 + +chrony (4.1-3ubuntu1) impish; urgency=medium + + * Merge with Debian unstable (LP: #1940252). Remaining changes: + Remaining changes: + - d/chrony.conf: use ubuntu ntp pool and server (LP 1744664 1754358) + - Set -x as default if unable to set time (e.g. in containers) (LP 1589780) + Chrony is a single service which acts as both NTP client (i.e. syncing the + local clock) and NTP server (i.e. providing NTP services to the network), + and that is both desired and expected in the vast majority of cases. + But in containers syncing the local clock is usually impossible, but this + shall not break the providing of NTP services to the network. + To some extent this makes chrony's default config more similar to 'ntpd', + which complained in syslog but still provided NTP server service in those + cases. + + debian/chrony.service: allow the service to run without CAP_SYS_TIME + + debian/control: add new dependency libcap2-bin for capsh (usually + installed anyway, but make them explicit to be sure). + + debian/chrony.default: new option SYNC_IN_CONTAINER to not fall back + (Default off) [fixed a minor typo in the comment in this update] + + debian/chronyd-starter.sh: wrapper to handle special cases in containers + and if CAP_SYS_TIME is missing. Effectively allows on to run NTP server + in containers on a default installation and avoid failing to sync time + (or if allowed to sync, avoid multiple containers to fight over it by + accident). + + debian/install: make chrony-starter.sh available on install. + + debian/docs, debian/README.container: provide documentation about the + handling of this case. + * Dropped changes: + - d/t/helper-functions: restart explicitly to fix test issues + * Added changes: + - d/p/lp-1940252-rtc-avoid-printing-and-scanning-time_t.patch: glibc 2.34 + compatibility + + -- Christian Ehrhardt Tue, 17 Aug 2021 12:22:32 +0200 + +chrony (4.1-3) unstable; urgency=medium + + * Upload to unstable. + + * debian/patches/: + - Add allow-clone3-and-pread64-in-seccomp-filter.patch. + + * debian/tests/upstream-simulation-test-suite: + - Update clknetsim version. + + -- Vincent Blut Sun, 15 Aug 2021 15:08:56 +0200 + +chrony (4.1-2) experimental; urgency=medium + + * Merge branch 'debian/unstable' into 'debian/latest'. + + * debian/rules: + - Remove needless dh_auto_test override. + + [ Christian Ehrhardt ] + * debian/tests/helper-functions: + - Improve chronyd restart logic. While rendering the tests more readable, + this also fixes ntp-server-and-nts-auth test on Ubuntu. + + [ Debian Janitor ] + * debian/{control,chrony.maintscript}: + - Remove constraints unnecessary since stretch. + + -- Vincent Blut Sat, 26 Jun 2021 17:16:45 +0200 + +chrony (4.1-1ubuntu1) impish; urgency=medium + + * Merge new upstream 4.1 and yet unrelased changes from Debian salsa. + Remaining changes: + - d/chrony.conf: use ubuntu ntp pool and server (LP 1744664 1754358) + - Set -x as default if unable to set time (e.g. in containers) (LP 1589780) + Chrony is a single service which acts as both NTP client (i.e. syncing the + local clock) and NTP server (i.e. providing NTP services to the network), + and that is both desired and expected in the vast majority of cases. + But in containers syncing the local clock is usually impossible, but this + shall not break the providing of NTP services to the network. + To some extent this makes chrony's default config more similar to 'ntpd', + which complained in syslog but still provided NTP server service in those + cases. + + debian/chrony.service: allow the service to run without CAP_SYS_TIME + + debian/control: add new dependency libcap2-bin for capsh (usually + installed anyway, but make them explicit to be sure). + + debian/chrony.default: new option SYNC_IN_CONTAINER to not fall back + (Default off) [fixed a minor typo in the comment in this update] + + debian/chronyd-starter.sh: wrapper to handle special cases in containers + and if CAP_SYS_TIME is missing. Effectively allows on to run NTP server + in containers on a default installation and avoid failing to sync time + (or if allowed to sync, avoid multiple containers to fight over it by + accident). + + debian/install: make chrony-starter.sh available on install. + + debian/docs, debian/README.container: provide documentation about the + handling of this case. + * Dropped changes: + - d/t/helper-functions: reduce default ubuntu config, to make space for + testcase config + [ in Debian 4.0-6 ] + - d/t/{dynamically-add-source,ntp-server-and-nts-auth,helper-functions}: + unify tests to use reload and restart + [ in Debian 4.0-6 ] + - d/t/upstream-simulation-test-suite: Update clknetsim version to fix + a test failure on s390x when LTO is enabled at build time (LP #1921377) + [ in Debian 4.1~pre1-1 ] + - d/p/lp-1915006-sys_linux-allow-statx-and-fstatat64-in-seccomp-filte.patch: + add compatibility for glibc 2.33 (LP: 1915006) + [ upstream in 4.1-pre1 ] + * Added changes: + - d/t/helper-functions: restart explicitly to fix test issues + + -- Christian Ehrhardt Tue, 18 May 2021 08:12:59 +0200 + +chrony (4.1-1) experimental; urgency=medium + + * Import upstream version 4.1: + - Please see /usr/share/doc/chrony/NEWS.gz for the release notes. + + * debian/copyright: + - Update copyright years. + + * debian/upstream/signing-key.asc: + - Update Miroslav Lichvar's key. + + * debian/watch: + - Add upstream version mangle to transform -pre to ~pre. + + -- Vincent Blut Fri, 14 May 2021 00:25:34 +0200 + +chrony (4.1~pre1-1) experimental; urgency=medium + + * Import upstream version 4.1-pre1: + - Please see /usr/share/doc/chrony/NEWS.gz for the release notes. + + * debian/control: + - Now that the upstream system tests support 'ss', replace the net-tools + build-dependency by iproute2. + + * debian/NEWS: + - Mention that lines in *.sources files must be terminated by a trailing + newline. + + * debian/patches/: + - Drop allow-IP_TOS-socket-option-in-seccomp-filter.patch. Applied + upstream. + + * debian/postinst: + - Do not redirect 'chronyd -p' stderr to /dev/null. Not needed anymore. + + * debian/sources.d/README: + - Mention that lines in *.sources files must be terminated by a trailing + newline. + + * debian/tests/dynamically-add-source: + - Terminate the line in dummy-server.sources with the newline control + character. + + * debian/tests/fragmented-configuration: + - Do not redirect 'chronyd -p' stderr to /dev/null. Not needed anymore. + + * debian/tests/upstream-simulation-test-suite: + - Update clknetsim version. + + -- Vincent Blut Mon, 10 May 2021 18:56:53 +0200 + +chrony (4.0-8) unstable; urgency=medium + + * debian/patches/: + - Add allow-BINDTODEVICE-option-in-seccomp-filter.patch to enable support + for binding sockets to a device without having to disable the seccomp + filter. + - Add allow-getuid32-in-seccomp-filter.patch. Upstream found out that + getuid32() needed to be allowed in the seccomp filter to enable some NTS + operations on i686. This may affect other 32-bits architectures. + + -- Vincent Blut Thu, 13 May 2021 16:51:41 +0200 + +chrony (4.0-7) unstable; urgency=medium + + * debian/patches/: + - Add allow-IP_TOS-socket-option-in-seccomp-filter.patch to enable the use + of the 'dscp' directive. + + -- Vincent Blut Thu, 08 Apr 2021 16:21:16 +0200 + +chrony (4.0-6) unstable; urgency=medium + + * debian/tests/helper-functions: + - Instead of running 'systemctl restart chrony.service', use + __restart_chronyd() in the __no_system_clock_control() function. + - Run 'sleep 3' only if chronyd has successfully restarted. + + [ Christian Ehrhardt ] + * debian/tests/{dynamically-add-source,ntp-server-and-nts-auth}: + - Reduce default Ubuntu config to make space for testcase config. + + * debian/tests/helper-functions: + - Add more common functions and update some tests to use them. + - Wait after restarting chronyd. Without this, some tests break on Ubuntu by + checking state too early. + + -- Vincent Blut Sun, 21 Feb 2021 21:59:22 +0100 + +chrony (4.0-5ubuntu3) hirsute; urgency=medium + + * d/t/upstream-simulation-test-suite: Update clknetsim version to fix + a test failure on s390x when LTO is enabled at build time (LP: #1921377) + + -- Christian Ehrhardt Thu, 25 Mar 2021 15:45:47 +0100 + +chrony (4.0-5ubuntu2) hirsute; urgency=medium + + * d/p/lp-1915006-sys_linux-allow-statx-and-fstatat64-in-seccomp-filte.patch: + add compatibility for glibc 2.33 (LP: 1915006) + + -- Christian Ehrhardt Mon, 15 Feb 2021 12:50:29 +0100 + +chrony (4.0-5ubuntu1) hirsute; urgency=medium + + * Merge with Debian unstable (LP: #1915006). Remaining changes: + - d/chrony.conf: use ubuntu ntp pool and server (LP 1744664 1754358) + - Set -x as default if unable to set time (e.g. in containers) (LP 1589780) + Chrony is a single service which acts as both NTP client (i.e. syncing the + local clock) and NTP server (i.e. providing NTP services to the network), + and that is both desired and expected in the vast majority of cases. + But in containers syncing the local clock is usually impossible, but this + shall not break the providing of NTP services to the network. + To some extent this makes chrony's default config more similar to 'ntpd', + which complained in syslog but still provided NTP server service in those + cases. + + debian/chrony.service: allow the service to run without CAP_SYS_TIME + + debian/control: add new dependency libcap2-bin for capsh (usually + installed anyway, but make them explicit to be sure). + + debian/chrony.default: new option SYNC_IN_CONTAINER to not fall back + (Default off) [fixed a minor typo in the comment in this update] + + debian/chronyd-starter.sh: wrapper to handle special cases in containers + and if CAP_SYS_TIME is missing. Effectively allows on to run NTP server + in containers on a default installation and avoid failing to sync time + (or if allowed to sync, avoid multiple containers to fight over it by + accident). + + debian/install: make chrony-starter.sh available on install. + + debian/docs, debian/README.container: provide documentation about the + handling of this case. + * Added changes: + - d/t/helper-functions: reduce default ubuntu config, to make space for + testcase config + - d/t/{dynamically-add-source,ntp-server-and-nts-auth,helper-functions}: + unify tests to use reload and restart + + -- Christian Ehrhardt Mon, 08 Feb 2021 12:45:05 +0100 + +chrony (4.0-5) unstable; urgency=medium + + * Follow DEP-14 branch naming conventions: + master -> debian/latest + upstream -> upstream/latest + + * debian/chrony.service: + - Enable some hardening settings. + + * debian/control: + - Remove Joachim Wiedorn from the Uploaders field. This decision was taken + in agreement with him. Thanks a lot, Joachim, for your work on chrony and + for your benevolence when you handed me its maintenance. + - Point Vcs-Git to the debian/latest branch. + + * debian/dirs: + - Do not create the /etc/apparmor.d/force-complain directory. Not needed + anymore. + + * debian/postrm: + - Remove /run/chrony-dhcp on purge. + + * debian/preinst: + - Drop old migration code snippet. It was used to put the newly provided + AppArmor profile in complain mode when upgrading chrony to prevent + regressions this profile could have caused. (Closes: #905485) + + -- Vincent Blut Thu, 04 Feb 2021 19:49:22 +0100 + +chrony (4.0-4) unstable; urgency=medium + + * debian/chrony.examples: + - Provide example configuration files. + + * debian/postinst: + - Run adduser unconditionally. + - Use 'chronyd -p' to check the whole configuration. + + * debian/tests/: + - Prevent dynamically-add-source and ntp-server-and-nts-auth tests from + failing on chronyd's preparation step. + - Don't pass 'set -u' to dynamically-add-source and + ntp-server-and-nts-auth scripts. + + * debian/tests/control: + - Mark dynamically-add-source as skippable. + + -- Vincent Blut Thu, 21 Jan 2021 20:02:39 +0100 + +chrony (4.0-3) unstable; urgency=medium + + * debian/: + - chronyd's configuration can now be fragmented. Please see + /etc/chrony/conf.d/README for more information. + - NTP sources can be specified in /etc/chrony/sources.d. Please see + /etc/chrony/sources.d/README for more information. + + * debian/chrony.conf: + - Include configuration files found in /etc/chrony/conf.d. + - Use NTP sources found in /etc/chrony/sources.d. + - Get TAI-UTC offset and leap seconds from the system tz database by using + the "leapsectz right/UTC" directive. This directive must be commented out + when using time sources serving leap-smeared time. (Closes: #974845) + - Add missing comment. + + * debian/chrony.default: + - Switch the seccomp filter to level 1. + + * debian/chrony.lintian-overrides: + - Override breakout-link. + + * debian/control: + - Add tzdata to the dependencies. + - Bump Standards-Version to 4.5.1 (no changes required). + + * debian/copyright: + - Update copyright year for debian/*. + + * debian/postinst: + - Use dpkg-statoverride to manage mode bits and ownership of + /var/l{ib,og}/chrony. + + * debian/postrm: + - Remove overrides for /var/l{ib,og}/chrony on purge. + + * debian/rules: + - Drop '--without-readline' option. GNU readline support has been dropped + upstream due to license incompatibility. + - Replace -F -1 by -F 1 in the sed invocation. + + * debian/tests/: + - Add fragmented-configuration autopkgtest. + - Add dynamically-add-source autopkgtest. + - Add ntp-server-and-nts-auth autopkgtest. + + * debian/tests/control: + - Mark ntp-server-and-nts-auth as skippable. + + * debian/tests/fragmented-configuration: + - Use another directive for the test since "leapsectz right/UTC" is now + used by default. + + * debian/tests/helper-functions: + - Add __no_system_clock_control() function. + + * debian/tests/upstream-simulation-test-suite: + - Always use the same seed to get deterministic results. + + * debian/upstream/metadata: + - Remove obsolete field Name. Thanks to Debian Janitor . + + * debian/usr.sbin.chronyd: + - Make use of the @{run} variable. + + -- Vincent Blut Mon, 18 Jan 2021 21:58:52 +0100 + +chrony (4.0-2ubuntu1) hirsute; urgency=medium + + * Merge with Debian unstable. Remaining changes: + - d/chrony.conf: use ubuntu ntp pool and server (LP 1744664 1754358) + - Set -x as default if unable to set time (e.g. in containers) (LP 1589780) + Chrony is a single service which acts as both NTP client (i.e. syncing the + local clock) and NTP server (i.e. providing NTP services to the network), + and that is both desired and expected in the vast majority of cases. + But in containers syncing the local clock is usually impossible, but this + shall not break the providing of NTP services to the network. + To some extent this makes chrony's default config more similar to 'ntpd', + which complained in syslog but still provided NTP server service in those + cases. + + debian/chrony.service: allow the service to run without CAP_SYS_TIME + + debian/control: add new dependency libcap2-bin for capsh (usually + installed anyway, but make them explicit to be sure). + + debian/chrony.default: new option SYNC_IN_CONTAINER to not fall back + (Default off) [fixed a minor typo in the comment in this update] + + debian/chronyd-starter.sh: wrapper to handle special cases in containers + and if CAP_SYS_TIME is missing. Effectively allows on to run NTP server + in containers on a default installation and avoid failing to sync time + (or if allowed to sync, avoid multiple containers to fight over it by + accident). + + debian/install: make chrony-starter.sh available on install. + + debian/docs, debian/README.container: provide documentation about the + handling of this case. + + -- Christian Ehrhardt Tue, 27 Oct 2020 10:55:19 +0100 + +chrony (4.0-2) unstable; urgency=medium + + * Merge branch 'experimental' into 'master'. + + * Upload to unstable. + + -- Vincent Blut Tue, 13 Oct 2020 15:59:33 +0200 + +chrony (4.0-1) experimental; urgency=medium + + * Import upstream version 4.0: + - This release adds support for the Network Time Security (NTS) + authentication mechanism (RFC 8915). + - Please see /usr/share/doc/chrony/NEWS.gz for the release notes. + + -- Vincent Blut Wed, 07 Oct 2020 19:14:51 +0200 + +chrony (4.0~pre4-2) experimental; urgency=medium + + * debian/postinst: + - Fix user and group ownership of "/var/lib/chrony" to allow chronyd + to write in it. This will also fix a regression in the 104-systemdirs + test. + + -- Vincent Blut Sat, 03 Oct 2020 11:20:02 +0200 + +chrony (4.0~pre4-1) experimental; urgency=medium + + * Import upstream version 4.0-pre4: + - Please see /usr/share/doc/chrony/NEWS.gz for the release notes. + + * Merge branch 'master' into experimental. (Closes: #970421) + + * debian/chrony.conf: + - Use NTP sources from /run/chrony-dhcp. + - Save NTS keys and cookies in /var/lib/chrony/. + + * debian/chrony-dnssrv@.service: + - Update "chrony-helper" path. + + * debian/chrony.dhcp: + - Save NTP servers from DHCP to /run/chrony-dhcp/$interface.sources. + + * debian/chrony.lintian-overrides: + - Override executable-in-usr-lib for NetworkManager dispatcher scripts. + - Update NetworkManager dispatcher script name. + + * debian/chrony.ppp.ip-{down,up}: + - Update PID file path. + + * debian/chrony.service: + - Update PID file path. + - Do not run 'chrony-helper update-daemon' after starting chronyd. Not + needed anymore. + + * debian/control: + - Build-depend on libgnutls28-dev to support NTS. + - Build-depend on gnutls-bin for the test suite. + - Bump debhelper-compat to 13. + + * debian/copyright: + - Update copyright years. + + * debian/dirs: + - Remove var/log/chrony as it will be created automatically if it doesn’t + exist. + + * debian/if-{post-down,up}: + - Update PID file path. + + * debian/init: + - Update PID file path. + - Drop the unnecessary '--remove pidfile' option from the stop target. + - Do not run 'chrony-helper update-daemon' after starting chronyd. Not + needed anymore. + + * debian/install: + - Move "chrony-helper" to "/usr/libexec/chrony". + + * debian/links: + - Update source and destination filenames. + + * debian/patches/: + - Drop patches applied upstream. + - Add nm-dispatcher-dhcp_Move-server_dir-to-run.patch. + + * debian/postinst: + - Drop migration code from pre-Stretch. + - Migrate NTP sources obtained from DHCP to /run/chrony-dhcp on upgrade + from chrony < 4.0~pre4-1. + - Remove staled PID file when upgrading from chrony < 4.0~pre4-1. + + * debian/rules: + - Change the default PID file location from /run to /run/chrony. + - Drop dh_missing --fail-missing. This is the default in debhelper 13. + - Enable seccomp support by default on riscv64. + - Update NetworkManager dispatcher script name from 20-chrony to + 20-chrony-onoffline. + - Add DHCP NetworkManager dispatcher script to allow chronyd to use + NTP sources obtained from NM's internal DHCP client. + + * debian/tests/: + - Add some helper functions. Some tests will be updated thereafter + to use them. + + * debian/tests/time-sources-from-dhcp-servers: + - Adapt to the new way of using time sources from DHCP. + - Improve sed invocation. + + * debian/tests/upstream-simulation-test-suite: + - Update clknetsim version. + - Cosmetic changes. + + * debian/tests/upstream-system-tests: + - No need to stop systemd-timesyncd anymore since it is no more + co-installable with chrony anymore. + + * debian/usr.sbin.chronyd: + - Update PID file path. + - Add dac_override and dac_read_search capabilities to give "root" the + ability to write the PID file in /run/chrony/. + - Prefix flag definition by "flags=". + - Sort the capabilities. + - Grant CAP_NET_RAW capability to allow an NTP socket to be bound to a + device using the SO_BINDTODEVICE socket option on kernels before 5.7. + - Add comments regarding capabilities. + - Let chronyd create /var/l{ib,og}/chrony. + - Remove a superfluous rule. + - Allow reading of NTP sources in /run/chrony-dhcp/. + + * debian/watch: + - Make use of special strings. + + -- Vincent Blut Fri, 02 Oct 2020 21:21:08 +0200 + +chrony (3.5.1-1ubuntu2) groovy; urgency=medium + + * d/chronyd-starter.sh: fix commandline argument parsing (LP: #1898000) + + -- Christian Ehrhardt Tue, 06 Oct 2020 12:20:40 +0200 + +chrony (3.5.1-1ubuntu1) groovy; urgency=medium + + * Merge with Debian unstable. Remaining changes: + - d/chrony.conf: use ubuntu ntp pool and server (LP 1744664 1754358) + - Set -x as default if unable to set time (e.g. in containers) (LP 1589780) + Chrony is a single service which acts as both NTP client (i.e. syncing the + local clock) and NTP server (i.e. providing NTP services to the network), + and that is both desired and expected in the vast majority of cases. + But in containers syncing the local clock is usually impossible, but this + shall not break the providing of NTP services to the network. + To some extent this makes chrony's default config more similar to 'ntpd', + which complained in syslog but still provided NTP server service in those + cases. + + debian/chrony.service: allow the service to run without CAP_SYS_TIME + + debian/control: add new dependency libcap2-bin for capsh (usually + installed anyway, but make them explicit to be sure). + + debian/chrony.default: new option SYNC_IN_CONTAINER to not fall back + (Default off) [fixed a minor typo in the comment in this update] + + debian/chronyd-starter.sh: wrapper to handle special cases in containers + and if CAP_SYS_TIME is missing. Effectively allows to run NTP server in + containers on a default installation and avoid failing to sync time (or + if allowed to sync, avoid multiple containers to fight over it by + accident). + + debian/install: make chrony-starter.sh available on install. + + debian/docs, debian/README.container: provide documentation about the + handling of this case. + * Dropped changes + - d/t/control: harden time-sources-from-dhcp-servers test for systemd change + (LP: 1873031) [no more needed with recent systemd that is in groovy] + + -- Christian Ehrhardt Wed, 26 Aug 2020 15:30:48 +0200 + +chrony (3.5.1-1) unstable; urgency=medium + + * Import upstream version 3.5.1: + - Please see /usr/share/doc/chrony/NEWS.gz for the release notes. + - CVE-2020-14367: create new file when writing pidfile. + + * debian/chrony.lintian-overrides: + - Remove unused override. + + [ Ville Skyttä ] + * debian/chrony.conf: + - Comment spelling fix. (MR: !5) + + -- Vincent Blut Thu, 20 Aug 2020 14:07:22 +0200 + +chrony (3.5-9ubuntu2) groovy; urgency=medium + + * No change rebuild against new libnettle8 and libhogweed6 ABI. + + -- Dimitri John Ledkov Mon, 29 Jun 2020 22:22:19 +0100 + +chrony (3.5-9ubuntu1) groovy; urgency=medium + + * Merge with Debian unstable (LP: #1878005). Remaining changes: + - d/chrony.conf: use ubuntu ntp pool and server (LP 1744664 1754358) + - Set -x as default if unable to set time (e.g. in containers) (LP 1589780) + Chrony is a single service which acts as both NTP client (i.e. syncing the + local clock) and NTP server (i.e. providing NTP services to the network), + and that is both desired and expected in the vast majority of cases. + But in containers syncing the local clock is usually impossible, but this + shall not break the providing of NTP services to the network. + To some extent this makes chrony's default config more similar to 'ntpd', + which complained in syslog but still provided NTP server service in those + cases. + + debian/chrony.service: allow the service to run without CAP_SYS_TIME + + debian/control: add new dependency libcap2-bin for capsh (usually + installed anyway, but make them explicit to be sure). + + debian/chrony.default: new option SYNC_IN_CONTAINER to not fall back + (Default off) [fixed a minor typo in the comment in this update] + + debian/chronyd-starter.sh: wrapper to handle special cases in containers + and if CAP_SYS_TIME is missing. Effectively allows to run NTP server in + containers on a default installation and avoid failing to sync time (or + if allowed to sync, avoid multiple containers to fight over it by + accident). + + debian/install: make chrony-starter.sh available on install. + + debian/docs, debian/README.container: provide documentation about the + handling of this case. + - d/t/control: harden time-sources-from-dhcp-servers test for systemd change + (LP: 1873031) + * Dropped changes [in Debian now] + - d/t/upstream-system-tests: stop chrony/systemd-timesynd before tests + - d/t/upstream-system-tests: fix stderr in case services do not exist + - Stop starting systemd-timesyncd in postrm. This is no longer relevant + since systemd-timesyncd is a standalone package declaring + Conflicts/Replaces/Provides: time-daemon. (Closes 955773, LP: 1872183) + - d/postrm: Reinstate the remove target (LP: 1873810) + + -- Christian Ehrhardt Wed, 20 May 2020 09:57:39 +0200 + +chrony (3.5-9) unstable; urgency=medium + + * debian/patches/: + - Add allow-some-*time64-syscalls-in-seccomp-filter.patch. Needed for + 32-bit architectures with new system calls using 64-bit time_t. + (LP: #1878005) + + * debian/tests/control: + - Add needs-internet restriction to the upstream-simulation-test-suite + test. + + [ Christian Ehrhardt ] + * debian/tests/upstream-simulation-test-suite: + - Skip if preparation steps fail. + - Make preparation steps more verbose. + + -- Vincent Blut Tue, 19 May 2020 16:42:18 +0200 + +chrony (3.5-8) unstable; urgency=medium + + * debian/postrm: + - Stop starting systemd-timesyncd in postrm. This is no longer relevant + since systemd-timesyncd is a standalone package declaring + Conflicts/Replaces/Provides: time-daemon. (Closes: #955773) + + [ Christian Ehrhardt ] + * debian/tests/upstream-system-tests: + - Stop chrony/systemd-timesynd before running these tests. (LP: #1870144) + + -- Vincent Blut Sun, 05 Apr 2020 17:44:31 +0200 + +chrony (3.5-7) unstable; urgency=medium + + * debian/chrony.maintscript: + - Remove the /etc/NetworkManager/dispatcher.d/20-chrony conffile. + + * debian/control: + - Support seccomp facility on riscv64. It should be noted that the system + call filter will stay disabled by default on this architecture until + Linux >= 5.5 hits unstable. + - Bump libseccomp-dev build-dep to 2.4.3-1~ to provide seccomp facility on + riscv64. + - Break network-manager (<< 1.20.0-1~). Prior to this version, + NetworkManager would not look for dispatcher scripts into + /usr/lib/NetworkManager/dispatcher.d/. + + * debian/dirs: + - Create the usr/lib/NetworkManager/dispatcher.d subdirectories. + + * debian/links: + - Change the location of the NetworkManager dispatcher script. + + * debian/patches/: + - Add allow-renameat2-in-seccomp-filter.patch. Required as the riscv64 + architecture does not support the rename() and renameat() system calls. + + * debian/rules: + - Move the NetworkManager dispatcher script in + /usr/lib/NetworkManager/dispatcher.d/. + + -- Vincent Blut Tue, 17 Mar 2020 15:21:53 +0100 + chrony (3.5-6ubuntu6) focal; urgency=medium * d/postrm: Reinstate the remove target (LP: #1873810) diff -Nru chrony-3.5/debian/chrony.conf chrony-4.1/debian/chrony.conf --- chrony-3.5/debian/chrony.conf 2020-04-15 06:59:45.000000000 +0000 +++ chrony-4.1/debian/chrony.conf 2021-10-21 12:48:46.000000000 +0000 @@ -1,5 +1,8 @@ # Welcome to the chrony configuration file. See chrony.conf(5) for more -# information about usuable directives. +# information about usable directives. + +# Include configuration files found in /etc/chrony/conf.d. +confdir /etc/chrony/conf.d # This will use (up to): # - 4 sources from ntp.ubuntu.com which some are ipv6 enabled @@ -14,10 +17,20 @@ # About using servers from the NTP Pool Project in general see (LP: #104525). # Approved by Ubuntu Technical Board on 2011-02-08. # See http://www.pool.ntp.org/join.html for more information. -pool ntp.ubuntu.com iburst maxsources 4 -pool 0.ubuntu.pool.ntp.org iburst maxsources 1 -pool 1.ubuntu.pool.ntp.org iburst maxsources 1 -pool 2.ubuntu.pool.ntp.org iburst maxsources 2 +server ntp1.sptime.se iburst +server ntp2.sptime.se iburst +server ntp3.sptime.se iburst + +#pool ntp.ubuntu.com iburst maxsources 4 +#pool 0.ubuntu.pool.ntp.org iburst maxsources 1 +#pool 1.ubuntu.pool.ntp.org iburst maxsources 1 +#pool 2.ubuntu.pool.ntp.org iburst maxsources 2 + +# Use time sources from DHCP. +sourcedir /run/chrony-dhcp + +# Use NTP sources found in /etc/chrony/sources.d. +sourcedir /etc/chrony/sources.d # This directive specify the location of the file containing ID/key pairs for # NTP authentication. @@ -27,6 +40,9 @@ # information. driftfile /var/lib/chrony/chrony.drift +# Save NTS keys and cookies. +ntsdumpdir /var/lib/chrony + # Uncomment the following line to turn logging on. #log tracking measurements statistics @@ -43,3 +59,8 @@ # Step the system clock instead of slewing it if the adjustment is larger than # one second, but only in the first three clock updates. makestep 1 3 + +# Get TAI-UTC offset and leap seconds from the system tz database. +# This directive must be commented out when using time sources serving +# leap-smeared time. +leapsectz right/UTC diff -Nru chrony-3.5/debian/chrony.default chrony-4.1/debian/chrony.default --- chrony-3.5/debian/chrony.default 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/chrony.default 2021-08-17 10:22:16.000000000 +0000 @@ -3,7 +3,7 @@ # the chrony daemon without editing the init script or service file. # Options to pass to chrony. -DAEMON_OPTS="-F -1" +DAEMON_OPTS="-F 1" # Sync system clock in containers or without CAP_SYS_TIME (likely to fail) # See /usr/share/doc/chrony/README.container for details. diff -Nru chrony-3.5/debian/chrony.dhcp chrony-4.1/debian/chrony.dhcp --- chrony-3.5/debian/chrony.dhcp 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/chrony.dhcp 2021-05-03 14:51:16.000000000 +0000 @@ -1,17 +1,19 @@ -SERVERFILE=/var/lib/dhcp/chrony.servers.$interface +CHRONY_SOURCEDIR=/run/chrony-dhcp +SERVERFILE=$CHRONY_SOURCEDIR/$interface.sources chrony_config() { rm -f "$SERVERFILE" + mkdir -p "$CHRONY_SOURCEDIR" for server in $new_ntp_servers; do - echo "$server iburst" >> "$SERVERFILE" + echo "server $server iburst" >> "$SERVERFILE" done - /usr/lib/chrony/chrony-helper update-daemon || : + /usr/bin/chronyc reload sources > /dev/null 2>&1 || : } chrony_restore() { if [ -f "$SERVERFILE" ]; then rm -f "$SERVERFILE" - /usr/lib/chrony/chrony-helper update-daemon || : + /usr/bin/chronyc reload sources > /dev/null 2>&1 || : fi } diff -Nru chrony-3.5/debian/chrony-dnssrv@.service chrony-4.1/debian/chrony-dnssrv@.service --- chrony-3.5/debian/chrony-dnssrv@.service 2020-04-01 14:24:09.000000000 +0000 +++ chrony-4.1/debian/chrony-dnssrv@.service 2021-05-03 14:51:16.000000000 +0000 @@ -5,7 +5,7 @@ [Service] Type=oneshot -ExecStart=/usr/lib/chrony/chrony-helper update-dnssrv-servers %I +ExecStart=/usr/libexec/chrony/chrony-helper update-dnssrv-servers %I ProtectSystem=strict PrivateDevices=yes ProtectHome=yes diff -Nru chrony-3.5/debian/chronyd-starter.sh chrony-4.1/debian/chronyd-starter.sh --- chrony-3.5/debian/chronyd-starter.sh 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/chronyd-starter.sh 2021-08-17 10:22:16.000000000 +0000 @@ -25,12 +25,10 @@ # Check if -x is already set manually, don't process further if that is the case X_SET=0 -while getopts ":x" opt; do - case $opt in - x) - X_SET=1 - ;; - esac +for arg in $@; do + if echo "$arg" | grep -q -e '^-[a-zA-Z0-9]*x'; then + X_SET=1 + fi done if [ ${X_SET} -ne 1 ]; then diff -Nru chrony-3.5/debian/chrony.examples chrony-4.1/debian/chrony.examples --- chrony-3.5/debian/chrony.examples 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/debian/chrony.examples 2021-05-03 14:51:16.000000000 +0000 @@ -0,0 +1 @@ +examples/chrony.conf* diff -Nru chrony-3.5/debian/chrony.if-post-down chrony-4.1/debian/chrony.if-post-down --- chrony-3.5/debian/chrony.if-post-down 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/chrony.if-post-down 2021-05-03 14:51:16.000000000 +0000 @@ -4,7 +4,7 @@ [ -x /usr/sbin/chronyd ] || exit 0 -if [ -e /run/chronyd.pid ]; then +if [ -e /run/chrony/chronyd.pid ]; then chronyc onoffline > /dev/null 2>&1 fi diff -Nru chrony-3.5/debian/chrony.if-up chrony-4.1/debian/chrony.if-up --- chrony-3.5/debian/chrony.if-up 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/chrony.if-up 2021-05-03 14:51:16.000000000 +0000 @@ -4,7 +4,7 @@ [ -x /usr/sbin/chronyd ] || exit 0 -if [ -e /run/chronyd.pid ]; then +if [ -e /run/chrony/chronyd.pid ]; then chronyc onoffline > /dev/null 2>&1 fi diff -Nru chrony-3.5/debian/chrony.lintian-overrides chrony-4.1/debian/chrony.lintian-overrides --- chrony-3.5/debian/chrony.lintian-overrides 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/chrony.lintian-overrides 2021-05-03 14:51:16.000000000 +0000 @@ -2,6 +2,10 @@ # symmetric keys used for NTP authentication. chrony: non-standard-file-perm usr/share/chrony/chrony.keys 0640 != 0644 -# False positive. chrony-dnssrv@.service isn’t a daemon but a oneshot service, -# not started at boot, whose role is to lookup for _ntp._udp DNS SRV records. -chrony: package-supports-alternative-init-but-no-init.d-script lib/systemd/system/chrony-dnssrv@.service +# NetworkManager does not execute dispatcher scripts in /usr/libexec. +chrony: executable-in-usr-lib usr/lib/NetworkManager/dispatcher.d/20-chrony-dhcp +chrony: executable-in-usr-lib usr/lib/NetworkManager/dispatcher.d/20-chrony-onoffline + +# Being architecture-independent, these symlinks should be harmless. +chrony: breakout-link usr/lib/networkd-dispatcher/off.d/chrony-onoffline -> usr/lib/NetworkManager/dispatcher.d/20-chrony-onoffline +chrony: breakout-link usr/lib/networkd-dispatcher/routable.d/chrony-onoffline -> usr/lib/NetworkManager/dispatcher.d/20-chrony-onoffline diff -Nru chrony-3.5/debian/chrony.maintscript chrony-4.1/debian/chrony.maintscript --- chrony-3.5/debian/chrony.maintscript 2020-04-15 06:59:45.000000000 +0000 +++ chrony-4.1/debian/chrony.maintscript 2021-08-17 10:22:15.000000000 +0000 @@ -1 +1 @@ -rm_conffile /etc/apm/event.d/01chrony 2.4.1-3~ chrony +rm_conffile /etc/NetworkManager/dispatcher.d/20-chrony 3.5-7~ chrony diff -Nru chrony-3.5/debian/chrony.ppp.ip-down chrony-4.1/debian/chrony.ppp.ip-down --- chrony-3.5/debian/chrony.ppp.ip-down 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/chrony.ppp.ip-down 2021-05-03 14:51:16.000000000 +0000 @@ -6,7 +6,7 @@ # were in the public domain. I waive all rights. # Modified by Vincent Blut -if [ -e /run/chronyd.pid ]; then +if [ -e /run/chrony/chronyd.pid ]; then chronyc onoffline > /dev/null 2>&1 fi diff -Nru chrony-3.5/debian/chrony.ppp.ip-up chrony-4.1/debian/chrony.ppp.ip-up --- chrony-3.5/debian/chrony.ppp.ip-up 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/chrony.ppp.ip-up 2021-05-03 14:51:16.000000000 +0000 @@ -5,7 +5,7 @@ # were in the public domain. I waive all rights. # Modified by Vincent Blut -if [ -e /run/chronyd.pid ]; then +if [ -e /run/chrony/chronyd.pid ]; then chronyc onoffline > /dev/null 2>&1 fi diff -Nru chrony-3.5/debian/chrony.service chrony-4.1/debian/chrony.service --- chrony-3.5/debian/chrony.service 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/chrony.service 2021-08-17 10:22:16.000000000 +0000 @@ -8,14 +8,15 @@ [Service] Type=forking -PIDFile=/run/chronyd.pid +PIDFile=/run/chrony/chronyd.pid EnvironmentFile=-/etc/default/chrony -# Starter takes care of special cases mostly for containers ExecStart=/usr/lib/systemd/scripts/chronyd-starter.sh $DAEMON_OPTS -ExecStartPost=-/usr/lib/chrony/chrony-helper update-daemon PrivateTmp=yes ProtectHome=yes ProtectSystem=full +ProtectControlGroups=yes +ProtectKernelModules=yes +ProtectKernelTunables=yes [Install] Alias=chronyd.service diff -Nru chrony-3.5/debian/conf.d/README chrony-4.1/debian/conf.d/README --- chrony-3.5/debian/conf.d/README 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/debian/conf.d/README 2021-05-03 14:51:16.000000000 +0000 @@ -0,0 +1,7 @@ +Files found under the /etc/chrony/conf.d directory with the .conf suffix are +parsed in the lexicographical order of the file names when chronyd starts up. +This enables a fragmented configuration of chronyd. + +Although those files can contain any directives listed in chrony.conf(5), +it would be wiser to add NTP sources in the /etc/chrony/sources.d +directory. Please read /etc/chrony/sources.d/README for more information. diff -Nru chrony-3.5/debian/control chrony-4.1/debian/control --- chrony-3.5/debian/control 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/control 2021-10-21 12:48:42.000000000 +0000 @@ -3,22 +3,23 @@ Priority: optional Maintainer: Ubuntu Developers XSBC-Original-Maintainer: Vincent Blut -Uploaders: Joachim Wiedorn -Standards-Version: 4.5.0 -Build-Depends: asciidoctor (>= 1.5.3-1~), +Standards-Version: 4.5.1 +Build-Depends: asciidoctor, bison, debhelper-compat (= 12), dh-apparmor, + gnutls-bin , + iproute2 [linux-any] , libcap-dev [linux-any], libedit-dev, - libseccomp-dev (>= 2.2.3-3~) [amd64 arm64 armel armhf hppa i386 mips mipsel mips64el powerpc powerpcspe ppc64 ppc64el s390x x32], - net-tools , + libgnutls28-dev, + libseccomp-dev (>= 2.4.3-1~) [amd64 arm64 armel armhf hppa i386 mips mipsel mips64el powerpc powerpcspe ppc64 ppc64el riscv64 s390x x32], nettle-dev, pkg-config, - pps-tools (>= 0.20120406+g0deb9c7e-2) [linux-any], + pps-tools [linux-any], procps Homepage: https://chrony.tuxfamily.org -Vcs-Git: https://salsa.debian.org/debian/chrony.git +Vcs-Git: https://salsa.debian.org/debian/chrony.git -b debian/latest Vcs-Browser: https://salsa.debian.org/debian/chrony Rules-Requires-Root: no @@ -28,11 +29,13 @@ Depends: adduser, iproute2 [linux-any], libcap2-bin (>= 1:2.32-1), + tzdata, ucf, ${misc:Depends}, ${shlibs:Depends} Suggests: dnsutils, networkd-dispatcher +Breaks: network-manager (<< 1.20.0-1~) Conflicts: time-daemon Provides: time-daemon Replaces: time-daemon diff -Nru chrony-3.5/debian/copyright chrony-4.1/debian/copyright --- chrony-3.5/debian/copyright 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/copyright 2021-05-18 06:12:12.000000000 +0000 @@ -4,25 +4,25 @@ Source: https://download.tuxfamily.org/chrony/ Files: * -Copyright: 2009-2018, Miroslav Lichvar +Copyright: 2009-2021, Miroslav Lichvar 1997-2007, Richard P. Curnow License: GPL-2 Files: main.c sys_linux.c -Copyright: 2009-2018, Miroslav Lichvar +Copyright: 2012-2020, Miroslav Lichvar 2009, John G. Hasler 1997-2003, Richard P. Curnow License: GPL-2 Files: ntp_io.c -Copyright: 2009, 2013-2016, 2018, Miroslav Lichvar +Copyright: 2009, 2013-2016, 2018-2020, Miroslav Lichvar 2009, Timo Teras 1997-2003, Richard P. Curnow License: GPL-2 Files: sys_macosx.? -Copyright: 2015, 2017, Bryan Christianson +Copyright: 2015, 2017, 2020, Bryan Christianson 2001, J. Hannken-Illjes 1997-2001, Richard P. Curnow License: GPL-2 @@ -33,7 +33,7 @@ License: GPL-2 Files: debian/* -Copyright: 2015-2020, Vincent Blut +Copyright: 2015-2021, Vincent Blut 2012-2014, Joachim Wiedorn 2000-2012, John Hasler License: GPL-2 @@ -56,7 +56,7 @@ License: GPL-2 Files: test/unit/* -Copyright: 2016-2018, Miroslav Lichvar +Copyright: 2016-2021, Miroslav Lichvar License: GPL-2 Files: hwclock.? @@ -74,27 +74,27 @@ Files: client.c Copyright: 1997-2003, Richard P. Curnow 2016, Lonnie Abelbeck - 2009-2019, Miroslav Lichvar + 2009-2021, Miroslav Lichvar License: GPL-2 Files: configure Copyright: 1997-2003, Richard P. Curnow 2016, Bryan Christianson - 2009, 2012-2018, Miroslav Lichvar + 2009, 2012-2021, Miroslav Lichvar 2019, Stefan R. Filipek License: GPL-2 Files: doc/chrony.conf.adoc Copyright: 1997-2003, Richard P. Curnow 2016, Stephen Wadeley - 2009-2017, Miroslav Lichvar + 2009-2021, Miroslav Lichvar 2017, Bryan Christianson License: GPL-2 Files: doc/chronyc.adoc Copyright: 1997-2003, Richard P. Curnow 2016, Stephen Wadeley - 2009-2017, Miroslav Lichvar + 2009-2020, Miroslav Lichvar License: GPL-2 Files: refclock.c @@ -117,7 +117,7 @@ Files: sourcestats.c Copyright: 1997-2003, Richard P. Curnow - 2011-2014, 2016-2018, Miroslav Lichvar + 2011-2014, 2016-2018, 2021, Miroslav Lichvar License: GPL-2 Files: stubs.c diff -Nru chrony-3.5/debian/dirs chrony-4.1/debian/dirs --- chrony-3.5/debian/dirs 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/dirs 2021-05-03 14:51:16.000000000 +0000 @@ -1,8 +1,6 @@ -etc/NetworkManager/dispatcher.d -etc/apparmor.d/force-complain etc/chrony etc/logrotate.d etc/ppp/ip-down.d etc/ppp/ip-up.d +usr/lib/NetworkManager/dispatcher.d var/lib/chrony -var/log/chrony diff -Nru chrony-3.5/debian/init chrony-4.1/debian/init --- chrony-3.5/debian/init 2020-04-01 14:24:09.000000000 +0000 +++ chrony-4.1/debian/init 2021-05-03 14:51:16.000000000 +0000 @@ -22,8 +22,7 @@ DAEMON=/usr/sbin/chronyd NAME="chronyd" DESC="time daemon" -PIDFILE=/run/chronyd.pid -CHRONY_HELPER=/usr/lib/chrony/chrony-helper +PIDFILE=/run/chrony/chronyd.pid [ -x "$DAEMON" ] || exit 0 @@ -42,16 +41,13 @@ else log_daemon_msg "Starting $DESC" "$NAME" start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- $DAEMON_OPTS - if [ -x $CHRONY_HELPER ]; then - $CHRONY_HELPER update-daemon - fi log_end_msg $? fi ;; stop) log_daemon_msg "Stopping $DESC" "$NAME" - start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE --remove-pidfile --exec $DAEMON + start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE --exec $DAEMON log_end_msg $? ;; diff -Nru chrony-3.5/debian/install chrony-4.1/debian/install --- chrony-3.5/debian/install 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/install 2021-08-17 10:22:17.000000000 +0000 @@ -1,6 +1,8 @@ debian/chrony-dnssrv@.* lib/systemd/system -debian/chrony-helper usr/lib/chrony +debian/chrony-helper usr/libexec/chrony debian/chrony.conf usr/share/chrony +debian/conf.d etc/chrony debian/ntp-units.d/50-chrony.list usr/lib/systemd/ntp-units.d +debian/sources.d etc/chrony debian/usr.sbin.chronyd etc/apparmor.d debian/chronyd-starter.sh usr/lib/systemd/scripts/ diff -Nru chrony-3.5/debian/links chrony-4.1/debian/links --- chrony-3.5/debian/links 2020-04-15 06:59:45.000000000 +0000 +++ chrony-4.1/debian/links 2021-05-03 14:51:16.000000000 +0000 @@ -1,5 +1,5 @@ # Update sources in response to systemd-networkd events (LP: #1718227). # This is reusing the NetworkManager dispatch script which has no hard # dependency to NetworkManager (not using any of its arguments) -etc/NetworkManager/dispatcher.d/20-chrony usr/lib/networkd-dispatcher/routable.d/chrony -etc/NetworkManager/dispatcher.d/20-chrony usr/lib/networkd-dispatcher/off.d/chrony +usr/lib/NetworkManager/dispatcher.d/20-chrony-onoffline usr/lib/networkd-dispatcher/routable.d/chrony-onoffline +usr/lib/NetworkManager/dispatcher.d/20-chrony-onoffline usr/lib/networkd-dispatcher/off.d/chrony-onoffline diff -Nru chrony-3.5/debian/NEWS chrony-4.1/debian/NEWS --- chrony-3.5/debian/NEWS 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/NEWS 2021-05-18 06:12:12.000000000 +0000 @@ -1,3 +1,15 @@ +chrony (4.1~pre1-1) experimental; urgency=medium + + Starting with chrony 4.0, it is possible to specify NTP sources in files + having the .sources suffix. In Debian, these files can be placed in + /etc/chrony/sources.d/ or any other directories specified by the 'sourcedir' + directive. + I would like to draw your attention that with chrony 4.1, each line of a + source file *must* be terminated by a trailing newline. Failing that, the NTP + source(s) will be ignored. + + -- Vincent Blut Sun, 25 Apr 2021 12:44:09 +0200 + chrony (3.4-2) unstable; urgency=medium To reduce the range of operations available to chronyd, and thereby decrease diff -Nru chrony-3.5/debian/patches/allow-clock_adjtime-in-seccomp-filter.patch chrony-4.1/debian/patches/allow-clock_adjtime-in-seccomp-filter.patch --- chrony-3.5/debian/patches/allow-clock_adjtime-in-seccomp-filter.patch 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/patches/allow-clock_adjtime-in-seccomp-filter.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -From 0cf506c92967c84f9ed83ba9e1be946a7fda6425 Mon Sep 17 00:00:00 2001 -From: Miroslav Lichvar -Date: Mon, 2 Dec 2019 12:47:13 +0100 -Subject: sys_linux: allow clock_adjtime in seccomp filter - -The adjtimex() function in glibc was switched to the clock_adjtime -system call. - ---- a/sys_linux.c -+++ b/sys_linux.c -@@ -478,8 +478,8 @@ SYS_Linux_EnableSystemCallFilter(int lev - { - const int syscalls[] = { - /* Clock */ -- SCMP_SYS(adjtimex), SCMP_SYS(clock_gettime), SCMP_SYS(gettimeofday), -- SCMP_SYS(settimeofday), SCMP_SYS(time), -+ SCMP_SYS(adjtimex), SCMP_SYS(clock_adjtime), SCMP_SYS(clock_gettime), -+ SCMP_SYS(gettimeofday), SCMP_SYS(settimeofday), SCMP_SYS(time), - /* Process */ - SCMP_SYS(clone), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(getpid), - SCMP_SYS(getrlimit), SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigreturn), diff -Nru chrony-3.5/debian/patches/allow-clone3-and-pread64-in-seccomp-filter.patch chrony-4.1/debian/patches/allow-clone3-and-pread64-in-seccomp-filter.patch --- chrony-3.5/debian/patches/allow-clone3-and-pread64-in-seccomp-filter.patch 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/debian/patches/allow-clone3-and-pread64-in-seccomp-filter.patch 2021-08-17 10:22:15.000000000 +0000 @@ -0,0 +1,32 @@ +From bbbd80bf03223f181d4abf5c8e5fe6136ab6129a Mon Sep 17 00:00:00 2001 +From: Miroslav Lichvar +Date: Mon, 9 Aug 2021 11:48:21 +0200 +Subject: sys_linux: allow clone3 and pread64 in seccomp filter + +These seem to be needed with the latest glibc. + +diff --git a/sys_linux.c b/sys_linux.c +index 50c0843..2b53f72 100644 +--- a/sys_linux.c ++++ b/sys_linux.c +@@ -503,6 +503,9 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context) + + /* Process */ + SCMP_SYS(clone), ++#ifdef __NR_clone3 ++ SCMP_SYS(clone3), ++#endif + SCMP_SYS(exit), + SCMP_SYS(exit_group), + SCMP_SYS(getpid), +@@ -595,6 +598,7 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context) + #ifdef __NR_ppoll_time64 + SCMP_SYS(ppoll_time64), + #endif ++ SCMP_SYS(pread64), + SCMP_SYS(pselect6), + #ifdef __NR_pselect6_time64 + SCMP_SYS(pselect6_time64), +-- +cgit v0.10.2 + diff -Nru chrony-3.5/debian/patches/lp-1940252-rtc-avoid-printing-and-scanning-time_t.patch chrony-4.1/debian/patches/lp-1940252-rtc-avoid-printing-and-scanning-time_t.patch --- chrony-3.5/debian/patches/lp-1940252-rtc-avoid-printing-and-scanning-time_t.patch 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/debian/patches/lp-1940252-rtc-avoid-printing-and-scanning-time_t.patch 2021-08-17 10:22:32.000000000 +0000 @@ -0,0 +1,56 @@ +From f27d719a4e2e983aea23058a29729a200b070dc6 Mon Sep 17 00:00:00 2001 +From: Miroslav Lichvar +Date: Thu, 5 Aug 2021 14:07:17 +0200 +Subject: [PATCH] rtc: avoid printing and scanning time_t + +With the latest glibc it's now possible to define _TIME_BITS=64 to get +64-bit time_t on 32-bit Linux systems. This breaks the %ld printf/scanf +modifier used with the RTC drift timestamp. Process it as a double. + +Origin: upstream, https://git.tuxfamily.org/chrony/chrony.git/commit/?id=f27d719a4e +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1940252 +Last-Update: 2021-08-17 + +--- + rtc_linux.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/rtc_linux.c b/rtc_linux.c +index 08c2a19f..c95c06e3 100644 +--- a/rtc_linux.c ++++ b/rtc_linux.c +@@ -434,6 +434,7 @@ setup_config(void) + static void + read_coefs_from_file(void) + { ++ double ref_time; + FILE *in; + + if (!tried_to_load_coefs) { +@@ -444,11 +445,12 @@ read_coefs_from_file(void) + + if (coefs_file_name && + (in = UTI_OpenFile(NULL, coefs_file_name, NULL, 'r', 0))) { +- if (fscanf(in, "%d%ld%lf%lf", ++ if (fscanf(in, "%d%lf%lf%lf", + &valid_coefs_from_file, +- &file_ref_time, ++ &ref_time, + &file_ref_offset, + &file_rate_ppm) == 4) { ++ file_ref_time = ref_time; + } else { + LOG(LOGS_WARN, "Could not read coefficients from %s", coefs_file_name); + } +@@ -472,7 +474,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate) + return RTC_ST_BADFILE; + + /* Gain rate is written out in ppm */ +- fprintf(out, "%1d %ld %.6f %.3f\n", valid, ref_time, offset, 1.0e6 * rate); ++ fprintf(out, "%1d %.0f %.6f %.3f\n", valid, (double)ref_time, offset, 1.0e6 * rate); + fclose(out); + + /* Rename the temporary file to the correct location */ +-- +2.32.0 + diff -Nru chrony-3.5/debian/patches/nm-dispatcher-dhcp_Move-server_dir-to-run.patch chrony-4.1/debian/patches/nm-dispatcher-dhcp_Move-server_dir-to-run.patch --- chrony-3.5/debian/patches/nm-dispatcher-dhcp_Move-server_dir-to-run.patch 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/debian/patches/nm-dispatcher-dhcp_Move-server_dir-to-run.patch 2021-05-03 14:51:16.000000000 +0000 @@ -0,0 +1,17 @@ +Description: Move server_dir path to /run +Author: Vincent Blut +Forwarded: no +Last-Update: 2020-09-16 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/examples/chrony.nm-dispatcher.dhcp ++++ b/examples/chrony.nm-dispatcher.dhcp +@@ -11,7 +11,7 @@ action=$2 + + chronyc=/usr/bin/chronyc + default_server_options=iburst +-server_dir=/var/run/chrony-dhcp ++server_dir=/run/chrony-dhcp + + dhcp_server_file=$server_dir/$interface.sources + # DHCP4_NTP_SERVERS is passed from DHCP options by NetworkManager. diff -Nru chrony-3.5/debian/patches/rtc-disable-interrupts-in-finalization.patch chrony-4.1/debian/patches/rtc-disable-interrupts-in-finalization.patch --- chrony-3.5/debian/patches/rtc-disable-interrupts-in-finalization.patch 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/patches/rtc-disable-interrupts-in-finalization.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -From f5eb7daf2087d52e3a55f9c081ff318588e287e8 Mon Sep 17 00:00:00 2001 -From: Miroslav Lichvar -Date: Tue, 10 Dec 2019 17:42:34 +0100 -Subject: rtc: disable interrupts in finalization - -Don't leave interrupts enabled if chronyd is stopped when making an RTC -measurement. - -diff --git a/rtc_linux.c b/rtc_linux.c -index fad95b5..aee768f 100644 ---- a/rtc_linux.c -+++ b/rtc_linux.c -@@ -566,6 +566,7 @@ RTC_Linux_Finalise(void) - /* Remove input file handler */ - if (fd >= 0) { - SCH_RemoveFileHandler(fd); -+ switch_interrupts(0); - close(fd); - - /* Save the RTC data */ --- -cgit v0.10.2 - diff -Nru chrony-3.5/debian/patches/rtc-do-not-finalize-driver-if-initialization-failed.patch chrony-4.1/debian/patches/rtc-do-not-finalize-driver-if-initialization-failed.patch --- chrony-3.5/debian/patches/rtc-do-not-finalize-driver-if-initialization-failed.patch 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/patches/rtc-do-not-finalize-driver-if-initialization-failed.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -From a57e1eb542b2a79245d3bf242bb531b186c7c0b6 Mon Sep 17 00:00:00 2001 -From: Miroslav Lichvar -Date: Tue, 10 Dec 2019 17:30:42 +0100 -Subject: rtc: don't finalize driver if initialization failed - - -diff --git a/rtc.c b/rtc.c -index 33d04bf..fa83173 100644 ---- a/rtc.c -+++ b/rtc.c -@@ -160,7 +160,7 @@ RTC_Initialise(int initial_set) - void - RTC_Finalise(void) - { -- if (driver.fini) { -+ if (driver_initialised) { - (driver.fini)(); - } - } -diff --git a/rtc_linux.c b/rtc_linux.c -index a88368b..f5bc9bd 100644 ---- a/rtc_linux.c -+++ b/rtc_linux.c -@@ -505,6 +505,17 @@ switch_interrupts(int on_off) - int - RTC_Linux_Initialise(void) - { -+ /* Try to open the device */ -+ fd = open(CNF_GetRtcDevice(), O_RDWR); -+ if (fd < 0) { -+ LOG(LOGS_ERR, "Could not open RTC device %s : %s", -+ CNF_GetRtcDevice(), strerror(errno)); -+ return 0; -+ } -+ -+ /* Close on exec */ -+ UTI_FdSetCloexec(fd); -+ - rtc_sec = MallocArray(time_t, MAX_SAMPLES); - rtc_trim = MallocArray(double, MAX_SAMPLES); - system_times = MallocArray(struct timespec, MAX_SAMPLES); -@@ -515,18 +526,6 @@ RTC_Linux_Initialise(void) - /* In case it didn't get done by pre-init */ - coefs_file_name = CNF_GetRtcFile(); - -- /* Try to open device */ -- -- fd = open (CNF_GetRtcDevice(), O_RDWR); -- if (fd < 0) { -- LOG(LOGS_ERR, "Could not open RTC device %s : %s", -- CNF_GetRtcDevice(), strerror(errno)); -- return 0; -- } -- -- /* Close on exec */ -- UTI_FdSetCloexec(fd); -- - n_samples = 0; - n_samples_since_regression = 0; - n_runs = 0; --- -cgit v0.10.2 - diff -Nru chrony-3.5/debian/patches/rtc-extend-check-for-RTCs-that-do-not-support-interrupts.patch chrony-4.1/debian/patches/rtc-extend-check-for-RTCs-that-do-not-support-interrupts.patch --- chrony-3.5/debian/patches/rtc-extend-check-for-RTCs-that-do-not-support-interrupts.patch 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/patches/rtc-extend-check-for-RTCs-that-do-not-support-interrupts.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -From bff3f51d13c3f41e2ead2cfff5bfe0b8c22ef44a Mon Sep 17 00:00:00 2001 -From: Christian Ehrhardt -Date: Thu, 12 Dec 2019 12:06:40 +0100 -Subject: rtc: extend check for RTCs that don't support interrupts -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Several RTCs would only expose the broken behavior on enabling -interrupts. The reason for that is that the kernel only returns the -error if the state changes. Therefore the check has to probe -switch_interrupts(1) as well. - -On platforms that work it will be switched on and off, while on those it -never works it will just stay off. - -Clocks known to expose that behavior include, but are not limited to: -PPC64# dmesg | grep -i rtc    -[    0.241872] rtc-generic rtc-generic: registered as rtc0 -[    0.270221] rtc-generic rtc-generic: setting system clock to ... -ARM64# dmesg | grep -i rtc -[    0.876198] rtc-efi rtc-efi: registered as rtc0 -[    1.046869] rtc-efi rtc-efi: setting system clock to ... - -Signed-off-by: Christian Ehrhardt - -diff --git a/rtc_linux.c b/rtc_linux.c -index aee768f..a44a912 100644 ---- a/rtc_linux.c -+++ b/rtc_linux.c -@@ -516,7 +516,7 @@ RTC_Linux_Initialise(void) - } - - /* Make sure the RTC supports interrupts */ -- if (!switch_interrupts(0)) { -+ if (!switch_interrupts(1) || !switch_interrupts(0)) { - close(fd); - return 0; - } --- -cgit v0.10.2 - diff -Nru chrony-3.5/debian/patches/rtc-handle-RTCs-that-do-not-support-interrupts.patch chrony-4.1/debian/patches/rtc-handle-RTCs-that-do-not-support-interrupts.patch --- chrony-3.5/debian/patches/rtc-handle-RTCs-that-do-not-support-interrupts.patch 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/patches/rtc-handle-RTCs-that-do-not-support-interrupts.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -From d66b2f2b2423bfbd3de4d69895024dac7eefb306 Mon Sep 17 00:00:00 2001 -From: Miroslav Lichvar -Date: Tue, 10 Dec 2019 17:33:17 +0100 -Subject: rtc: handle RTCs that don't support interrupts - -Some RTCs supported by the Linux kernel don't support the RTC_UIE_ON/OFF -ioctls, which causes chronyd started with the -s option to get stuck in -the initial RTC mode. - -After opening the RTC device in the initialization, return error if -the ioctls are not supported to prevent the upper layer from calling the -time_init() function and expecting it to finish. - -diff --git a/rtc_linux.c b/rtc_linux.c -index f5bc9bd..fad95b5 100644 ---- a/rtc_linux.c -+++ b/rtc_linux.c -@@ -484,17 +484,19 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate) - - /* ================================================== */ - --static void -+static int - switch_interrupts(int on_off) - { - if (ioctl(fd, on_off ? RTC_UIE_ON : RTC_UIE_OFF, 0) < 0) { - LOG(LOGS_ERR, "Could not %s RTC interrupt : %s", - on_off ? "enable" : "disable", strerror(errno)); -- return; -+ return 0; - } - - if (on_off) - skip_interrupts = 1; -+ -+ return 1; - } - - /* ================================================== */ -@@ -513,6 +515,12 @@ RTC_Linux_Initialise(void) - return 0; - } - -+ /* Make sure the RTC supports interrupts */ -+ if (!switch_interrupts(0)) { -+ close(fd); -+ return 0; -+ } -+ - /* Close on exec */ - UTI_FdSetCloexec(fd); - --- -cgit v0.10.2 - diff -Nru chrony-3.5/debian/patches/rtc-simplify-and-move-switch_interrupts-function.patch chrony-4.1/debian/patches/rtc-simplify-and-move-switch_interrupts-function.patch --- chrony-3.5/debian/patches/rtc-simplify-and-move-switch_interrupts-function.patch 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/patches/rtc-simplify-and-move-switch_interrupts-function.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,64 +0,0 @@ -From 25bdee7a0e6f1dcc6e89034c91cec474fbe8196e Mon Sep 17 00:00:00 2001 -From: Miroslav Lichvar -Date: Tue, 10 Dec 2019 17:40:44 +0100 -Subject: rtc: simplify and move switch_interrupts() - - -diff --git a/rtc_linux.c b/rtc_linux.c -index e6fedd8..a88368b 100644 ---- a/rtc_linux.c -+++ b/rtc_linux.c -@@ -482,6 +482,20 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate) - return RTC_ST_OK; - } - -+/* ================================================== */ -+ -+static void -+switch_interrupts(int on_off) -+{ -+ if (ioctl(fd, on_off ? RTC_UIE_ON : RTC_UIE_OFF, 0) < 0) { -+ LOG(LOGS_ERR, "Could not %s RTC interrupt : %s", -+ on_off ? "enable" : "disable", strerror(errno)); -+ return; -+ } -+ -+ if (on_off) -+ skip_interrupts = 1; -+} - - /* ================================================== */ - /* file_name is the name of the file where we save the RTC params -@@ -559,29 +573,6 @@ RTC_Linux_Finalise(void) - /* ================================================== */ - - static void --switch_interrupts(int onoff) --{ -- int status; -- -- if (onoff) { -- status = ioctl(fd, RTC_UIE_ON, 0); -- if (status < 0) { -- LOG(LOGS_ERR, "Could not %s RTC interrupt : %s", "enable", strerror(errno)); -- return; -- } -- skip_interrupts = 1; -- } else { -- status = ioctl(fd, RTC_UIE_OFF, 0); -- if (status < 0) { -- LOG(LOGS_ERR, "Could not %s RTC interrupt : %s", "disable", strerror(errno)); -- return; -- } -- } --} -- --/* ================================================== */ -- --static void - measurement_timeout(void *any) - { - timeout_id = 0; --- -cgit v0.10.2 - diff -Nru chrony-3.5/debian/patches/series chrony-4.1/debian/patches/series --- chrony-3.5/debian/patches/series 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/patches/series 2021-08-17 10:22:32.000000000 +0000 @@ -1,8 +1,3 @@ -allow-clock_adjtime-in-seccomp-filter.patch -update_processing_of_packet_log.patch -rtc-simplify-and-move-switch_interrupts-function.patch -rtc-do-not-finalize-driver-if-initialization-failed.patch -rtc-handle-RTCs-that-do-not-support-interrupts.patch -rtc-disable-interrupts-in-finalization.patch -rtc-extend-check-for-RTCs-that-do-not-support-interrupts.patch -test-accept-test-result-if-RTC-can-not-enable-RTC_UIE_ON.patch +nm-dispatcher-dhcp_Move-server_dir-to-run.patch +allow-clone3-and-pread64-in-seccomp-filter.patch +lp-1940252-rtc-avoid-printing-and-scanning-time_t.patch diff -Nru chrony-3.5/debian/patches/test-accept-test-result-if-RTC-can-not-enable-RTC_UIE_ON.patch chrony-4.1/debian/patches/test-accept-test-result-if-RTC-can-not-enable-RTC_UIE_ON.patch --- chrony-3.5/debian/patches/test-accept-test-result-if-RTC-can-not-enable-RTC_UIE_ON.patch 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/patches/test-accept-test-result-if-RTC-can-not-enable-RTC_UIE_ON.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -From c4d6f98bed890b4c8fc28d76b2c994f65b6d0eb7 Mon Sep 17 00:00:00 2001 -From: Christian Ehrhardt -Date: Thu, 12 Dec 2019 12:06:39 +0100 -Subject: test: accept test result if RTC can't enable RTC_UIE_ON - -The test might run on different platforms. If the platform happens -to have a RTC that does exist but unable to have RTC_UIE_ON set the -test will fail, while the chrony code is actually good. - -Examples of bad clocks are: -- ppc64el: rtc-generic -- arm64: rtc-efi - -To avoid that extend the log message check on 101-rtc to accept -that condition as a valid test result as well. - -Signed-off-by: Christian Ehrhardt - -diff --git a/test/system/101-rtc b/test/system/101-rtc -index fa9a70d..68bce68 100755 ---- a/test/system/101-rtc -+++ b/test/system/101-rtc -@@ -14,6 +14,6 @@ echo "1 $(date +%s) 0.0 0.0" > "$TEST_DIR/rtcfile" - - start_chronyd || test_fail - stop_chronyd || test_fail --check_chronyd_message_count "\(clock off from RTC\|RTC time before last\)" 1 1 || test_fail -+check_chronyd_message_count "\(clock off from RTC\|RTC time before last\|Could not \(enable\|disable\) RTC interrupt\)" 1 1 || test_fail - - test_pass --- -cgit v0.10.2 - diff -Nru chrony-3.5/debian/patches/update_processing_of_packet_log.patch chrony-4.1/debian/patches/update_processing_of_packet_log.patch --- chrony-3.5/debian/patches/update_processing_of_packet_log.patch 2020-04-16 06:19:10.000000000 +0000 +++ chrony-4.1/debian/patches/update_processing_of_packet_log.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -From 62d6aed6a64b887c9e3b7f03d9e0db1deaa2696a Mon Sep 17 00:00:00 2001 -From: Miroslav Lichvar -Date: Tue, 18 Jun 2019 15:41:50 +0200 -Subject: test: update processing of packet log - -Two new fields have been added to the packet log, which broke some -of the simulation tests. - -Applied-Upstream: https://git.tuxfamily.org/chrony/chrony.git/commit/?id=62d6aed6a64b887c9e3b7f03d9e0db1deaa2696a ---- a/test/simulation/test.common -+++ b/test/simulation/test.common -@@ -391,9 +391,9 @@ check_packet_port() { - for i in $(seq 1 $(get_chronyd_nodes)); do - test_message 3 0 "node $i:" - -- grep -E -q " $port [0-9]+\$" tmp/log.packets && \ -+ grep -E -q "^([0-9e.+-]+ ){5}$port " tmp/log.packets && \ - ! grep -E "^[0-9e.+-]+ $i " tmp/log.packets | \ -- grep -E -q -v " $port [0-9]+\$" && \ -+ grep -E -q -v "^([0-9e.+-]+ ){5}$port " && \ - test_ok || test_bad - [ $? -eq 0 ] || ret=1 - done diff -Nru chrony-3.5/debian/postinst chrony-4.1/debian/postinst --- chrony-3.5/debian/postinst 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/postinst 2021-05-03 14:51:16.000000000 +0000 @@ -11,27 +11,13 @@ case "$1" in configure) - if ! getent passwd _chrony > /dev/null 2>&1 - then - echo "Creating '_chrony' system user/group for the chronyd daemon…" - adduser --force-badname \ - --system \ - --group \ - --quiet \ - --gecos "Chrony daemon" \ - --home /var/lib/chrony \ - --no-create-home _chrony - fi - - # Change the owner of "/var/l{ib,og}/chrony" directories and their - # subfiles to "_chrony" only if the user has not set the "user" - # directive in chrony.conf - if ! grep "^user" /etc/chrony/chrony.conf > /dev/null 2>&1; then - chown _chrony:_chrony /var/lib/chrony - if [ -d /var/log/chrony ]; then - chown _chrony:_chrony /var/log/chrony - fi - fi + adduser --force-badname \ + --system \ + --group \ + --quiet \ + --gecos "Chrony daemon" \ + --home /var/lib/chrony \ + --no-create-home _chrony if command -v ucf >/dev/null then @@ -42,6 +28,32 @@ ucfr chrony /etc/chrony/chrony.keys fi fi + + # Change the user and group ownership of "/var/l{ib,og}/chrony" iif + # the chronyd's configuration does not contain the "user" directive. + # Also, update these directories' mode bits to 0750 to follow upstream. + if ! chronyd -p | grep -q "^user"; then + for d in /var/lib/chrony /var/log/chrony; do + if ! dpkg-statoverride --list "$d" >/dev/null; then + dpkg-statoverride --update --add _chrony _chrony 0750 "$d" + fi + done + fi + + if [ -n "$2" ] && dpkg --compare-versions "$2" lt 4.0~pre4-1; then + # Migrate NTP sources obtained from DHCP to /run/chrony-dhcp + mkdir -p /run/chrony-dhcp + for file in $(find /var/lib/dhcp/ -type f -name "chrony.servers.*"); do + sed 's/.*/server &/' < "$file" > /run/chrony-dhcp/"${file##*servers.}.sources" + done + + # Remove the staled PID file resulting from migrating its path from + # /run to /run/chrony/. Overriding dh_installinit and + # dh_systemd_start to use the --no-restart-after-upgrade option + # was a possibility but chronyd would have been down even longer + # during the upgrade. + rm -f /run/chronyd.pid + fi ;; abort-upgrade|abort-remove|abort-deconfigure) diff -Nru chrony-3.5/debian/postrm chrony-4.1/debian/postrm --- chrony-3.5/debian/postrm 2020-04-20 13:57:00.000000000 +0000 +++ chrony-4.1/debian/postrm 2021-05-03 14:51:16.000000000 +0000 @@ -23,6 +23,7 @@ fi rm -rf /etc/chrony rm -rf /run/chrony || true + rm -rf /run/chrony-dhcp || true rm -rf /var/lib/chrony rm -rf /var/log/chrony # Remove "_chrony" system user/group @@ -30,6 +31,12 @@ then deluser --quiet --system _chrony > /dev/null 2>&1 || true fi + + for d in /var/lib/chrony /var/log/chrony; do + if dpkg-statoverride --list "$d" >/dev/null; then + dpkg-statoverride --remove "$d" + fi + done ;; remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) diff -Nru chrony-3.5/debian/preinst chrony-4.1/debian/preinst --- chrony-3.5/debian/preinst 2020-04-01 14:24:09.000000000 +0000 +++ chrony-4.1/debian/preinst 2021-05-03 14:51:16.000000000 +0000 @@ -9,14 +9,6 @@ case "$1" in upgrade) - APP_PROFILE="usr.sbin.chronyd" - APP_CONFFILE="/etc/apparmor.d/$APP_PROFILE" - APP_COMPLAIN="/etc/apparmor.d/force-complain/$APP_PROFILE" - # force-complain on upgrade from pre-shipped profile - if dpkg --compare-versions "$2" lt "3.2-2" ; then - mkdir -p `dirname "$APP_COMPLAIN"` 2>/dev/null || true - ln -sf "$APP_CONFFILE" "$APP_COMPLAIN" - fi ;; install|abort-upgrade) diff -Nru chrony-3.5/debian/README.container chrony-4.1/debian/README.container --- chrony-3.5/debian/README.container 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/README.container 2021-08-17 10:22:17.000000000 +0000 @@ -1,7 +1,7 @@ Chrony in Containers -------------------- -Currently in in 99.9+% of the cases syncing the local clock in a container +Currently in 99.9+% of the cases syncing the local clock in a container is wrong. Most of the time it will be unable to do so, because it is lacking CAP_SYS_TIME. Or worse, if the CAP_SYS_TIME privilege is granted, multiple containers could fight over the system's time, because the Linux kernel does diff -Nru chrony-3.5/debian/rules chrony-4.1/debian/rules --- chrony-3.5/debian/rules 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/rules 2021-08-17 10:22:15.000000000 +0000 @@ -15,20 +15,20 @@ override_dh_auto_configure: dh_auto_configure -- --mandir=/usr/share/man \ --sysconfdir=/etc/chrony \ - --without-readline \ --with-user=_chrony \ --enable-scfilter \ --chronyrundir=/run/chrony \ --with-ntp-era=$(shell date -d '1970-01-01 00:00:00+00:00' +'%s') \ --enable-ntp-signd \ --with-hwclockfile=/etc/adjtime \ - --with-pidfile=/run/chronyd.pid \ + --with-pidfile=/run/chrony/chronyd.pid \ --host-system=Linux override_dh_install: dh_install install -m 0640 -t $(BASE)/usr/share/chrony/ debian/chrony.keys - install -m 0755 -T examples/chrony.nm-dispatcher $(BASE)/etc/NetworkManager/dispatcher.d/20-chrony + install -m 0755 -T examples/chrony.nm-dispatcher.dhcp ${BASE}/usr/lib/NetworkManager/dispatcher.d/20-chrony-dhcp + install -m 0755 -T examples/chrony.nm-dispatcher.onoffline $(BASE)/usr/lib/NetworkManager/dispatcher.d/20-chrony-onoffline install -m 0644 -T examples/chrony.logrotate $(BASE)/etc/logrotate.d/chrony dh_apparmor --profile-name=usr.sbin.chronyd -pchrony install -D -p -m 0644 debian/chrony.dhcp $(BASE)/etc/dhcp/dhclient-exit-hooks.d/chrony @@ -40,14 +40,6 @@ dh_installinit # Disable the system call filter on architectures mentioned below # due to missing support in libseccomp and/or in the Linux kernel. -ifneq (,$(filter $(DEB_HOST_ARCH), alpha ia64 m68k riscv64 sh4 sparc64)) - sed -i '/DAEMON_OPTS=/s/"-F -1"/""/' $(BASE)/etc/default/chrony +ifneq (,$(filter $(DEB_HOST_ARCH), alpha ia64 m68k sh4 sparc64)) + sed -i '/DAEMON_OPTS=/s/"-F 1"/""/' $(BASE)/etc/default/chrony endif - -override_dh_auto_test: -ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS))) - dh_auto_test -endif - -override_dh_missing: - dh_missing --fail-missing diff -Nru chrony-3.5/debian/sources.d/README chrony-4.1/debian/sources.d/README --- chrony-3.5/debian/sources.d/README 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/debian/sources.d/README 2021-05-18 06:12:12.000000000 +0000 @@ -0,0 +1,12 @@ +Only NTP sources can be specified in the /etc/chrony/sources.d directory. +Files in this directory must end with the ".sources" suffix. They can only +contain the "peer", "pool" and "server" directives and must have all +lines terminated by a trailing newline. + +There is no need to restart chronyd for these time sources to be usable, +running 'chronyc reload sources' is sufficient. + +Example: + +# echo 'server 192.0.2.1 iburst' > /etc/chrony/sources.d/local-ntp-server.sources +# chronyc reload sources diff -Nru chrony-3.5/debian/tests/control chrony-4.1/debian/tests/control --- chrony-3.5/debian/tests/control 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/tests/control 2021-05-03 14:51:16.000000000 +0000 @@ -1,11 +1,10 @@ Tests: upstream-simulation-test-suite Depends: @builddeps@, build-essential, ca-certificates, wget -Restrictions: isolation-container, build-needed, skippable, needs-root +Restrictions: isolation-container, build-needed, skippable, needs-root, needs-internet Tests: time-sources-from-dhcp-servers Depends: @, isc-dhcp-server, isc-dhcp-client, iproute2, kmod -# allow-stderr is needed until systemd fixed LP: 1873031 -Restrictions: isolation-machine, needs-root, allow-stderr +Restrictions: isolation-machine, needs-root Features: test-name=run_system_tests Test-Command: debian/tests/upstream-system-tests @@ -16,3 +15,14 @@ Test-Command: debian/tests/upstream-system-tests -d 1[0-9][0-9]-* Depends: @, @builddeps@, ethtool Restrictions: build-needed, isolation-machine, needs-root + +Tests: fragmented-configuration +Restrictions: isolation-container, needs-root + +Tests: dynamically-add-source +Depends: @, dpkg-dev +Restrictions: isolation-container, needs-root, skippable + +Tests: ntp-server-and-nts-auth +Depends: @, dpkg-dev, gnutls-bin +Restrictions: isolation-container, needs-root, skippable diff -Nru chrony-3.5/debian/tests/dynamically-add-source chrony-4.1/debian/tests/dynamically-add-source --- chrony-3.5/debian/tests/dynamically-add-source 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/debian/tests/dynamically-add-source 2021-08-17 10:18:41.000000000 +0000 @@ -0,0 +1,27 @@ +#!/bin/sh +# Make sure that NTP sources from /etc/chrony/sources.d are usable. + +set -e + +. debian/tests/helper-functions + +server_addr="192.0.2.1" + +printf "Preparing chronyd configuration: " +__no_system_clock_control +__restart_chronyd && __test_ok || __test_skip + +printf "Adding a dummy server to the list of NTP sources: " +printf "server $server_addr\n" > /etc/chrony/sources.d/dummy-server.sources && __test_ok || __test_fail + +printf "Reloading NTP sources: " +__reload_sources + +printf "Checking for dummy server availability: " +__check_sources "$server_addr" + +printf "Checking for dummy server availability after restarting chronyd: " +__restart_chronyd +__check_sources "$server_addr" + +exit 0 diff -Nru chrony-3.5/debian/tests/fragmented-configuration chrony-4.1/debian/tests/fragmented-configuration --- chrony-3.5/debian/tests/fragmented-configuration 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/debian/tests/fragmented-configuration 2021-05-03 14:51:16.000000000 +0000 @@ -0,0 +1,17 @@ +#!/bin/sh +# Make sure that fragmented configuration works as expected. + +set -e + +. debian/tests/helper-functions + +printf 'Setting "authselectmode prefer" as authentication policy: ' +echo "authselectmode prefer" > /etc/chrony/conf.d/authentication-policy.conf && __test_ok || __test_fail + +printf "Restart chronyd: " +systemctl --quiet restart chrony.service && __test_ok || __test_fail + +printf "Checking that chronyd uses the defined authentication policy: " +chronyd -p | grep -q "authselectmode prefer" && __test_ok || __test_fail + +exit 0 diff -Nru chrony-3.5/debian/tests/helper-functions chrony-4.1/debian/tests/helper-functions --- chrony-3.5/debian/tests/helper-functions 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/debian/tests/helper-functions 2021-08-17 10:18:41.000000000 +0000 @@ -0,0 +1,53 @@ +__no_system_clock_control() { + if ! dpkg-vendor --derives-from Ubuntu; then + sed -i '/^DAEMON_OPTS=/s/"\(.*\)"/"\1 -x"/' /etc/default/chrony + mkdir -p /etc/systemd/system/chrony.service.d + cat < /etc/systemd/system/chrony.service.d/override.conf +[Unit] +ConditionCapability= +EOF + systemctl daemon-reload + fi +} + +__test_fail() { + printf 'FAIL\n' >&2 + return 1 +} + +__test_ok() { + printf 'OK\n' + return 0 +} + +__test_skip() { + [ -n "$1" ] && printf 'SKIP: (%s)\n' "$1" || printf 'SKIP\n' + exit 77 +} + +__reload_sources() { + chronyc reload sources > /dev/null 2>&1 && __test_ok || __test_fail +} + +__restart_chronyd() { + systemctl --quiet restart chrony.service + rc=$? + sleep 3 + return $rc +} + +__check_sources() { + chronyc sources | grep -q "$1" && __test_ok || __test_fail +} + +__check_auth() { + chronyc -c authdata | grep -q "$1" && __test_ok || __test_fail +} + +# Ubuntu's default config is fully populated causing issues with the test +# If any of those tests run on Ubuntu, clear some and restart the daemon +# to pick this up before entering the tests. +if grep -q "^pool.*ubuntu.pool.ntp.org" /etc/chrony/chrony.conf; then + sudo sed -i -e '/^pool.*ubuntu.pool.ntp.org/d' /etc/chrony/chrony.conf + __restart_chronyd +fi diff -Nru chrony-3.5/debian/tests/ntp-server-and-nts-auth chrony-4.1/debian/tests/ntp-server-and-nts-auth --- chrony-3.5/debian/tests/ntp-server-and-nts-auth 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/debian/tests/ntp-server-and-nts-auth 2021-08-17 10:18:41.000000000 +0000 @@ -0,0 +1,59 @@ +#!/bin/sh +# Check that chronyd is able to authenticate NTP packets when NTS is enabled +# on the server. + +set -e + +. debian/tests/helper-functions + +cert_dir="/var/lib/chrony" +cert_template="$cert_dir/cert.cfg" +cert_file="$cert_dir/server.crt" +priv_key="$cert_dir/server.key" +server_addr="127.0.1.1" +server_name="chrony-nts-test" + +create_cert_template() { + printf "Creating certificate template: " + cat < "$cert_template" +cn = "$server_name" +serial = 001 +activation_date = "$(date -d '1 year ago' +'%Y-%m-%d') 00:00:00 UTC" +expiration_date = "$(date -d '1 year' +'%Y-%m-%d') 00:00:00 UTC" +signing_key +encryption_key +EOF +} + +generate_cert() { + printf "Generating self-signed certificate: " + certtool --generate-privkey --key-type=ed25519 --outfile "$priv_key" > /dev/null 2>&1 + certtool --generate-self-signed --load-privkey "$priv_key" --template "$cert_template" \ + --outfile "$cert_file" > /dev/null 2>&1 +} + +server_config() { + printf "Preparing chronyd configuration: " + cat < /etc/chrony/conf.d/local-server-config.conf +server $server_name nts minpoll -6 maxpoll -6 +ntsserverkey $priv_key +ntsservercert $cert_file +ntstrustedcerts $cert_file +EOF + + __no_system_clock_control + __restart_chronyd +} + +echo "$server_addr $server_name" >> /etc/hosts + +create_cert_template && __test_ok || __test_skip "unable to create certificate template" + +generate_cert && __test_ok || __test_skip "unable to generate self-signed certificate" + +server_config && __test_ok || __test_skip + +printf "Checking if server authenticates NTP packets: " +__check_auth "$server_addr,NTS" + +exit 0 diff -Nru chrony-3.5/debian/tests/time-sources-from-dhcp-servers chrony-4.1/debian/tests/time-sources-from-dhcp-servers --- chrony-3.5/debian/tests/time-sources-from-dhcp-servers 2020-04-01 14:24:09.000000000 +0000 +++ chrony-4.1/debian/tests/time-sources-from-dhcp-servers 2021-05-03 14:51:16.000000000 +0000 @@ -4,8 +4,6 @@ set -e -added_servers="/run/chrony-helper/added_servers" - prepare_iface() { modprobe dummy ip link add name dummy0 type dummy @@ -27,7 +25,11 @@ } EOF -sed -i 's/INTERFACESv4=""/INTERFACESv4="dummy0"/' /etc/default/isc-dhcp-server +sed -i '/INTERFACESv4=/s/".*"/"dummy0"/' /etc/default/isc-dhcp-server +} + +chk_time_src() { + chronyc -n sources | grep -q -F '192.168.1.50' } printf "Preparing the dummy network interface and dhcpd configuration…\n" @@ -36,7 +38,7 @@ fi printf "Check if the NTP server is made available to chronyd…\n" -grep -q 192.168.1.50 $added_servers && printf "SUCCESS!\n\n" +chk_time_src && printf "SUCCESS!\n\n" printf "Release the current lease and check if the NTP server has been correctly removed…\n" -dhclient -r dummy0 > /dev/null 2>&1 && [ ! -d "$added_servers" ] && printf "SUCCESS!\n\n" +dhclient -r dummy0 > /dev/null 2>&1 && ! chk_time_src && printf "SUCCESS!\n\n" diff -Nru chrony-3.5/debian/tests/upstream-simulation-test-suite chrony-4.1/debian/tests/upstream-simulation-test-suite --- chrony-3.5/debian/tests/upstream-simulation-test-suite 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/tests/upstream-simulation-test-suite 2021-08-17 10:22:15.000000000 +0000 @@ -1,18 +1,21 @@ #!/bin/sh -#Upstream makes use of “clknetsim” to test how well “chronyd” controls the -#system clocks in various conditions. Due to “clknetsim” not being available -#in Debian, let’s use autopkgtest facility to build it in a container and -#test “chronyd” from there. +# Upstream makes use of “clknetsim” to test how well “chronyd” controls the +# system clocks in various conditions. Due to “clknetsim” not being available +# in Debian, let’s use autopkgtest facility to build it in a container and +# test “chronyd” from there. set -e testdir="$PWD/test/simulation" -clknetsim_ver=79ffe44 +clknetsim_ver=153a8af clknetsim_src=https://github.com/mlichvar/clknetsim/archive/"$clknetsim_ver"/clknetsim-"$clknetsim_ver".tar.gz clknetsim_archive=$(basename "$clknetsim_src") export CLKNETSIM_PATH="$AUTOPKGTEST_TMP" +# Always use the same seed to get deterministic results +export CLKNETSIM_RANDOM_SEED=24505 + DEB_HOST_MULTIARCH=$(dpkg-architecture -qDEB_HOST_MULTIARCH) # The simulation tests are only supported on Linux. @@ -22,8 +25,9 @@ # This symbolic link is necessary to prevent clknetsim from FTBFS. ln -s /usr/include/"$DEB_HOST_MULTIARCH"/sys/time.h /usr/include/sys/ - wget -q -P "$CLKNETSIM_PATH" "$clknetsim_src" && tar -xzf "$CLKNETSIM_PATH"/"$clknetsim_archive" \ - -C "$CLKNETSIM_PATH" --strip-components=1 + wget -P "$CLKNETSIM_PATH" "$clknetsim_src" 2>&1 || exit 77 + tar -xvzf "$CLKNETSIM_PATH"/"$clknetsim_archive" \ + -C "$CLKNETSIM_PATH" --strip-components=1 2>&1 || exit 77 if [ ! -x "$CLKNETSIM_PATH/clknetsim" ] && [ ! -e "$CLKNETSIM_PATH/clknetsim.so" ]; then make -C "$CLKNETSIM_PATH" 2>&1 diff -Nru chrony-3.5/debian/tests/upstream-system-tests chrony-4.1/debian/tests/upstream-system-tests --- chrony-3.5/debian/tests/upstream-system-tests 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/tests/upstream-system-tests 2021-05-03 14:51:16.000000000 +0000 @@ -10,8 +10,7 @@ logdir="$testdir/tmp/*" # some tests need chrony installed, but make sure to avoid the test daemon is -# fighting with the systems chrony/systemd-timesyncd services over the clock -systemctl stop systemd-timesyncd.service 2>/dev/null || true +# fighting with the systems chrony service over the clock systemctl stop chrony.service 2>/dev/null || true run_test() { diff -Nru chrony-3.5/debian/upstream/metadata chrony-4.1/debian/upstream/metadata --- chrony-3.5/debian/upstream/metadata 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/upstream/metadata 2021-05-03 14:51:16.000000000 +0000 @@ -1,4 +1,3 @@ -Name: chrony Documentation: https://chrony.tuxfamily.org/documentation.html Changelog: https://chrony.tuxfamily.org/news.html FAQ: https://chrony.tuxfamily.org/faq.html diff -Nru chrony-3.5/debian/upstream/signing-key.asc chrony-4.1/debian/upstream/signing-key.asc --- chrony-3.5/debian/upstream/signing-key.asc 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/upstream/signing-key.asc 2021-05-18 06:12:12.000000000 +0000 @@ -1,29 +1,52 @@ -----BEGIN PGP PUBLIC KEY BLOCK----- -mQGiBEYLz1cRBADYNM9gn8g1Bw8t2Zj+HT9hbSHVs9ofSdxqdLEVAbNySeLftOlZ -ba+4CU+lIfC/6XHZ0r+UvTBVK+r/KLjFxWz5cWGGFVUrXOSjo2PDXDqWrs9VALtT -zH8sr0/7qJCByF9fnryPO1fmMKlh9R0+X5cF7vZjlWbM+BV/yxARi4lb4wCgpf9M -7uo9hJUcMyy2zJSdzjUPkcMEAMVyDpw7kwTjnWzwaOHnPlT/x31OkGAO2sZgzRGu -VE1zGN4Ruv36GS7hNPndtpTGZuPtmLrE2wJS2exer4kTYANfiGj/JDTiuGQYF2jp -9cN3zJL7e7Bik004TZVUGg3HzpuWWc/uiTXgrZxIDz4uPxjy5kdDfbhUziNsy9Uj -igOZBADQ9T6XYQBTfRmGUkl7hEeAeu+WfEGDVlHP+EpMtk/uANUqYef5xUG4RomE -EyjRlrEXwG7Ly2HhH3UADBuPjkP68AGN8WslbCNx5Na+nZr6r1sT1+Z3OdUDprpY -PQxCu5WWYsYgzroO/JEA2d3pYgaaHEAhyZxau1UtW4hpAn8svbQmTWlyb3NsYXYg -TGljaHZhciA8bWxpY2h2YXJAcmVkaGF0LmNvbT6IZgQTEQIAJgIbAwYLCQgHAwIE -FQIIAwQWAgMBAh4BAheABQJbt20rBQkb2aQNAAoJEF/wbym6HgE7MOkAnjdG94MF -4XAVLnzCVbrJb/Ishao4AJ9o1EL9U/at8KzvfZdpPyNrmoeq+bkCDQRGC89XEAgA -medsNk8FIYdzJYyP2eaIYKMTpSCFgTKE1EHdiRaX5n3oo9o26+vfA1NfIwKM8G54 -3Ddr1yl2PRmQermHMQahMMsXcehQXjsJoZXTglJq6kw5Xb1V1K6SyXQv/sLmWGxw -T91T+0I+9g+UqMeqR8B2hj950BbfWn6Pu5CRk2voTsYEU2ecejKOWOOrbUnD/5wy -mkSD/1g+T7bgGOHMrSgYWH3Fk7dWNKpGBtQn3cL7fKy+cn4koDW1L3ebxg4zWpFo -l51m3u8DXc9lqUjg9AoqJH1bc9eQPQvJKxd5syU2pkgtHhT2rlSqpRtsKsgRNfBC -qBbK9gtEM3DRUD+EbbEZgwADBQf8CTSksVEUs5svpQlldZERwViUwwVb4TMszKKq -nEti6zu6oMkIDreGzSISDsrWq1WxzUv9IYumwanzkgTpVVfFPxK7samtol8Lol5V -r3Zbil3Q0IGJ9thhitMHRSU3ClhVRZF5QF/MhSzD1j0cXK4Ls0np5DePT3H4tItZ -+OcEhZcDb8k2DMcJW/REuiisWOElwIDM0o0kZyQiy+5QRfE2xancu3n8+wGtwc0N -2Yp/elmIigreu0xuK7HaFOiScUYv00BJa/ZEO2aOkRuiKkdp3oxtz3MIdDYyGbI6 -mL4h+X8079i95yu+L2tUJGHeN5u+X0Hsg9sE6TpVEggQEI30YYhPBBgRAgAPAhsM -BQJbt22dBQkb2aZBAAoJEF/wbym6HgE7rJYAn1gpOMPrFyjezpaYsloAwjSZhu8t -AKCTJlsZByvaTTXjUMyQy2z7tjnVpw== -=4XBU +mQINBGCc9dwBEADLydyZIqgarshQeCtIlWAgP3coy0mdJwxet1CvXwF1xpq18Qi1 +Tt9RZL64SkbQ8sKryBqnPjKZdOfVT5FwUucjp9L+/j7Bhk0tqv30EIQ57rnDLJ9T +c4LG1leO+Tc5Ym/0tvv4uMjkxr4KAKHPYrweHk6EAw06bbJ02mfy9xhlITSfyyFl +QRoRTEjy8N2IDutA4QzbZm0T5kvI7k7s/ILG5vyNo53X5PI/rWrSqmPZ5qs0lvDv +tA+rxOJp+FvlvOyBuv3ftIX0kAwRU+x/ET2Yd9qQWnXRx9d9D2UpFXm9DHfCDJYR +F56D0O3hf+rrCa/uSutIqmR33j5Wz4bYjWdmg4wbRQaoVxJl5AUrWuYEFwcCuY2B +FFgttLPb0qHpeBwuWaWJ9U6HM7qY3WEI2C/OWM0XFM8ERezedNEf7O2GTsoVVcm+ +LRg31R3eJzipKMAGZWScSDSRAXhh6oZhflMRjYKGvwRfgeos/Sl2bdYL80hqyjGV +jMhEYDC9sfLXRyLU+9FexruIzSLR8Vornma3zjzu9pRkbfTHb8FfBMt9MZEWraF2 +7riRq/zJE9QPWnBL/C8rdaXXxflBmGctn7RDKGOvxZ7SxPzzHbl5tV/Fizhkeph/ +v8YLVuCOk0pIpX65mFun3Xw5IF01x1GMzU1xYezExti9yBNiv9HVqf1DWwARAQAB +tCZNaXJvc2xhdiBMaWNodmFyIDxtbGljaHZhckByZWRoYXQuY29tPokCVAQTAQgA +PhYhBI83XH6NDuElo9O9UVN+K3b3aA2sBQJgnPXcAhsDBQkSzAMABQsJCAcCBhUK +CQgLAgQWAgMBAh4BAheAAAoJEFN+K3b3aA2sl8IQAJ9AMppV6cdxzt8g2Ypz0hw1 +6+9T5DjbYE/s0lozFQhCoYfo+SZyc3+yyKzlxI3ryHwFk9NjXGZZ8QjzT7FLj7/s +nKDjv5hUCOAi9Q+k217xwlBueeMyheeVaGGGa+Hv5CF1fZx/MtxiShUqu8oSqUyP +nW8lPGz73MfGAPT7kijVnz73pbht0vrZ9I+r8dnQGiweGBohexfCvmncrTyhjM8r +nvecycYBNnXhupzpmSMZgIA1s2v7oVmTnV0bntxE/gr7+SPk7KozhD12K8OU8deJ +cDD8F7NKa9Oe5NtuGVN4IPqp5cgj7GAyIj0sYss9Jknu4jX0imR5kwH6GbgFa7c/ +kU+fKTz57Rs1OGr3glYpMnNftXSWbC2V/OJxHVEcMk8HwKLgnQjtmKLVGeCo5iS6 +LFQuWaxpfjvxVjGSpnNu19cHVUhDM9cTP1DhUd4LdnltHQ+/xjwgzTgE4GJ1ZB0W +vhvxcdb69Sf50bGd4/WuURRoYSE7M6UKRwfXmMpyTiNhZz+3XjAoScA9AS7q9xfS +y3OddQEle/+qNFdABB12WmCgRhWemHzTZDXydIJuw+ucLO7U5RrDdqdaHkRVXJ9G +4mdk+3FgUlYgB9GY4pHQdqGdE60838R2zY9x0gK8cHU+FaRPAiTU8SJL0wb/Rko7 +qbZUY/6bgrDoXp4otAP2uQINBGCc9dwBEADKy14JeburwGpEhfZ/sQj9ljojB886 +0TsR51s7F+j016SbdeC0mTmecI3ckd4CyVhi3ZHx6MR6UGKezy4DUtvUrgHOB0Pg +azuReIPRJsAydLnlGMVNRXMkkV310foC2m6UAnNrPW1ukWjMC7kDcWcJH1wpZD0J +rCJkxQNCp7VqR3PrhK9Sdoui0Euw/wSupu9pfcfM/ZPYKgGm18PsR7IrPn3zzBuS +Zhzqnh2LbKvtiVtVbwZwOEIdwyh/TkGg/YWWxIlAt5zJpS5Dxf69vbVkz2u5bgRw +Z1+eeiU11Zvrw1A8GdrKy32iskAsWzS8L0Nj5+Ujssgp7iFkdsWbP/ijwzIbSlAQ +FR3G6Fbuojwk1ONglTD2ib2A9olBJ3GTXlBiTkrhtuNC0izRhBRbA+2IF29Vx40A ++XItyouz/4R4twBudyLEE4mhOVYAXQ43t82An+VmmenNw0EAfZWm9kTM/uRXa+2e +mJhUVG5sUp7DHtJsMHKlXt6H1dHyrxOxKdE/GwYOd3QqslPISGw+cFHvfSZoTBZ8 +7OXpcJL/x29xIPdnYLtc1m+ZDM7rxhB7QiLpCMtQfqc0Dn0/ojgvpW5kR4T7JSfU +tNbxDSRPpxBLBsT8vaUR65lkiiOGYfsrI2/71v7zsAHdUbL8U34AXjjmsJpc/p9K +SU9NfGZ1IJnN+QARAQABiQI8BBgBCAAmFiEEjzdcfo0O4SWj071RU34rdvdoDawF +AmCc9dwCGwwFCRLMAwAACgkQU34rdvdoDaz7kQ/+N5DND+9YMXyUI+jwaT7YyFVX +5uEyD1iplfheP/XgsADwv6OOGMPp8Rq++0ZNg7Gkp9FedY9noEQlyrmQWcf7+iVz ++6qQ5N1HnJpeyH8U3fjfoJCN74lLK7eMpFIMlB/8Mv3nTBt4faoQUD9EcYg4HtfD ++mFKUEp6t7j+9snEzywSZaqf2Pfhl1Gqb3Mg6gRZ77Peong5km40n+ra4rNXLi7+ +n3pXeBzVGPy5iOTJWG8sG4FhqUjsilJU7chd9y6j51ne1Lkze0DbGMoxp5SW5JBo +vysLjWyKORrOGr2lEk3O0gS3QmJ3hCLe5re4YNErSh0AurP2pP8PMpdchcuYEbrD +uEAhU9mTEOo2VALmkBsdK4VkK/WpDTSRA7B0wUEfKcaRady3A13FF0knskkUhWS7 +foRMbw0MV7oXW2R2Bi9ROoOYLSUCp6GzbtozDs4gXA4r4QLKjcNj6KunyVS2r4wS +OQSNcQKpspiBMSRUpR3S7matPXFc2NxDo98dt2E8zyjTiP6ruI01E+AcovDstzzd +QQdJ+UqjkVidCnwMlRHsLIUgKie862ebCV1pynlUKgToORMJDgfK8/zN5qoK5wUY +qoS4oohubShEwNWCEBlyGz/Ldb6qEK6b2GiX5JN5EJZLcXxZA0bfHSiMaRZPLj4j +W23ALtrl30a0MaWqnDo= +=kLnA -----END PGP PUBLIC KEY BLOCK----- diff -Nru chrony-3.5/debian/usr.sbin.chronyd chrony-4.1/debian/usr.sbin.chronyd --- chrony-3.5/debian/usr.sbin.chronyd 2020-04-20 13:53:05.000000000 +0000 +++ chrony-4.1/debian/usr.sbin.chronyd 2021-05-03 14:51:16.000000000 +0000 @@ -1,42 +1,61 @@ # Last Modified: Sat Jan 20 10:45:05 2018 #include -/usr/sbin/chronyd (attach_disconnected) { +/usr/sbin/chronyd flags=(attach_disconnected) { #include #include - capability sys_time, + # For /run/chrony to be created + capability chown, + + # Give “root” the ability to read and write the PID file + capability dac_override, + capability dac_read_search, + + # Needed to support HW timestamping + capability net_admin, + + # Needed to allow NTP server sockets to be bound to a privileged port capability net_bind_service, - capability setuid, + + # Needed to allow an NTP socket to be bound to a device using the + # SO_BINDTODEVICE socket option on kernels before 5.7 + capability net_raw, + + # Needed to drop privileges capability setgid, + capability setuid, + + # Needed to set the SCHED_FIFO real-time scheduler at the specified priority + # using the '-P' option capability sys_nice, + + # Needed to lock chronyd into RAM capability sys_resource, - # for /run/chrony to be created - capability chown, - # Needed to support HW timestamping - capability net_admin, + + # Needed to set the system/real-time clock + capability sys_time, /usr/sbin/chronyd mr, /etc/chrony/{,**} r, - /{,var/}run/chronyd.pid w, - /{,var/}run/chrony/{,*} rw, - /var/lib/chrony/{,*} r, - /var/lib/chrony/* w, - /var/log/chrony/{,*} r, - /var/log/chrony/* w, + /var/lib/chrony/{,*} rw, + /var/log/chrony/{,*} rw, + @{run}/chrony/{,*} rw, + @{run}/chrony-dhcp/{,*} r, # Using the “tempcomp” directive gives chronyd the ability to improve # the stability and accuracy of the clock by compensating the temperature # changes measured by a sensor close to the oscillator. @{sys}/class/hwmon/hwmon[0-9]*/temp[0-9]*_input r, + @{sys}/devices/virtual/thermal/thermal_zone[0-9]*/hwmon[0-9]*/temp[0-9]*_input r, # Support all paths suggested in the man page (LP: #1771028). Assume these # are common use cases; others should be set as local include (see below). # Configs using a 'chrony.' prefix like the tempcomp config file example /etc/chrony.* r, - # Example gpsd socket is outside /{,var/}run/chrony/ - /{,var/}run/chrony.tty{,*}.sock rw, + # Example gpsd socket is outside @{run}/chrony/ + @{run}/chrony.tty{,*}.sock rw, # To sign replies to MS-SNTP clients by the smbd daemon /var/lib/samba/ntp_signd/socket rw, diff -Nru chrony-3.5/debian/watch chrony-4.1/debian/watch --- chrony-3.5/debian/watch 2020-04-01 14:24:09.000000000 +0000 +++ chrony-4.1/debian/watch 2021-05-18 06:12:12.000000000 +0000 @@ -1,3 +1,3 @@ version=4 -opts=pgpsigurlmangle=s/\.tar\.gz$/-tar-gz-asc.txt/ \ -https://download.tuxfamily.org/chrony/chrony-([\d\.]*)\.tar\.gz +opts="pgpsigurlmangle=s/\.tar\.gz$/-tar-gz-asc.txt/,uversionmangle=s/-pre/~pre/" \ +https://download.tuxfamily.org/@PACKAGE@/@PACKAGE@@ANY_VERSION@@ARCHIVE_EXT@ diff -Nru chrony-3.5/doc/chronyc.adoc chrony-4.1/doc/chronyc.adoc --- chrony-3.5/doc/chronyc.adoc 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/doc/chronyc.adoc 2021-05-12 11:06:15.000000000 +0000 @@ -2,7 +2,7 @@ // // Copyright (C) Richard P. Curnow 1997-2003 // Copyright (C) Stephen Wadeley 2016 -// Copyright (C) Miroslav Lichvar 2009-2017 +// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2020 // // This program is free software; you can redistribute it and/or modify // it under the terms of version 2 of the GNU General Public License as @@ -39,7 +39,7 @@ If no commands are specified on the command line, *chronyc* will expect input from the user. The prompt _chronyc>_ will be displayed when it is being run from a terminal. If *chronyc*'s input or output are redirected from or to a file, -the prompt is not shown. +the prompt will not be shown. There are two ways *chronyc* can access *chronyd*. One is the Internet Protocol (IPv4 or IPv6) and the other is a Unix domain socket, which is @@ -51,7 +51,8 @@ Only the following monitoring commands, which do not affect the behaviour of *chronyd*, are allowed from the network: *activity*, *manual list*, -*rtcdata*, *smoothing*, *sources*, *sourcestats*, *tracking*, *waitsync*. The +*rtcdata*, *smoothing*, *sourcename*, *sources*, *sourcestats*, *tracking*, +*waitsync*. The set of hosts from which *chronyd* will accept these commands can be configured with the <> directive in the *chronyd*'s configuration file or the <> command in *chronyc*. By @@ -59,9 +60,7 @@ All other commands are allowed only through the Unix domain socket. When sent over the network, *chronyd* will respond with a '`Not authorised`' error, even -if it is from localhost. In chrony versions before 2.2 they were allowed -from the network if they were authenticated with a password, but that is no -longer supported. +if it is from localhost. Having full access to *chronyd* via *chronyc* is more or less equivalent to being able to modify the *chronyd*'s configuration file and restart it. @@ -78,11 +77,17 @@ This option disables resolving of IP addresses to hostnames, e.g. to avoid slow DNS lookups. Long addresses will not be truncated to fit into the column. +*-N*:: +This option enables printing of original hostnames or IP addresses of NTP +sources that were specified in the configuration file, or *chronyc* commands. +Without the *-n* and *-N* option, the printed hostnames are obtained from +reverse DNS lookups and can be different from the specified hostnames. + *-c*:: This option enables printing of reports in a comma-separated values (CSV) -format. IP addresses will not be resolved to hostnames, time will be printed as -number of seconds since the epoch and values in seconds will not be converted -to other units. +format. Reverse DNS lookups will be disabled, time will be printed as number of +seconds since the epoch, and values in seconds will not be converted to other +units. *-d*:: This option enables printing of debugging messages if *chronyc* was compiled @@ -112,10 +117,14 @@ *-a*:: 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 exits. +*--help*:: +With this option *chronyc* displays a help message on the terminal and +exits. + == COMMANDS This section describes each of the commands available within the *chronyc* @@ -175,10 +184,12 @@ and then returning to the system clock's normal speed. A consequence of this is that there will be a period when the system clock (as read by other programs) will be different from *chronyd*'s estimate of the current true time (which it -reports to NTP clients when it is operating in server mode). The value reported +reports to NTP clients when it is operating as a server). The value reported on this line is the difference due to this effect. *Last offset*::: -This is the estimated local offset on the last clock update. +This is the estimated local offset on the last clock update. A positive value +indicates the local time (as previously estimated true time) was ahead of the +time sources. *RMS offset*::: This is a long-term average of the offset value. *Frequency*::: @@ -284,15 +295,18 @@ === Time sources -[[sources]]*sources* [*-v*]:: +[[sources]]*sources* [*-a*] [*-v*]:: This command displays information about the current time sources that *chronyd* is accessing. + -The optional argument *-v* can be specified, meaning _verbose_. In this case, +If the *-a* option is specified, all sources are displayed, including those that +do not have a known address yet. Such sources have an identifier in the format +_ID#XXXXXXXXXX_, which can be used in other commands expecting a source address. ++ +The *-v* option enables a verbose output. In this case, extra caption lines are shown as a reminder of the meanings of the columns. + ---- -210 Number of sources = 3 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== #* GPS0 0 4 377 11 -479ns[ -621ns] +/- 134ns @@ -306,18 +320,23 @@ This indicates the mode of the source. _^_ means a server, _=_ means a peer and _#_ indicates a locally connected reference clock. *S*::: -This column indicates the state of the source. -* _*_ indicates the source to which *chronyd* is currently synchronised. -* _+_ indicates acceptable sources which are combined with the selected - source. -* _-_ indicates acceptable sources which are excluded by the combining - algorithm. -* _?_ indicates sources to which connectivity has been lost or whose packets - do not pass all tests. It is also shown at start-up, until at least 3 samples - have been gathered from it. -* _x_ indicates a clock which *chronyd* thinks is a falseticker (i.e. its - time is inconsistent with a majority of other sources). +This column indicates the selection state of the source. +* _*_ indicates the best source which is currently selected for + synchronisation. +* _+_ indicates other sources selected for synchronisation, which are combined + with the best source. +* _-_ indicates a source which is considered to be selectable for + synchronisation, but not currently selected. +* _x_ indicates a source which *chronyd* thinks is a falseticker (i.e. its + time is inconsistent with a majority of other sources, or sources specified + with the *trust* option). * _~_ indicates a source whose time appears to have too much variability. +* _?_ indicates a source which is not considered to be selectable for + synchronisation for other reasons (e.g. unreachable, not synchronised, or + does not have enough measurements). +{blank}::: +The <> command can be used to get more details about +the selection state. *Name/IP address*::: This shows the name or the IP address of the source, or reference ID for reference clocks. @@ -353,18 +372,21 @@ the measurement. Positive offsets indicate that the local clock is ahead of the source. -[[sourcestats]]*sourcestats* [*-v*]:: +[[sourcestats]]*sourcestats* [*-a*] [*-v*]:: The *sourcestats* command displays information about the drift rate and offset estimation process for each of the sources currently being examined by *chronyd*. + -The optional argument *-v* can be specified, meaning _verbose_. In this case, +If the *-a* option is specified, all sources are displayed, including those that +do not have a known address yet. Such sources have an identifier in the format +_ID#XXXXXXXXXX_, which can be used in other commands expecting a source address. ++ +The *-v* option enables a verbose output. In this case, extra caption lines are shown as a reminder of the meanings of the columns. + An example report is: + ---- -210 Number of sources = 1 Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev =============================================================================== foo.example.net 11 5 46m -0.001 0.045 1us 25us @@ -400,6 +422,98 @@ *Std Dev*::: This is the estimated sample standard deviation. +[[selectdata]]*selectdata* [*-a*] [*-v*]:: +The *selectdata* command displays information specific to the selection of time +sources. If the *-a* option is specified, all sources are displayed, including +those that do not have a known address yet. With the *-v* option, extra caption +lines are shown as a reminder of the meanings of the columns. ++ +An example of the output is shown below. ++ +---- +S Name/IP Address Auth COpts EOpts Last Score Interval Leap +======================================================================= +D foo.example.net Y ----- --TR- 4 1.0 -61ms +62ms N +* bar.example.net N ----- ----- 0 1.0 -6846us +7305us N ++ baz.example.net N ----- ----- 10 1.0 -7381us +7355us N +---- ++ +The columns are as follows: ++ +*S*::: +This column indicates the state of the source after the last source selection. +It is similar to the state reported by the *sources* command, but more +states are reported. +{blank}::: +The following states indicate the source is not considered selectable for +synchronisation: +* _N_ - has the *noselect* option. +* _M_ - does not have enough measurements. +* _d_ - has a root distance larger than the maximum distance (configured by the + <> directive). +* _~_ - has a jitter larger than the maximum jitter (configured by the + <> directive). +* _w_ - waits for other sources to get out of the _M_ state. +* _S_ - has older measurements than other sources. +* _O_ - has a stratum equal or larger than the orphan stratum (configured by + the <> directive). +* _T_ - does not fully agree with sources that have the *trust* option. +* _x_ - does not agree with other sources (falseticker). +{blank}::: +The following states indicate the source is considered selectable, but it is +not currently used for synchronisation: +* _W_ - waits for other sources to be selectable (required by the + <> directive, or + the *require* option of another source). +* _P_ - another selectable source is preferred due to the *prefer* option. +* _U_ - waits for a new measurement (after selecting a different best source). +* _D_ - has, or recently had, a root distance which is too large to be combined + with other sources (configured by the + <> directive). +{blank}::: +The following states indicate the source is used for synchronisation of the +local clock: +* _+_ - combined with the best source. +* _*_ - selected as the best source to update the reference data (e.g. root + delay, root dispersion). +*Name/IP address*::: +This column shows the name or IP address of the source if it is an NTP server, +or the reference ID if it is a reference clock. +*Auth*::: +This column indicites whether an authentication mechanism is enabled for the +source. _Y_ means yes and _N_ means no. +*COpts*::: +This column displays the configured selection options of the source. +* _N_ indicates the *noselect* option. +* _P_ indicates the *prefer* option. +* _T_ indicates the *trust* option. +* _R_ indicates the *require* option. +*EOpts*::: +This column displays the current effective selection options of the source, +which can be different from the configured options due to the authentication +selection mode (configured by the +<> directive). The symbols are the +same as in the *COpts* column. +*Last*::: +This column displays how long ago was the last measurement of the source made +when the selection was performed. +*Score*::: +This column displays the current score against the source in the _*_ state. The +scoring system avoids frequent reselection when multiple sources have a similar +root distance. A value larger than 1 indicates this source was better than the +_*_ source in recent selections. If the score reaches 10, the best source will +be reselected and the scores will be reset to 1. +*Interval*::: +This column displays the lower and upper endpoint of the interval which was +expected to contain the true offset of the local clock considering the root +distance at the time of the selection. +*Leap*::: +This column displays the current leap status of the source. +* _N_ indicates the normal status (no leap second). +* _+_ indicates that a leap second will be inserted at the end of the month. +* _-_ indicates that a leap second will be deleted at the end of the month. +* _?_ indicates the unknown status (i.e. no valid measurement was made). + [[reselect]]*reselect*:: To avoid excessive switching between sources, *chronyd* can stay synchronised to a source even when it is not currently the best one among the available @@ -440,10 +554,82 @@ the name of the server or peer was not resolved to an address yet; this source is not visible in the *sources* and *sourcestats* reports. +[[authdata]]*authdata* [*-a*]:: +The *authdata* command displays information specific to authentication of NTP +sources. If the *-a* option is specified, all sources are displayed, including +those that do not have a known address yet. An example of the output is +shown below. ++ +---- +Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen +========================================================================= +foo.example.net NTS 1 15 256 135m 0 0 8 100 +bar.example.net SK 30 13 128 - 0 0 0 0 +baz.example.net - 0 0 0 - 0 0 0 0 +---- ++ +The columns are as follows: ++ +*Name/IP address*::: +This column shows the name or the IP address of the source. +*Mode*::: +This column shows which mechanism authenticates NTP packets received from the +source. _NTS_ means Network Time Security, _SK_ means a symmetric key, and _-_ +means authentication is disabled. +*KeyID*::: +This column shows an identifier of the key used for authentication. With a +symmetric key, it is the ID from the <>. +With NTS, it is a number starting at zero and incremented by one with each +successful key establishment using the NTS-KE protocol, i.e. it shows how many +times the key establishment was performed with this source. +*Type*::: +This columns shows an identifier of the algorithm used for authentication. +With a symmetric key, it is the hash function or cipher specified in the key +file. With NTS, it is an authenticated encryption with associated data (AEAD) +algorithm, which is negotiated in the NTS-KE protocol. The following values can +be reported: +* 1: MD5 +* 2: SHA1 +* 3: SHA256 +* 4: SHA384 +* 5: SHA512 +* 6: SHA3-224 +* 7: SHA3-256 +* 8: SHA3-384 +* 9: SHA3-512 +* 10: TIGER +* 11: WHIRLPOOL +* 13: AES128 +* 14: AES256 +* 15: AEAD-AES-SIV-CMAC-256 +*KLen*::: +This column shows the length of the key in bits. +*Last*::: +This column shows how long ago the last successful key establishment was +performed. It is in seconds, or letters _m_, _h_, _d_ or _y_ indicate minutes, +hours, days, or years. +*Atmp*::: +This column shows the number of attempts to perform the key establishment since +the last successful key establishment. A number larger than 1 indicates a +problem with the network or server. +*NAK*::: +This column shows whether an NTS NAK was received since the last request. +A NAK indicates that authentication failed on the server side due to +*chronyd* using a cookie which is no longer valid and that it needs to perform +the key establishment again in order to get new cookies. +*Cook*::: +This column shows the number of NTS cookies that *chronyd* currently has. If +the key establishment was successful, a number smaller than 8 indicates a +problem with the network or server. +*CLen*::: +This column shows the length in bytes of the NTS cookie which will be used in +the next request. + [[ntpdata]]*ntpdata* [_address_]:: The *ntpdata* command displays the last valid measurement and other -NTP-specific information about the specified NTP source, or all NTP sources if -no address was specified. An example of the output is shown below. +NTP-specific information about the specified NTP source, or all NTP sources +(with a known address) if no address was specified. An example of the output is +shown below. + ---- Remote address : 203.0.113.15 (CB00710F) @@ -526,15 +712,13 @@ *Total valid RX*::: The number of valid packets received from the source. -[[add_peer]]*add peer* _address_ [_option_]...:: +[[add_peer]]*add peer* _name_ [_option_]...:: The *add peer* command allows a new NTP peer to be added whilst *chronyd* is running. + Following the words *add peer*, the syntax of the following -parameters and options is similar to that for the +parameters and options is identical to that for the <> directive in the configuration file. -The following peer options can be set in the command: *port*, *minpoll*, -*maxpoll*, *presend*, *maxdelayratio*, *maxdelay*, *key*. + An example of using this command is shown below. + @@ -542,15 +726,27 @@ add peer foo.example.net minpoll 6 maxpoll 10 key 25 ---- -[[add_server]]*add server* _address_ [_option_]...:: +[[add_pool]]*add pool* _name_ [_option_]...:: +The *add pool* command allows a pool of NTP servers to be added whilst +*chronyd* is running. ++ +Following the words *add pool*, the syntax of the following parameters and +options is identical to that for the <> +directive in the configuration file. ++ +An example of using this command is shown below: ++ +---- +add pool foo.example.net maxsources 3 iburst +---- + +[[add_server]]*add server* _name_ [_option_]...:: The *add server* command allows a new NTP server to be added whilst *chronyd* is running. + Following the words *add server*, the syntax of the following parameters and -options is similar to that for the <> +options is identical to that for the <> directive in the configuration file. -The following server options can be set in the command: *port*, *minpoll*, -*maxpoll*, *presend*, *maxdelayratio*, *maxdelay*, *key*. + An example of using this command is shown below: + @@ -601,7 +797,7 @@ _address_::: This is an IP address or a hostname. The burst command is applied only to that source. -:: +{blank}:: + If no _mask_ or _masked-address_ arguments are provided, every source will be matched. @@ -688,7 +884,8 @@ this. + There are four forms of the *offline* command. The first form is a wildcard, -meaning all sources. The second form allows an IP address mask and a masked +meaning all sources (including sources that do not have a known address yet). +The second form allows an IP address mask and a masked address to be specified. The third form uses CIDR notation. The fourth form uses an IP address or a hostname. These forms are illustrated below. + @@ -725,10 +922,11 @@ [[onoffline]] *onoffline*:: -The *onoffline* command tells *chronyd* to switch all sources to the online or +The *onoffline* command tells *chronyd* to switch all sources that have a known +address to the online or offline status according to the current network configuration. A source is -considered online if it is possible to send requests to it, i.e. a route to the -network is present. +considered online if it is possible to send requests to it, i.e. a network +route to the source is present. [[polltarget]]*polltarget* _address_ _polltarget_:: The *polltarget* command is used to modify the poll target for one of the @@ -744,6 +942,20 @@ automatically after 8 polling intervals, but this command can still be useful to replace them immediately and not wait until they are marked as unreachable. +[[reload]]*reload* *sources*:: +The *reload sources* command causes *chronyd* to re-read all _*.sources_ files +from the directories specified by the +<> directive. + +[[sourcename]]*sourcename* _address_:: +The *sourcename* command prints the original hostname or address that was +specified for an NTP source in the configuration file, or the *add* command. +This command is an alternative to the *-N* option, which can be useful in +scripts. ++ +Note that different NTP sources can share the same name, e.g. servers from a +pool. + === Manual time input [[manual]] @@ -780,7 +992,7 @@ . The regression residual at this point, in seconds. This allows '`outliers`' to be easily spotted, so that they can be deleted using the *manual delete* command. -:: +{blank}:: + The *delete* form of the command deletes a single sample. The parameter is the index of the sample, as shown in the first column of the output from *manual @@ -851,10 +1063,17 @@ all*, *deny*, and *deny all* commands specified either via *chronyc*, or in *chronyd*'s configuration file. -[[clients]]*clients*:: +[[clients]]*clients* [*-p* _packets_] [*-k*] [*-r*]:: This command shows a list of clients that have accessed the server, through -either the NTP or command ports. It does not include accesses over -the Unix domain command socket. There are no arguments. +the NTP, command, or NTS-KE port. It does not include accesses over the Unix +domain command socket. ++ +The *-p* option specifies the minimum number of received NTP or command +packets, or accepted NTS-KE connections, needed to include a client in the +list. The default value is 0, i.e. all clients are reported. With the *-k* +option the last four columns will show the NTS-KE accesses instead of command +accesses. If the *-r* option is specified, *chronyd* will reset the counters of +received and dropped packets or connections after reporting the current values. + An example of the output is: + @@ -879,20 +1098,22 @@ . The average interval between NTP packets. . The average interval between NTP packets after limiting the response rate. . Time since the last NTP packet was received -. The number of command packets received from the client. -. The number of command packets dropped to limit the response rate. -. The average interval between command packets. -. Time since the last command packet was received. +. The number of command packets or NTS-KE connections received/accepted from + the client. +. The number of command packets or NTS-KE connections dropped to limit the + response rate. +. The average interval between command packets or NTS-KE connections. +. Time since the last command packet or NTS-KE connection was + received/accepted. [[serverstats]]*serverstats*:: -The *serverstats* command displays how many valid NTP and command requests -*chronyd* as a server received from clients, how many of them were dropped to -limit the response rate as configured by the -<> and -<> directives, and how many +The *serverstats* command displays how many valid NTP and command requests, and +NTS-KE connections, *chronyd* operating as a server received from clients, and +how many of them were dropped due to rate limiting. It also displays how many client log records were dropped due to the memory limit configured by the -<> directive. An example of -the output is shown below. +<> directive and how many of +the NTP requests (from those which were not dropped) were authenticated. An +example of the output is shown below. + ---- NTP packets received : 1598 @@ -900,6 +1121,9 @@ Command packets received : 19 Command packets dropped : 0 Client log records dropped : 0 +NTS-KE connections accepted: 3 +NTS-KE connections dropped : 0 +Authenticated NTP packets : 189 ---- [[allow]]*allow* [*all*] [_subnet_]:: @@ -909,11 +1133,8 @@ The syntax is illustrated in the following examples: + ---- -allow foo.example.net -allow all 1.2 -allow 3.4.5 -allow 6.7.8/22 -allow 6.7.8.9/22 +allow 1.2.3.4 +allow all 3.4.5.0/24 allow 2001:db8:789a::/48 allow 0/0 allow ::/0 @@ -928,11 +1149,8 @@ The syntax is illustrated in the following examples: + ---- -deny foo.example.net -deny all 1.2 -deny 3.4.5 -deny 6.7.8/22 -deny 6.7.8.9/22 +deny 1.2.3.4 +deny all 3.4.5.0/24 deny 2001:db8:789a::/48 deny 0/0 deny ::/0 @@ -1085,7 +1303,7 @@ error). . Save the RTC parameters to the RTC file (specified with the <> directive in the configuration file). -:: +{blank}:: + The last step is done as a precaution against the computer suffering a power failure before either the daemon exits or the <> command @@ -1118,25 +1336,36 @@ ---- # mv /var/log/chrony/measurements.log /var/log/chrony/measurements1.log # chronyc cyclelogs -# ls -l /var/log/chrony --rw-r--r-- 1 root root 0 Jun 8 18:17 measurements.log --rw-r--r-- 1 root root 12345 Jun 8 18:17 measurements1.log -# rm -f measurements1.log +# rm /var/log/chrony/measurements1.log ---- [[dump]]*dump*:: The *dump* command causes *chronyd* to write its current history of measurements for each of its sources to dump files in the directory specified in the configuration file by the <> +directive and also write server NTS keys and client NTS cookies to the +directory specified by the <> directive. Note that *chronyd* does this automatically when it exits. This -command is mainly useful for inspection of the history whilst *chronyd* is -running. +command is mainly useful for inspection whilst *chronyd* is running. [[rekey]]*rekey*:: The *rekey* command causes *chronyd* to re-read the key file specified in the -configuration file by the <> directive. +configuration file by the <> directive. It +also re-reads the server NTS keys if +<> is specified and +<> is disabled in the +configuration file. -[[rekey]]*shutdown*:: +[[reset]]*reset* *sources*:: +The *reset sources* command causes *chronyd* to drop all measurements and +switch to the unsynchronised state. This command can help *chronyd* with +recovery when the measurements are known to be no longer valid or accurate, +e.g. due to moving the computer to a different network, or resuming the +computer from a low-power state (which resets the system clock). *chronyd* will +drop the measurements automatically when it detects the clock has made an +unexpected jump, but the detection is not completely reliable. + +[[shutdown]]*shutdown*:: The *shutdown* command causes *chronyd* to exit. This is equivalent to sending the process the SIGTERM signal. @@ -1188,10 +1417,10 @@ + The command has three optional arguments. The first argument is the key number (by default 1), which will be specified with the *key* option of the *server* -or *peer* directives in the configuration file. The second argument is the hash -function (by default SHA1 or MD5 if SHA1 is not available) and the third -argument is the number of bits the key should have, between 80 and 4096 bits -(by default 160 bits). +or *peer* directives in the configuration file. The second argument is the name +of the hash function or cipher (by default SHA1, or MD5 if SHA1 is not +available). The third argument is the length of the key in bits if a hash +function was selected, between 80 and 4096 bits (by default 160 bits). + An example is: + @@ -1201,7 +1430,13 @@ + which generates a 256-bit SHA1 key with number 73. The printed line should then be securely transferred and added to the key files on both server and -client, or peers. +client, or peers. A different key should be generated for each client or peer. ++ +An example using the AES128 cipher is: ++ +---- +keygen 151 AES128 +---- [[exit]]*exit*:: [[quit]]*quit*:: diff -Nru chrony-3.5/doc/chronyc.man.in chrony-4.1/doc/chronyc.man.in --- chrony-3.5/doc/chronyc.man.in 2019-05-14 11:00:04.000000000 +0000 +++ chrony-4.1/doc/chronyc.man.in 2021-05-13 10:48:12.000000000 +0000 @@ -1,23 +1,32 @@ '\" t .\" Title: chronyc -.\" Author: [see the "AUTHORS" section] -.\" Generator: Asciidoctor 1.5.6.1 -.\" Date: 2019-05-10 +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.10 +.\" Date: 2021-05-12 .\" Manual: User manual .\" Source: chrony @CHRONY_VERSION@ .\" Language: English .\" -.TH "CHRONYC" "1" "2019-05-10" "chrony @CHRONY_VERSION@" "User manual" +.TH "CHRONYC" "1" "2021-05-12" "chrony @CHRONY_VERSION@" "User manual" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 .nh .ad l .de URL -\\$2 \(laURL: \\$1 \(ra\\$3 +\fI\\$2\fP <\\$1>\\$3 .. -.if \n[.g] .mso www.tmac -.LINKSTYLE blue R < > +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} .SH "NAME" chronyc \- command\-line interface for chrony daemon .SH "SYNOPSIS" @@ -32,7 +41,7 @@ If no commands are specified on the command line, \fBchronyc\fP will expect input from the user. The prompt \fIchronyc>\fP will be displayed when it is being run from a terminal. If \fBchronyc\fP\(cqs input or output are redirected from or to a file, -the prompt is not shown. +the prompt will not be shown. .sp There are two ways \fBchronyc\fP can access \fBchronyd\fP. One is the Internet Protocol (IPv4 or IPv6) and the other is a Unix domain socket, which is @@ -44,7 +53,8 @@ .sp Only the following monitoring commands, which do not affect the behaviour of \fBchronyd\fP, are allowed from the network: \fBactivity\fP, \fBmanual list\fP, -\fBrtcdata\fP, \fBsmoothing\fP, \fBsources\fP, \fBsourcestats\fP, \fBtracking\fP, \fBwaitsync\fP. The +\fBrtcdata\fP, \fBsmoothing\fP, \fBsourcename\fP, \fBsources\fP, \fBsourcestats\fP, \fBtracking\fP, +\fBwaitsync\fP. The set of hosts from which \fBchronyd\fP will accept these commands can be configured with the \fBcmdallow\fP directive in the \fBchronyd\fP\(cqs configuration file or the \fBcmdallow\fP command in \fBchronyc\fP. By @@ -52,9 +62,7 @@ .sp All other commands are allowed only through the Unix domain socket. When sent over the network, \fBchronyd\fP will respond with a \(oqNot authorised\(cq error, even -if it is from localhost. In chrony versions before 2.2 they were allowed -from the network if they were authenticated with a password, but that is no -longer supported. +if it is from localhost. .sp Having full access to \fBchronyd\fP via \fBchronyc\fP is more or less equivalent to being able to modify the \fBchronyd\fP\(cqs configuration file and restart it. @@ -76,12 +84,20 @@ DNS lookups. Long addresses will not be truncated to fit into the column. .RE .sp +\fB\-N\fP +.RS 4 +This option enables printing of original hostnames or IP addresses of NTP +sources that were specified in the configuration file, or \fBchronyc\fP commands. +Without the \fB\-n\fP and \fB\-N\fP option, the printed hostnames are obtained from +reverse DNS lookups and can be different from the specified hostnames. +.RE +.sp \fB\-c\fP .RS 4 This option enables printing of reports in a comma\-separated values (CSV) -format. IP addresses will not be resolved to hostnames, time will be printed as -number of seconds since the epoch and values in seconds will not be converted -to other units. +format. Reverse DNS lookups will be disabled, time will be printed as number of +seconds since the epoch, and values in seconds will not be converted to other +units. .RE .sp \fB\-d\fP @@ -124,11 +140,17 @@ This option is ignored and is provided only for compatibility. .RE .sp -\fB\-v\fP +\fB\-v\fP, \fB\-\-version\fP .RS 4 With this option \fBchronyc\fP displays its version number on the terminal and exits. .RE +.sp +\fB\-\-help\fP +.RS 4 +With this option \fBchronyc\fP displays a help message on the terminal and +exits. +.RE .SH "COMMANDS" .sp This section describes each of the commands available within the \fBchronyc\fP @@ -140,9 +162,7 @@ The \fBtracking\fP command displays parameters about the system\(cqs clock performance. An example of the output is shown below. .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf Reference ID : CB00710F (foo.example.net) Stratum : 3 @@ -158,9 +178,7 @@ Update interval : 64.2 seconds Leap status : Normal .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The fields are explained as follows: .sp @@ -204,13 +222,15 @@ and then returning to the system clock\(cqs normal speed. A consequence of this is that there will be a period when the system clock (as read by other programs) will be different from \fBchronyd\fP\(cqs estimate of the current true time (which it -reports to NTP clients when it is operating in server mode). The value reported +reports to NTP clients when it is operating as a server). The value reported on this line is the difference due to this effect. .RE .sp \fBLast offset\fP .RS 4 -This is the estimated local offset on the last clock update. +This is the estimated local offset on the last clock update. A positive value +indicates the local time (as previously estimated true time) was ahead of the +time sources. .RE .sp \fBRMS offset\fP @@ -265,15 +285,11 @@ An absolute bound on the computer\(cqs clock accuracy (assuming the stratum\-1 computer is correct) is given by: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf clock_error <= |system_time_offset| + root_dispersion + (0.5 * root_delay) .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBUpdate interval\fP @@ -308,16 +324,12 @@ needed, without waiting for \fBchronyd\fP to complete the measurement and update the clock. .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf makestep 0.1 1 burst 1/2 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp BE WARNED: Certain software will be seriously affected by such jumps in the system time. (That is the reason why \fBchronyd\fP uses slewing normally.) @@ -348,15 +360,11 @@ .sp An example is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf waitsync 60 0.01 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp which will wait up to about 10 minutes (60 times 10 seconds) for \fBchronyd\fP to synchronise to a source and the remaining correction to be less than 10 @@ -364,28 +372,27 @@ .RE .SS "Time sources" .sp -\fBsources\fP [\fB\-v\fP] +\fBsources\fP [\fB\-a\fP] [\fB\-v\fP] .RS 4 This command displays information about the current time sources that \fBchronyd\fP is accessing. .sp -The optional argument \fB\-v\fP can be specified, meaning \fIverbose\fP. In this case, +If the \fB\-a\fP option is specified, all sources are displayed, including those that +do not have a known address yet. Such sources have an identifier in the format +\fIID#XXXXXXXXXX\fP, which can be used in other commands expecting a source address. +.sp +The \fB\-v\fP option enables a verbose output. In this case, extra caption lines are shown as a reminder of the meanings of the columns. .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf -210 Number of sources = 3 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== #* GPS0 0 4 377 11 \-479ns[ \-621ns] +/\- 134ns ^? foo.example.net 2 6 377 23 \-923us[ \-924us] +/\- 43ms ^+ bar.example.net 1 6 377 21 \-2629us[\-2619us] +/\- 86ms .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The columns are as follows: .sp @@ -397,17 +404,18 @@ .sp \fBS\fP .RS 4 -This column indicates the state of the source. +This column indicates the selection state of the source. .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ -.sp -1 -.IP \(bu 2.3 +. sp -1 +. IP \(bu 2.3 .\} -\fI*\fP indicates the source to which \fBchronyd\fP is currently synchronised. +\fI*\fP indicates the best source which is currently selected for +synchronisation. .RE .sp .RS 4 @@ -415,11 +423,11 @@ \h'-04'\(bu\h'+03'\c .\} .el \{\ -.sp -1 -.IP \(bu 2.3 +. sp -1 +. IP \(bu 2.3 .\} -\fI+\fP indicates acceptable sources which are combined with the selected -source. +\fI+\fP indicates other sources selected for synchronisation, which are combined +with the best source. .RE .sp .RS 4 @@ -427,11 +435,11 @@ \h'-04'\(bu\h'+03'\c .\} .el \{\ -.sp -1 -.IP \(bu 2.3 +. sp -1 +. IP \(bu 2.3 .\} -\fI\-\fP indicates acceptable sources which are excluded by the combining -algorithm. +\fI\-\fP indicates a source which is considered to be selectable for +synchronisation, but not currently selected. .RE .sp .RS 4 @@ -439,12 +447,12 @@ \h'-04'\(bu\h'+03'\c .\} .el \{\ -.sp -1 -.IP \(bu 2.3 +. sp -1 +. IP \(bu 2.3 .\} -\fI?\fP indicates sources to which connectivity has been lost or whose packets -do not pass all tests. It is also shown at start\-up, until at least 3 samples -have been gathered from it. +\fIx\fP indicates a source which \fBchronyd\fP thinks is a falseticker (i.e. its +time is inconsistent with a majority of other sources, or sources specified +with the \fBtrust\fP option). .RE .sp .RS 4 @@ -452,11 +460,10 @@ \h'-04'\(bu\h'+03'\c .\} .el \{\ -.sp -1 -.IP \(bu 2.3 +. sp -1 +. IP \(bu 2.3 .\} -\fIx\fP indicates a clock which \fBchronyd\fP thinks is a falseticker (i.e. its -time is inconsistent with a majority of other sources). +\fI~\fP indicates a source whose time appears to have too much variability. .RE .sp .RS 4 @@ -464,13 +471,21 @@ \h'-04'\(bu\h'+03'\c .\} .el \{\ -.sp -1 -.IP \(bu 2.3 +. sp -1 +. IP \(bu 2.3 .\} -\fI~\fP indicates a source whose time appears to have too much variability. +\fI?\fP indicates a source which is not considered to be selectable for +synchronisation for other reasons (e.g. unreachable, not synchronised, or +does not have enough measurements). .RE .RE .sp + +.RS 4 +The \fBselectdata\fP command can be used to get more details about +the selection state. +.RE +.sp \fBName/IP address\fP .RS 4 This shows the name or the IP address of the source, or reference ID for reference @@ -524,29 +539,28 @@ .RE .RE .sp -\fBsourcestats\fP [\fB\-v\fP] +\fBsourcestats\fP [\fB\-a\fP] [\fB\-v\fP] .RS 4 The \fBsourcestats\fP command displays information about the drift rate and offset estimation process for each of the sources currently being examined by \fBchronyd\fP. .sp -The optional argument \fB\-v\fP can be specified, meaning \fIverbose\fP. In this case, +If the \fB\-a\fP option is specified, all sources are displayed, including those that +do not have a known address yet. Such sources have an identifier in the format +\fIID#XXXXXXXXXX\fP, which can be used in other commands expecting a source address. +.sp +The \fB\-v\fP option enables a verbose output. In this case, extra caption lines are shown as a reminder of the meanings of the columns. .sp An example report is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf -210 Number of sources = 1 Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev =============================================================================== foo.example.net 11 5 46m \-0.001 0.045 1us 25us .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The columns are as follows: .sp @@ -602,6 +616,367 @@ .RE .RE .sp +\fBselectdata\fP [\fB\-a\fP] [\fB\-v\fP] +.RS 4 +The \fBselectdata\fP command displays information specific to the selection of time +sources. If the \fB\-a\fP option is specified, all sources are displayed, including +those that do not have a known address yet. With the \fB\-v\fP option, extra caption +lines are shown as a reminder of the meanings of the columns. +.sp +An example of the output is shown below. +.sp +.if n .RS 4 +.nf +S Name/IP Address Auth COpts EOpts Last Score Interval Leap +======================================================================= +D foo.example.net Y \-\-\-\-\- \-\-TR\- 4 1.0 \-61ms +62ms N +* bar.example.net N \-\-\-\-\- \-\-\-\-\- 0 1.0 \-6846us +7305us N ++ baz.example.net N \-\-\-\-\- \-\-\-\-\- 10 1.0 \-7381us +7355us N +.fi +.if n .RE +.sp +The columns are as follows: +.sp +\fBS\fP +.RS 4 +This column indicates the state of the source after the last source selection. +It is similar to the state reported by the \fBsources\fP command, but more +states are reported. +.RE +.sp + +.RS 4 +The following states indicate the source is not considered selectable for +synchronisation: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fIN\fP \- has the \fBnoselect\fP option. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fIM\fP \- does not have enough measurements. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fId\fP \- has a root distance larger than the maximum distance (configured by the +\fBmaxdistance\fP directive). +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fI~\fP \- has a jitter larger than the maximum jitter (configured by the +\fBmaxjitter\fP directive). +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fIw\fP \- waits for other sources to get out of the \fIM\fP state. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fIS\fP \- has older measurements than other sources. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fIO\fP \- has a stratum equal or larger than the orphan stratum (configured by +the \fBlocal\fP directive). +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fIT\fP \- does not fully agree with sources that have the \fBtrust\fP option. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fIx\fP \- does not agree with other sources (falseticker). +.RE +.RE +.sp + +.RS 4 +The following states indicate the source is considered selectable, but it is +not currently used for synchronisation: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fIW\fP \- waits for other sources to be selectable (required by the +\fBminsources\fP directive, or +the \fBrequire\fP option of another source). +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fIP\fP \- another selectable source is preferred due to the \fBprefer\fP option. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fIU\fP \- waits for a new measurement (after selecting a different best source). +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fID\fP \- has, or recently had, a root distance which is too large to be combined +with other sources (configured by the +\fBcombinelimit\fP directive). +.RE +.RE +.sp + +.RS 4 +The following states indicate the source is used for synchronisation of the +local clock: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fI+\fP \- combined with the best source. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fI*\fP \- selected as the best source to update the reference data (e.g. root +delay, root dispersion). +.RE +.RE +.sp +\fBName/IP address\fP +.RS 4 +This column shows the name or IP address of the source if it is an NTP server, +or the reference ID if it is a reference clock. +.RE +.sp +\fBAuth\fP +.RS 4 +This column indicites whether an authentication mechanism is enabled for the +source. \fIY\fP means yes and \fIN\fP means no. +.RE +.sp +\fBCOpts\fP +.RS 4 +This column displays the configured selection options of the source. +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fIN\fP indicates the \fBnoselect\fP option. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fIP\fP indicates the \fBprefer\fP option. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fIT\fP indicates the \fBtrust\fP option. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fIR\fP indicates the \fBrequire\fP option. +.RE +.RE +.sp +\fBEOpts\fP +.RS 4 +This column displays the current effective selection options of the source, +which can be different from the configured options due to the authentication +selection mode (configured by the +\fBauthselmode\fP directive). The symbols are the +same as in the \fBCOpts\fP column. +.RE +.sp +\fBLast\fP +.RS 4 +This column displays how long ago was the last measurement of the source made +when the selection was performed. +.RE +.sp +\fBScore\fP +.RS 4 +This column displays the current score against the source in the \fI*\fP state. The +scoring system avoids frequent reselection when multiple sources have a similar +root distance. A value larger than 1 indicates this source was better than the +\fI*\fP source in recent selections. If the score reaches 10, the best source will +be reselected and the scores will be reset to 1. +.RE +.sp +\fBInterval\fP +.RS 4 +This column displays the lower and upper endpoint of the interval which was +expected to contain the true offset of the local clock considering the root +distance at the time of the selection. +.RE +.sp +\fBLeap\fP +.RS 4 +This column displays the current leap status of the source. +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fIN\fP indicates the normal status (no leap second). +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fI+\fP indicates that a leap second will be inserted at the end of the month. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fI\-\fP indicates that a leap second will be deleted at the end of the month. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\fI?\fP indicates the unknown status (i.e. no valid measurement was made). +.RE +.RE +.RE +.sp \fBreselect\fP .RS 4 To avoid excessive switching between sources, \fBchronyd\fP can stay synchronised @@ -661,15 +1036,258 @@ .RE .RE .sp -\fBntpdata\fP [\fIaddress\fP] +\fBauthdata\fP [\fB\-a\fP] .RS 4 -The \fBntpdata\fP command displays the last valid measurement and other -NTP\-specific information about the specified NTP source, or all NTP sources if -no address was specified. An example of the output is shown below. +The \fBauthdata\fP command displays information specific to authentication of NTP +sources. If the \fB\-a\fP option is specified, all sources are displayed, including +those that do not have a known address yet. An example of the output is +shown below. +.sp +.if n .RS 4 +.nf +Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen +========================================================================= +foo.example.net NTS 1 15 256 135m 0 0 8 100 +bar.example.net SK 30 13 128 \- 0 0 0 0 +baz.example.net \- 0 0 0 \- 0 0 0 0 +.fi +.if n .RE +.sp +The columns are as follows: +.sp +\fBName/IP address\fP +.RS 4 +This column shows the name or the IP address of the source. +.RE +.sp +\fBMode\fP +.RS 4 +This column shows which mechanism authenticates NTP packets received from the +source. \fINTS\fP means Network Time Security, \fISK\fP means a symmetric key, and \fI\-\fP +means authentication is disabled. +.RE +.sp +\fBKeyID\fP +.RS 4 +This column shows an identifier of the key used for authentication. With a +symmetric key, it is the ID from the key file. +With NTS, it is a number starting at zero and incremented by one with each +successful key establishment using the NTS\-KE protocol, i.e. it shows how many +times the key establishment was performed with this source. +.RE +.sp +\fBType\fP +.RS 4 +This columns shows an identifier of the algorithm used for authentication. +With a symmetric key, it is the hash function or cipher specified in the key +file. With NTS, it is an authenticated encryption with associated data (AEAD) +algorithm, which is negotiated in the NTS\-KE protocol. The following values can +be reported: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +1: MD5 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +2: SHA1 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +3: SHA256 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +4: SHA384 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +5: SHA512 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +6: SHA3\-224 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +7: SHA3\-256 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +8: SHA3\-384 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +9: SHA3\-512 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +10: TIGER +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +11: WHIRLPOOL +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +13: AES128 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +14: AES256 +.RE .sp -.if n \{\ .RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c .\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +15: AEAD\-AES\-SIV\-CMAC\-256 +.RE +.RE +.sp +\fBKLen\fP +.RS 4 +This column shows the length of the key in bits. +.RE +.sp +\fBLast\fP +.RS 4 +This column shows how long ago the last successful key establishment was +performed. It is in seconds, or letters \fIm\fP, \fIh\fP, \fId\fP or \fIy\fP indicate minutes, +hours, days, or years. +.RE +.sp +\fBAtmp\fP +.RS 4 +This column shows the number of attempts to perform the key establishment since +the last successful key establishment. A number larger than 1 indicates a +problem with the network or server. +.RE +.sp +\fBNAK\fP +.RS 4 +This column shows whether an NTS NAK was received since the last request. +A NAK indicates that authentication failed on the server side due to +\fBchronyd\fP using a cookie which is no longer valid and that it needs to perform +the key establishment again in order to get new cookies. +.RE +.sp +\fBCook\fP +.RS 4 +This column shows the number of NTS cookies that \fBchronyd\fP currently has. If +the key establishment was successful, a number smaller than 8 indicates a +problem with the network or server. +.RE +.sp +\fBCLen\fP +.RS 4 +This column shows the length in bytes of the NTS cookie which will be used in +the next request. +.RE +.RE +.sp +\fBntpdata\fP [\fIaddress\fP] +.RS 4 +The \fBntpdata\fP command displays the last valid measurement and other +NTP\-specific information about the specified NTP source, or all NTP sources +(with a known address) if no address was specified. An example of the output is +shown below. +.sp +.if n .RS 4 .nf Remote address : 203.0.113.15 (CB00710F) Remote port : 123 @@ -698,9 +1316,7 @@ Total RX : 24 Total valid RX : 24 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The fields are explained as follows: .sp @@ -788,52 +1404,58 @@ .RE .RE .sp -\fBadd peer\fP \fIaddress\fP [\fIoption\fP]... +\fBadd peer\fP \fIname\fP [\fIoption\fP]... .RS 4 The \fBadd peer\fP command allows a new NTP peer to be added whilst \fBchronyd\fP is running. .sp Following the words \fBadd peer\fP, the syntax of the following -parameters and options is similar to that for the +parameters and options is identical to that for the \fBpeer\fP directive in the configuration file. -The following peer options can be set in the command: \fBport\fP, \fBminpoll\fP, -\fBmaxpoll\fP, \fBpresend\fP, \fBmaxdelayratio\fP, \fBmaxdelay\fP, \fBkey\fP. .sp An example of using this command is shown below. .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf add peer foo.example.net minpoll 6 maxpoll 10 key 25 .fi -.if n \{\ +.if n .RE .RE -.\} +.sp +\fBadd pool\fP \fIname\fP [\fIoption\fP]... +.RS 4 +The \fBadd pool\fP command allows a pool of NTP servers to be added whilst +\fBchronyd\fP is running. +.sp +Following the words \fBadd pool\fP, the syntax of the following parameters and +options is identical to that for the \fBpool\fP +directive in the configuration file. +.sp +An example of using this command is shown below: +.sp +.if n .RS 4 +.nf +add pool foo.example.net maxsources 3 iburst +.fi +.if n .RE .RE .sp -\fBadd server\fP \fIaddress\fP [\fIoption\fP]... +\fBadd server\fP \fIname\fP [\fIoption\fP]... .RS 4 The \fBadd server\fP command allows a new NTP server to be added whilst \fBchronyd\fP is running. .sp Following the words \fBadd server\fP, the syntax of the following parameters and -options is similar to that for the \fBserver\fP +options is identical to that for the \fBserver\fP directive in the configuration file. -The following server options can be set in the command: \fBport\fP, \fBminpoll\fP, -\fBmaxpoll\fP, \fBpresend\fP, \fBmaxdelayratio\fP, \fBmaxdelay\fP, \fBkey\fP. .sp An example of using this command is shown below: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf add server foo.example.net minpoll 6 maxpoll 10 key 25 .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBdelete\fP \fIaddress\fP @@ -906,15 +1528,11 @@ .sp An example of the two\-argument form of the command is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf burst 2/10 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp This will cause \fBchronyd\fP to attempt to get two good measurements from each source, stopping after two have been obtained, but in no event will it try more @@ -922,16 +1540,12 @@ .sp Examples of the four\-argument form of the command are: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf burst 2/10 255.255.0.0/1.2.0.0 burst 2/10 2001:db8:789a::/48 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp In the first case, the two out of ten sampling will only be applied to sources whose IPv4 addresses are of the form \fI1.2.x.y\fP, where \fIx\fP and \fIy\fP are @@ -940,15 +1554,11 @@ .sp Example of the three\-argument form of the command is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf burst 2/10 foo.example.net .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBmaxdelay\fP \fIaddress\fP \fIdelay\fP @@ -1015,22 +1625,19 @@ this. .sp There are four forms of the \fBoffline\fP command. The first form is a wildcard, -meaning all sources. The second form allows an IP address mask and a masked +meaning all sources (including sources that do not have a known address yet). +The second form allows an IP address mask and a masked address to be specified. The third form uses CIDR notation. The fourth form uses an IP address or a hostname. These forms are illustrated below. .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf offline offline 255.255.255.0/1.2.3.0 offline 2001:db8:789a::/48 offline foo.example.net .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The second form means that the \fBoffline\fP command is to be applied to any source whose IPv4 address is in the \fI1.2.3\fP subnet. (The host\(cqs address is logically @@ -1041,16 +1648,12 @@ .sp The wildcard form of the address is equivalent to: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf offline 0.0.0.0/0.0.0.0 offline ::/0 .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBonline\fP [\fIaddress\fP], \fBonline\fP [\fImasked\-address\fP/\fImasked\-bits\fP], \fBonline\fP [\fImask\fP/\fImasked\-address\fP] @@ -1064,10 +1667,11 @@ .sp \fBonoffline\fP .RS 4 -The \fBonoffline\fP command tells \fBchronyd\fP to switch all sources to the online or +The \fBonoffline\fP command tells \fBchronyd\fP to switch all sources that have a known +address to the online or offline status according to the current network configuration. A source is -considered online if it is possible to send requests to it, i.e. a route to the -network is present. +considered online if it is possible to send requests to it, i.e. a network +route to the source is present. .RE .sp \fBpolltarget\fP \fIaddress\fP \fIpolltarget\fP @@ -1087,6 +1691,24 @@ automatically after 8 polling intervals, but this command can still be useful to replace them immediately and not wait until they are marked as unreachable. .RE +.sp +\fBreload\fP \fBsources\fP +.RS 4 +The \fBreload sources\fP command causes \fBchronyd\fP to re\-read all \fI*.sources\fP files +from the directories specified by the +\fBsourcedir\fP directive. +.RE +.sp +\fBsourcename\fP \fIaddress\fP +.RS 4 +The \fBsourcename\fP command prints the original hostname or address that was +specified for an NTP source in the configuration file, or the \fBadd\fP command. +This command is an alternative to the \fB\-N\fP option, which can be useful in +scripts. +.sp +Note that different NTP sources can share the same name, e.g. servers from a +pool. +.RE .SS "Manual time input" .sp \fBmanual\fP \fBon\fP, \fBmanual\fP \fBoff\fP, \fBmanual\fP \fBdelete\fP \fIindex\fP, \fBmanual\fP \fBlist\fP, \fBmanual\fP \fBreset\fP @@ -1101,18 +1723,14 @@ The \fBlist\fP form of the command lists all the samples currently stored in \fBchronyd\fP. The output is illustrated below. .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf 210 n_samples = 1 # Date Time(UTC) Slewed Original Residual ==================================================== 0 27Jan99 22:09:20 0.00 0.97 0.00 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The columns are as as follows: .sp @@ -1121,8 +1739,8 @@ \h'-04' 1.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 1." 4.2 +. sp -1 +. IP " 1." 4.2 .\} The sample index (used for the \fBmanual delete\fP command). .RE @@ -1132,8 +1750,8 @@ \h'-04' 2.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 2." 4.2 +. sp -1 +. IP " 2." 4.2 .\} The date and time of the sample. .RE @@ -1143,8 +1761,8 @@ \h'-04' 3.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 3." 4.2 +. sp -1 +. IP " 3." 4.2 .\} The system clock error when the timestamp was entered, adjusted to allow for changes made to the system clock since. @@ -1155,8 +1773,8 @@ \h'-04' 4.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 4." 4.2 +. sp -1 +. IP " 4." 4.2 .\} The system clock error when the timestamp was entered, as it originally was (without allowing for changes to the system clock since). @@ -1167,8 +1785,8 @@ \h'-04' 5.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 5." 4.2 +. sp -1 +. IP " 5." 4.2 .\} The regression residual at this point, in seconds. This allows \(oqoutliers\(cq to be easily spotted, so that they can be deleted using the \fBmanual delete\fP @@ -1223,17 +1841,13 @@ .sp Examples of inputs that are valid are shown below: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf settime 16:30 settime 16:30:05 settime Nov 21, 2015 16:30:05 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp For a full description of getdate, see the getdate documentation (bundled, for example, with the source for GNU tar). @@ -1247,43 +1861,42 @@ .sp Examples of use, showing a named host and a numeric IP address, are as follows: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf accheck foo.example.net accheck 1.2.3.4 accheck 2001:db8::1 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp This command can be used to examine the effect of a series of \fBallow\fP, \fBallow all\fP, \fBdeny\fP, and \fBdeny all\fP commands specified either via \fBchronyc\fP, or in \fBchronyd\fP\(cqs configuration file. .RE .sp -\fBclients\fP +\fBclients\fP [\fB\-p\fP \fIpackets\fP] [\fB\-k\fP] [\fB\-r\fP] .RS 4 This command shows a list of clients that have accessed the server, through -either the NTP or command ports. It does not include accesses over -the Unix domain command socket. There are no arguments. +the NTP, command, or NTS\-KE port. It does not include accesses over the Unix +domain command socket. +.sp +The \fB\-p\fP option specifies the minimum number of received NTP or command +packets, or accepted NTS\-KE connections, needed to include a client in the +list. The default value is 0, i.e. all clients are reported. With the \fB\-k\fP +option the last four columns will show the NTS\-KE accesses instead of command +accesses. If the \fB\-r\fP option is specified, \fBchronyd\fP will reset the counters of +received and dropped packets or connections after reporting the current values. .sp An example of the output is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf Hostname NTP Drop Int IntL Last Cmd Drop Int Last =============================================================================== localhost 2 0 2 \- 133 15 0 \-1 7 foo.example.net 12 0 6 \- 23 0 0 \- \- .fi -.if n \{\ -.RE -.\} +.if n .RE .sp Each row shows the data for a single host. Only hosts that have passed the host access checks (set with the \fBallow\fP, \fBdeny\fP, @@ -1298,8 +1911,8 @@ \h'-04' 1.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 1." 4.2 +. sp -1 +. IP " 1." 4.2 .\} The hostname of the client. .RE @@ -1309,8 +1922,8 @@ \h'-04' 2.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 2." 4.2 +. sp -1 +. IP " 2." 4.2 .\} The number of NTP packets received from the client. .RE @@ -1320,8 +1933,8 @@ \h'-04' 3.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 3." 4.2 +. sp -1 +. IP " 3." 4.2 .\} The number of NTP packets dropped to limit the response rate. .RE @@ -1331,8 +1944,8 @@ \h'-04' 4.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 4." 4.2 +. sp -1 +. IP " 4." 4.2 .\} The average interval between NTP packets. .RE @@ -1342,8 +1955,8 @@ \h'-04' 5.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 5." 4.2 +. sp -1 +. IP " 5." 4.2 .\} The average interval between NTP packets after limiting the response rate. .RE @@ -1353,8 +1966,8 @@ \h'-04' 6.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 6." 4.2 +. sp -1 +. IP " 6." 4.2 .\} Time since the last NTP packet was received .RE @@ -1364,10 +1977,11 @@ \h'-04' 7.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 7." 4.2 +. sp -1 +. IP " 7." 4.2 .\} -The number of command packets received from the client. +The number of command packets or NTS\-KE connections received/accepted from +the client. .RE .sp .RS 4 @@ -1375,10 +1989,11 @@ \h'-04' 8.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 8." 4.2 +. sp -1 +. IP " 8." 4.2 .\} -The number of command packets dropped to limit the response rate. +The number of command packets or NTS\-KE connections dropped to limit the +response rate. .RE .sp .RS 4 @@ -1386,10 +2001,10 @@ \h'-04' 9.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 9." 4.2 +. sp -1 +. IP " 9." 4.2 .\} -The average interval between command packets. +The average interval between command packets or NTS\-KE connections. .RE .sp .RS 4 @@ -1397,37 +2012,36 @@ \h'-04' 10.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 10." 4.2 +. sp -1 +. IP " 10." 4.2 .\} -Time since the last command packet was received. +Time since the last command packet or NTS\-KE connection was +received/accepted. .RE .RE .sp \fBserverstats\fP .RS 4 -The \fBserverstats\fP command displays how many valid NTP and command requests -\fBchronyd\fP as a server received from clients, how many of them were dropped to -limit the response rate as configured by the -\fBratelimit\fP and -\fBcmdratelimit\fP directives, and how many +The \fBserverstats\fP command displays how many valid NTP and command requests, and +NTS\-KE connections, \fBchronyd\fP operating as a server received from clients, and +how many of them were dropped due to rate limiting. It also displays how many client log records were dropped due to the memory limit configured by the -\fBclientloglimit\fP directive. An example of -the output is shown below. +\fBclientloglimit\fP directive and how many of +the NTP requests (from those which were not dropped) were authenticated. An +example of the output is shown below. .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf NTP packets received : 1598 NTP packets dropped : 8 Command packets received : 19 Command packets dropped : 0 Client log records dropped : 0 +NTS\-KE connections accepted: 3 +NTS\-KE connections dropped : 0 +Authenticated NTP packets : 189 .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBallow\fP [\fBall\fP] [\fIsubnet\fP] @@ -1437,24 +2051,17 @@ .sp The syntax is illustrated in the following examples: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf -allow foo.example.net -allow all 1.2 -allow 3.4.5 -allow 6.7.8/22 -allow 6.7.8.9/22 +allow 1.2.3.4 +allow all 3.4.5.0/24 allow 2001:db8:789a::/48 allow 0/0 allow ::/0 allow allow all .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBdeny\fP [\fBall\fP] [\fIsubnet\fP] @@ -1464,24 +2071,17 @@ .sp The syntax is illustrated in the following examples: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf -deny foo.example.net -deny all 1.2 -deny 3.4.5 -deny 6.7.8/22 -deny 6.7.8.9/22 +deny 1.2.3.4 +deny all 3.4.5.0/24 deny 2001:db8:789a::/48 deny 0/0 deny ::/0 deny deny all .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBlocal\fP [\fIoption\fP]..., \fBlocal\fP \fBoff\fP @@ -1505,9 +2105,7 @@ \fBsmoothtime\fP directive. An example of the output is shown below. .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf Active : Yes Offset : +1.000268817 seconds @@ -1516,9 +2114,7 @@ Last update : 17.8 seconds ago Remaining time : 19988.4 seconds .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The fields are explained as follows: .sp @@ -1575,17 +2171,13 @@ .sp Examples of use are as follows: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf cmdaccheck foo.example.net cmdaccheck 1.2.3.4 cmdaccheck 2001:db8::1 .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBcmdallow\fP [\fBall\fP] [\fIsubnet\fP] @@ -1609,9 +2201,7 @@ .sp An example output is shown below. .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf RTC ref time (GMT) : Sat May 30 07:25:56 2015 Number of samples : 10 @@ -1620,9 +2210,7 @@ RTC is fast by : \-1.632736 seconds RTC gains time at : \-107.623 ppm .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The fields have the following meaning: .sp @@ -1687,8 +2275,8 @@ \h'-04' 1.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 1." 4.2 +. sp -1 +. IP " 1." 4.2 .\} Remember the currently estimated gain or loss rate of the RTC and flush the previous measurements. @@ -1699,8 +2287,8 @@ \h'-04' 2.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 2." 4.2 +. sp -1 +. IP " 2." 4.2 .\} Step the real\-time clock to bring it within a second of the system clock. .RE @@ -1710,8 +2298,8 @@ \h'-04' 3.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 3." 4.2 +. sp -1 +. IP " 3." 4.2 .\} Make several measurements to accurately determine the new offset between the RTC and the system clock (i.e. the remaining fraction of a second @@ -1723,8 +2311,8 @@ \h'-04' 4.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 4." 4.2 +. sp -1 +. IP " 4." 4.2 .\} Save the RTC parameters to the RTC file (specified with the \fBrtcfile\fP directive in the configuration file). @@ -1765,20 +2353,13 @@ and re\-opened. This allows them to be renamed so that they can be periodically purged. An example of how to do this is shown below. .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf # mv /var/log/chrony/measurements.log /var/log/chrony/measurements1.log # chronyc cyclelogs -# ls \-l /var/log/chrony -\-rw\-r\-\-r\-\- 1 root root 0 Jun 8 18:17 measurements.log -\-rw\-r\-\-r\-\- 1 root root 12345 Jun 8 18:17 measurements1.log -# rm \-f measurements1.log +# rm /var/log/chrony/measurements1.log .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBdump\fP @@ -1786,15 +2367,31 @@ The \fBdump\fP command causes \fBchronyd\fP to write its current history of measurements for each of its sources to dump files in the directory specified in the configuration file by the \fBdumpdir\fP +directive and also write server NTS keys and client NTS cookies to the +directory specified by the \fBntsdumpdir\fP directive. Note that \fBchronyd\fP does this automatically when it exits. This -command is mainly useful for inspection of the history whilst \fBchronyd\fP is -running. +command is mainly useful for inspection whilst \fBchronyd\fP is running. .RE .sp \fBrekey\fP .RS 4 The \fBrekey\fP command causes \fBchronyd\fP to re\-read the key file specified in the -configuration file by the \fBkeyfile\fP directive. +configuration file by the \fBkeyfile\fP directive. It +also re\-reads the server NTS keys if +\fBntsdumpdir\fP is specified and +automatic rotation is disabled in the +configuration file. +.RE +.sp +\fBreset\fP \fBsources\fP +.RS 4 +The \fBreset sources\fP command causes \fBchronyd\fP to drop all measurements and +switch to the unsynchronised state. This command can help \fBchronyd\fP with +recovery when the measurements are known to be no longer valid or accurate, +e.g. due to moving the computer to a different network, or resuming the +computer from a low\-power state (which resets the system clock). \fBchronyd\fP will +drop the measurements automatically when it detects the clock has made an +unexpected jump, but the detection is not completely reliable. .RE .sp \fBshutdown\fP @@ -1871,26 +2468,30 @@ .sp The command has three optional arguments. The first argument is the key number (by default 1), which will be specified with the \fBkey\fP option of the \fBserver\fP -or \fBpeer\fP directives in the configuration file. The second argument is the hash -function (by default SHA1 or MD5 if SHA1 is not available) and the third -argument is the number of bits the key should have, between 80 and 4096 bits -(by default 160 bits). +or \fBpeer\fP directives in the configuration file. The second argument is the name +of the hash function or cipher (by default SHA1, or MD5 if SHA1 is not +available). The third argument is the length of the key in bits if a hash +function was selected, between 80 and 4096 bits (by default 160 bits). .sp An example is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf keygen 73 SHA1 256 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp which generates a 256\-bit SHA1 key with number 73. The printed line should then be securely transferred and added to the key files on both server and -client, or peers. +client, or peers. A different key should be generated for each client or peer. +.sp +An example using the AES128 cipher is: +.sp +.if n .RS 4 +.nf +keygen 151 AES128 +.fi +.if n .RE .RE .sp \fBexit\fP, \fBquit\fP diff -Nru chrony-3.5/doc/chrony.conf.adoc chrony-4.1/doc/chrony.conf.adoc --- chrony-3.5/doc/chrony.conf.adoc 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/doc/chrony.conf.adoc 2021-05-12 11:06:15.000000000 +0000 @@ -3,7 +3,7 @@ // Copyright (C) Richard P. Curnow 1997-2003 // Copyright (C) Stephen Wadeley 2016 // Copyright (C) Bryan Christianson 2017 -// Copyright (C) Miroslav Lichvar 2009-2017 +// Copyright (C) Miroslav Lichvar 2009-2021 // // This program is free software; you can redistribute it and/or modify // it under the terms of version 2 of the GNU General Public License as @@ -32,12 +32,14 @@ == DESCRIPTION This file configures the *chronyd* daemon. The compiled-in location is -_@SYSCONFDIR@/chrony.conf_, but other locations can be specified on the +_@SYSCONFDIR@/chrony.conf_. Other locations can be specified on the *chronyd* command line with the *-f* option. Each directive in the configuration file is placed on a separate line. The -following sections describe each of the directives in turn. The directives can -occur in any order in the file and they are not case-sensitive. +following sections describe each of the directives in turn. The directives are +not case-sensitive. Generally, the directives can occur in any order in the file +and if a directive is specified multiple times, only the last one will be +effective. Exceptions are noted in the descriptions. The configuration directives can also be specified directly on the *chronyd* command line. In this case each argument is parsed as a new line and the @@ -61,9 +63,10 @@ synchronise its system time to that of the server, but the server's system time will never be influenced by that of a client. + -The *server* directive is immediately followed by either the name of the -server, or its IP address. The *server* directive supports the following -options: +This directive can be used multiple times to specify multiple servers. ++ +The directive is immediately followed by either the name of the +server, or its IP address. It supports the following options: + *minpoll* _poll_::: This option specifies the minimum interval between requests sent to the server @@ -82,13 +85,13 @@ seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6 months). *iburst*::: -With this option, the interval between the first four requests sent to the -server will be 2 seconds or less instead of the interval specified by the -*minpoll* option, which allows *chronyd* to make the first update of the clock -shortly after start. +With this option, *chronyd* will start with a burst of 4-8 requests in order to +make the first update of the clock sooner. It will also repeat the burst every +time the source is switched from the offline state to online with the +<> command in *chronyc*. *burst*::: -With this option, *chronyd* will shorten the interval between up to four -requests to 2 seconds or less when it cannot get a good measurement from the +With this option, *chronyd* will send a burst of up to 4 requests when it +cannot get a good measurement from the server. The number of requests in the burst is limited by the current polling interval to keep the average interval at or above the minimum interval, i.e. the current interval needs to be at least two times longer than the minimum @@ -96,7 +99,7 @@ *key* _ID_::: The NTP protocol supports a message authentication code (MAC) to prevent computers having their system time upset by rogue packets being sent to them. -The MAC is generated as a function of a password specified in the key file, +The MAC is generated as a function of a key specified in the key file, which is specified by the <> directive. + The *key* option specifies which key (with an ID in the range 1 through 2^32-1) @@ -107,6 +110,18 @@ If the server is running *ntpd* and the output size of the hash function used by the key is longer than 160 bits (e.g. SHA256), the *version* option needs to be set to 4 for compatibility. +*nts*::: +This option enables authentication using the Network Time Security (NTS) +mechanism. Unlike with the *key* option, the server and client do not need to +share a key in a key file. NTS has a Key Establishment (NTS-KE) protocol using +the Transport Layer Security (TLS) protocol to get the keys and cookies +required by NTS for authentication of NTP packets. +*certset* _ID_::: +This option specifies which set of trusted certificates should be used to verify +the server's certificate when the *nts* option is enabled. Sets of certificates +can be specified with the <> directive. The +default set is 0, which by default contains certificates of the system's +default trusted certificate authorities. *maxdelay* _delay_::: *chronyd* uses the network round-trip delay to the server to determine how accurate a particular measurement is likely to be. Long round-trip delays @@ -116,9 +131,9 @@ For small variations in the round-trip delay, *chronyd* uses a weighting scheme when processing the measurements. However, beyond a certain level of delay the measurements are likely to be so corrupted as to be useless. (This is -particularly so on dial-up or other slow links, where a long delay probably -indicates a highly asymmetric delay caused by the response waiting behind a lot -of packets related to a download of some sort). +particularly so on wireless networks and other slow links, where a long delay +probably indicates a highly asymmetric delay caused by the response waiting +behind a lot of packets related to a download of some sort). + If the user knows that round trip delays above a certain level should cause the measurement to be ignored, this level can be defined with the *maxdelay* @@ -129,7 +144,7 @@ This option is similar to the *maxdelay* option above. *chronyd* keeps a record of the minimum round-trip delay amongst the previous measurements that it has buffered. If a measurement has a round trip delay that is greater than the -maxdelayratio times the minimum delay, it will be rejected. +specified ratio times the minimum delay, it will be rejected. *maxdelaydevratio* _ratio_::: If a measurement has a ratio of the increase in the round-trip delay from the minimum delay amongst the previous measurements to the standard deviation of @@ -194,14 +209,14 @@ order to improve the accuracy of the clock. They can be selected and used for synchronisation only if they agree with the trusted and required source. *xleave*::: -This option enables an interleaved mode which allows the server or the peer to -send transmit timestamps captured after the actual transmission (e.g. when the -server or the peer is running *chronyd* with software (kernel) or hardware -timestamping). This can significantly improve the accuracy of the measurements. +This option enables the interleaved mode of NTP. It enables the server to +respond with more accurate transmit timestamps (e.g. kernel or hardware +timestamps), which cannot be contained in the transmitted packet itself and +need to refer to a previous packet instead. This can significantly improve the +accuracy and stability of the measurements. + The interleaved mode is compatible with servers that support only the basic -mode, but peers must both support and have enabled the interleaved mode, -otherwise the synchronisation will work only in one direction. Note that even +mode. Note that even servers that support the interleaved mode might respond in the basic mode as the interleaved mode requires the servers to keep some state for each client and the state might be dropped when there are too many clients (e.g. @@ -221,6 +236,9 @@ This option allows the UDP port on which the server understands NTP requests to be specified. For normal servers this option should not be required (the default is 123, the standard NTP port). +*ntsport* _port_::: +This option specifies the TCP port on which the server is listening for NTS-KE +connections when the *nts* option is enabled. The default is 4460. *presend* _poll_::: If the timing measurements being made by *chronyd* are the only network data passing between two computers, you might find that some measurements are badly @@ -243,13 +261,13 @@ will be sent to the server a short time (2 seconds) before making the actual measurement. + -The *presend* option cannot be used in the *peer* directive. If it is used -with the *xleave* option, *chronyd* will send two extra packets instead of one. +If the *presend* option is used together with the *xleave* option, *chronyd* +will send two extra packets instead of one. *minstratum* _stratum_::: When the synchronisation source is selected from available sources, sources with lower stratum are normally slightly preferred. This option can be used to increase stratum of the source to the specified minimum, so *chronyd* will -avoid selecting that source. This is useful with low stratum sources that are +avoid selecting that source. This is useful with low-stratum sources that are known to be unreliable or inaccurate and which should be used only when other sources are unreachable. *version* _version_::: @@ -260,6 +278,15 @@ is using. If the output size of the hash function is longer than 160 bits, the default version is 3 for compatibility with older *chronyd* servers. Otherwise, the default version is 4. +*copy*::: +This option specifies that the server and client are closely related, their +configuration does not allow a synchronisation loop to form between them, and +the client is allowed to assume the reference ID and stratum of the server. +This is useful when multiple instances of `chronyd` are running on one computer +(e.g. for security or performance reasons), one primarily operating as a client +to synchronise the system clock and other instances started with the *-x* +option to operate as NTP servers for other computers with their NTP clocks +synchronised to the first instance. [[pool]]*pool* _name_ [_option_]...:: The syntax of this directive is similar to that for the <> @@ -267,18 +294,22 @@ a single NTP server. The pool name is expected to resolve to multiple addresses which might change over time. + +This directive can be used multiple times to specify multiple pools. ++ All options valid in the <> directive can be used in this directive too. There is one option specific to the *pool* directive: -*maxsources* sets the maximum number of sources that can be used from the pool, -the default value is 4. + -On start, when the pool name is resolved, *chronyd* will add up to 16 sources, -one for each resolved address. When the number of sources from which at least -one valid reply was received reaches the number specified by the *maxsources* -option, the other sources will be removed. When a pool source is unreachable, +*maxsources* _sources_::: +This option sets the desired number of sources to be used from the pool. +*chronyd* will repeatedly try to resolve the name until it gets this number of +sources responding to requests. The default value is 4 and the maximum value is +16. ++ +{blank}:: +When an NTP source is unreachable, marked as a falseticker, or has a distance larger than the limit set by the <> directive, *chronyd* will try to replace the -source with a newly resolved address from the pool. +source with a newly resolved address of the name. + An example of the *pool* directive is + @@ -295,6 +326,14 @@ ephemeral symmetric associations and does not need to be configured with an address of this host. *chronyd* does not support ephemeral associations. + +This directive can be used multiple times to specify multiple peers. ++ +The following options of the *server* directive do not work in the *peer* +directive: *iburst*, *burst*, *nts*, *presend*, *copy*. ++ +When using the *xleave* option, both peers must support and have enabled the +interleaved mode, otherwise the synchronisation will work in one direction +only. When a key is specified by the *key* option to enable authentication, both peers must use the same key and the same key number. + @@ -317,19 +356,8 @@ <> directive on both hosts) instead. [[initstepslew]]*initstepslew* _step-threshold_ [_hostname_]...:: -In normal operation, *chronyd* slews the time when it needs to adjust the -system clock. For example, to correct a system clock which is 1 second slow, -*chronyd* slightly increases the amount by which the system clock is advanced -on each clock interrupt, until the error is removed. Note that at no time does -time run backwards with this method. -+ -On most Unix systems it is not desirable to step the system clock, because many -programs rely on time advancing monotonically forwards. -+ -When the *chronyd* daemon is initially started, it is possible that the system -clock is considerably in error. Attempting to correct such an error by slewing -might not be sensible, since it might take several hours to correct the error by -this means. +(This directive is deprecated in favour of the <> +directive.) + The purpose of the *initstepslew* directive is to allow *chronyd* to make a rapid measurement of the system clock error at boot time, and to correct the @@ -350,29 +378,30 @@ An example of the use of the directive is: + ---- -initstepslew 30 foo.example.net bar.example.net +initstepslew 30 foo.example.net bar.example.net baz.example.net ---- + -where 2 NTP servers are used to make the measurement. The _30_ indicates that +where 3 NTP servers are used to make the measurement. The _30_ indicates that if the system's error is found to be 30 seconds or less, a slew will be used to correct it; if the error is above 30 seconds, a step will be used. + The *initstepslew* directive can also be used in an isolated LAN environment, where the clocks are set manually. The most stable computer is chosen as the -master, and the other computers are slaved to it. If each of the slaves is -configured with the <> directive, the master can be set up with -an *initstepslew* directive which references some or all of the slaves. Then, -if the master machine has to be rebooted, the slaves can be relied on to act -analogously to a flywheel and preserve the time for a short period while the -master completes its reboot. +primary server and the other computers are its clients. If each of the clients +is configured with the <> directive, the server can be set up +with an *initstepslew* directive which references some or all of the clients. +Then, if the server machine has to be rebooted, the clients can be relied on to +act analogously to a flywheel and preserve the time for a short period while +the server completes its reboot. + The *initstepslew* directive is functionally similar to a combination of the <> and <> directives with the *iburst* option. The main difference is that the *initstepslew* servers are used only before normal operation begins and that the foreground *chronyd* process waits -for *initstepslew* to finish before exiting. This is useful to prevent programs -started in the boot sequence after *chronyd* from reading the clock before it -has been stepped. +for *initstepslew* to finish before exiting. This prevent programs started in +the boot sequence after *chronyd* from reading the clock before it has been +stepped. With the *makestep* directive, the +<> command of *chronyc* can be used instead. [[refclock]]*refclock* _driver_ _parameter_[:__option__]... [_option_]...:: The *refclock* directive specifies a hardware reference clock to be used as a @@ -381,6 +410,8 @@ refclock options. Some drivers have special options, which can be appended to the driver-specific parameter using the *:* character. + +This directive can be used multiple times to specify multiple reference clocks. ++ There are four drivers included in *chronyd*: + *PPS*::: @@ -396,7 +427,7 @@ synchronisation. With this option, it will use clear events (falling edge) instead. + -::: +{blank}::: Examples: + ---- @@ -415,7 +446,7 @@ This option specifies the permissions of the shared memory segment created by *chronyd*. They are specified as a numeric mode. The default value is 0600 (read-write access for owner only). -::: +{blank}::: + Examples: + @@ -469,7 +500,7 @@ This option enables timestamping of clear events (falling edge) instead of assert events (rising edge) in the PPS mode. This may not work with some clocks. -::: +{blank}::: + Examples: + @@ -479,7 +510,7 @@ refclock PHC /dev/ptp2:extpps:pin=1 width 0.2 poll 2 ---- + -:: +{blank}:: The *refclock* directive supports the following options: + *poll* _poll_::: @@ -593,12 +624,13 @@ time to be provided.) [[acquisitionport]]*acquisitionport* _port_:: -By default, *chronyd* uses a separate client socket for each configured server -and their source port is chosen arbitrarily by the operating system. However, -you can use the *acquisitionport* directive to explicitly specify a port and -use only one socket (per IPv4 or IPv6 address family) for all configured servers. -This can be useful for getting through some firewalls. If set to 0, the source -port of the socket will be chosen arbitrarily. +By default, *chronyd* as an NTP client opens a new socket for each request with +the source port chosen randomly by the operating system. The *acquisitionport* +directive can be used to specify the source port and use only one socket (per +IPv4 or IPv6 address family) for all configured servers. This can be useful for +getting through some firewalls. It should not be used if not necessary as there +is a small impact on security of the client. If set to 0, the source port of +the permanent socket will be chosen randomly by the operating system. + It can be set to the same port as is used by the NTP server (which can be configured with the <> directive) to use only one socket for all @@ -614,14 +646,40 @@ You could then persuade the firewall administrator to open that port. [[bindacqaddress]]*bindacqaddress* _address_:: -The *bindacqaddress* directive sets the network interface to which -*chronyd* will bind its NTP client sockets. The syntax is similar to the -<> and <> +The *bindacqaddress* directive specifies a local IP address to which +*chronyd* will bind its NTP and NTS-KE client sockets. The syntax is similar to +the <> and <> directives. + For each of the IPv4 and IPv6 protocols, only one *bindacqaddress* directive can be specified. +[[bindacqdevice]]*bindacqdevice* _interface_:: +The *bindacqdevice* directive binds the client sockets to a network device +specified by the interface name. This can be useful when the local address is +dynamic, or to enable an NTP source specified with a link-local IPv6 address. +This directive can specify only one interface and it is supported on Linux +only. ++ +An example of the directive is: ++ +---- +bindacqdevice eth0 +---- + +[[dscp]]*dscp* _point_:: +The *dscp* directive sets the Differentiated Services Code Point (DSCP) in +transmitted NTP packets to the specified value. It can improve stability of NTP +measurements in local networks where switches or routers are configured to +prioritise forwarding of packets with specific DSCP values. The default value +is 0 and the maximum value is 63. ++ +An example of the directive (setting the Expedited Forwarding class) is: ++ +---- +dscp 46 +---- + [[dumpdir]]*dumpdir* _directory_:: To compute the rate of gain or loss of time, *chronyd* has to store a measurement history for each of the time sources it uses. @@ -639,6 +697,12 @@ directory where the measurement histories are saved when *chronyd* exits, or the <> command in *chronyc* is issued. + +If the directory does not exist, it will be created automatically. ++ +The *-r* option of *chronyd* enables loading of the dump files on start. All +dump files found in the directory will be removed after start, even if the *-r* +option is not present. ++ An example of the directive is: + ---- @@ -655,6 +719,10 @@ individual sources in the <> and <> directives. The default value is 0, which disables the configurable limit. The useful range is 4 to 64. ++ +As a special case, setting *maxsamples* to 1 disables frequency tracking in +order to make the sources immediately selectable with only one sample. This can +be useful when *chronyd* is started with the *-q* or *-Q* option. [[minsamples]]*minsamples* _samples_:: The *minsamples* directive sets the default minimum number of samples that @@ -669,8 +737,154 @@ <> reports (and the _tracking.log_ and _statistics.log_ files) may be smaller than the actual offsets. +[[ntsdumpdir1]]*ntsdumpdir* _directory_:: +This directive specifies a directory for the client to save NTS cookies it +received from the server in order to avoid making an NTS-KE request when +*chronyd* is started again. The cookies are saved separately for each NTP +source in files named by the IP address of the NTS-KE server (e.g. +_1.2.3.4.nts_). By default, the client does not save the cookies. ++ +If the directory does not exist, it will be created automatically. ++ +An example of the directive is: ++ +---- +ntsdumpdir @CHRONYVARDIR@ +---- ++ +This directory is used also by the <> to save keys. + +[[ntsrefresh]]*ntsrefresh* _interval_:: +This directive specifies the maximum interval between NTS-KE handshakes (in +seconds) in order to refresh the keys authenticating NTP packets. The default +value is 2419200 (4 weeks) and the maximum value is 2^31-1 (68 years). + +[[ntstrustedcerts]]*ntstrustedcerts* [_set-ID_] _file_|_directory_:: +This directive specifies a file or directory containing certificates (in the +PEM format) of trusted certificate authorities (CA) which can be used to +verify certificates of NTS servers. ++ +The optional _set-ID_ argument is a number in the range 0 through 2^32-1, which +selects the set of certificates where certificates from the specified file +or directory are added. The default ID is 0, which is a set containing the +system's default trusted CAs (unless the *nosystemcert* directive is present). +All other sets are empty by default. A set of certificates can be selected for +verification of an NTS server by the *certset* option in the *server* or *pool* +directive. ++ +This directive can be used multiple times to specify one or more sets of +trusted certificates, each containing certificates from one or more files +and/or directories. ++ +It is not necessary to restart *chronyd* in order to reload the certificates if +they change (e.g. after a renewal). ++ +An example is: ++ +---- +ntstrustedcerts /etc/pki/nts/foo.crt +ntstrustedcerts 1 /etc/pki/nts/bar.crt +ntstrustedcerts 1 /etc/pki/nts/baz.crt +ntstrustedcerts 2 /etc/pki/nts/qux.crt +---- + +[[nosystemcert]]*nosystemcert*:: +This directive disables the system's default trusted CAs. Only certificates +specified by the *ntstrustedcerts* directive will be trusted. + +[[nocerttimecheck]]*nocerttimecheck* _limit_:: +This directive disables the checks of the activation and expiration times of +certificates for the specified number of clock updates. It allows the NTS +authentication mechanism to be used on computers which start with wrong time +(e.g. due to not having an RTC or backup battery). Disabling the time checks +has important security implications and should be used only as a last resort, +preferably with a minimal number of trusted certificates. The default value is +0, which means the time checks are always enabled. ++ +An example of the directive is: ++ +---- +nocerttimecheck 1 +---- ++ +This would disable the time checks until the clock is updated for the first +time, assuming the first update corrects the clock and later checks can work +with correct time. + === Source selection +[[authselectmode]]*authselectmode* _mode_:: +NTP sources can be specified with the *key* or *nts* option to enable +authentication to limit the impact of man-in-the-middle attacks. The +attackers can drop or delay NTP packets (up to the *maxdelay* and +<> limits), but they cannot modify the timestamps +contained in the packets. The attack can cause only a limited slew or step, and +also cause the clock to run faster or slower than real time (up to double of +the <> limit). ++ +When authentication is enabled for an NTP source, it is important to disable +unauthenticated NTP sources which could be exploited in the attack, e.g. if +they are not reachable only over a trusted network. Alternatively, the source +selection can be configured with the *require* and *trust* options to +synchronise to the unauthenticated sources only if they agree with the +authenticated sources and might have a positive impact on the accuracy of the +clock. Note that in this case the impact of the attack is higher. The attackers +cannot cause an arbitrarily large step or slew, but they have more control over +the frequency of the clock and can cause *chronyd* to report false information, +e.g. a significantly smaller root delay and dispersion. ++ +This directive determines the default selection options for authenticated and +unauthenticated sources in order to simplify the configuration with the +configuration file and *chronyc* commands. It sets a policy for authentication. ++ +Sources specified with the *noselect* option are ignored (not counted as either +authenticated or unauthenticated), and they always have only the selection +options specified in the configuration. ++ +There are four modes: ++ +*require*::: +Authentication is strictly required for NTP sources in this mode. If any +unauthenticated NTP sources are specified, they will automatically get the +*noselect* option to prevent them from being selected for synchronisation. +*prefer*::: +In this mode, authentication is optional and preferred. If it is enabled for at +least one NTP source, all unauthenticated NTP sources will get the *noselect* +option. +*mix*::: +In this mode, authentication is optional and synchronisation to a mix of +authenticated and unauthenticated NTP sources is allowed. If both authenticated +and unauthenticated NTP sources are specified, all authenticated NTP sources +and reference clocks will get the *require* and *trust* options to prevent +synchronisation to unauthenticated NTP sources if they do not agree with a +majority of the authenticated sources and reference clocks. This is the default +mode. +*ignore*::: +In this mode, authentication is ignored in the source selection. All sources +will have only the selection options that were specified in the configuration +file, or *chronyc* command. This was the behaviour of *chronyd* in versions +before 4.0. +{blank}:: ++ +As an example, the following configuration using the default *mix* mode: ++ +---- +server foo.example.net nts +server bar.example.net nts +server baz.example.net +refclock SHM 0 +---- ++ +is equivalent to the following configuration using the *ignore* mode: ++ +---- +authselectmode ignore +server foo.example.net nts require trust +server bar.example.net nts require trust +server baz.example.net +refclock SHM 0 require trust +---- + [[combinelimit]]*combinelimit* _limit_:: When *chronyd* has multiple sources available for synchronisation, it has to select one source as the synchronisation source. The measured offsets and @@ -689,11 +903,11 @@ the system clock. [[maxdistance]]*maxdistance* _distance_:: -The *maxdistance* directive sets the maximum allowed root distance of the -sources to not be rejected by the source selection algorithm. The distance -includes the accumulated dispersion, which might be large when the source is no -longer synchronised, and half of the total round-trip delay to the primary -source. +The *maxdistance* directive sets the maximum root distance of a source to be +acceptable for synchronisation of the clock. Sources that have a distance +larger than the specified distance will be rejected. The distance estimates the +maximum error of the source. It includes the root dispersion and half of the +root delay (round-trip time) accumulated on the path to the primary source. + By default, the maximum root distance is 3 seconds. + @@ -737,6 +951,27 @@ === System clock +[[clockprecision]]*clockprecision* _precision_:: +The *clockprecision* directive specifies the precision of the system clock (in +seconds). It is used by *chronyd* to estimate the minimum noise in NTP +measurements and randomise low-order bits of timestamps in NTP responses. By +default, the precision is measured on start as the minimum time to read the +clock. ++ +The measured value works well in most cases. However, it generally +overestimates the precision and it can be sensitive to the CPU speed, which can +change over time to save power. In some cases with a high-precision clocksource +(e.g. the Time Stamp Counter of the CPU) and hardware timestamping, setting the +precision on the server to a smaller value can improve stability of clients' +NTP measurements. The server's precision is reported on clients by the +<> command. ++ +An example setting the precision to 8 nanoseconds is: ++ +---- +clockprecision 8e-9 +---- + [[corrtimeratio]]*corrtimeratio* _ratio_:: When *chronyd* is slewing the system clock to correct an offset, the rate at which it is slewing adds to the frequency error of the clock. On all supported @@ -845,7 +1080,7 @@ No correction is applied to the clock for the leap second. The clock will be corrected later in normal operation when new measurements are made and the estimated offset includes the one second error. -:: +{blank}:: + When serving time to NTP clients that cannot be configured to correct their clocks for a leap second by slewing, or to clients that would correct at @@ -854,7 +1089,7 @@ enable a server leap smear. + When smearing a leap second, the leap status is suppressed on the server and -the served time is corrected slowly be slewing instead of stepping. The clients +the served time is corrected slowly by slewing instead of stepping. The clients do not need any special configuration as they do not know there is any leap second and they follow the server time which eventually brings them back to UTC. Care must be taken to ensure they use only NTP servers which smear the @@ -868,7 +1103,7 @@ ---- leapsecmode slew maxslewrate 1000 -smoothtime 400 0.001 leaponly +smoothtime 400 0.001024 leaponly ---- + The first directive is necessary to disable the clock step which would reset @@ -876,17 +1111,23 @@ local clock to 1000 ppm, which improves the stability of the smoothing process when the local correction starts and ends. The third directive enables the server time smoothing process. It will start when the clock gets to 00:00:00 -UTC and it will take 17 hours 34 minutes to finish. The frequency offset will -be changing by 0.001 ppm per second and will reach a maximum of 31.623 ppm. The -*leaponly* option makes the duration of the leap smear constant and allows the -clients to safely synchronise with multiple identically configured leap -smearing servers. +UTC and it will take 62500 seconds (about 17.36 hours) to finish. The frequency +offset will be changing by 0.001024 ppm per second and will reach a maximum of +32 ppm in 31250 seconds. The *leaponly* option makes the duration of the leap +smear constant and allows the clients to safely synchronise with multiple +identically configured leap smearing servers. ++ +The duration of the leap smear can be calculated from the specified wander as ++ +---- +duration = sqrt(4 / wander) +---- [[leapsectz]]*leapsectz* _timezone_:: -This directive specifies a timezone in the system tz database which *chronyd* -can use to determine when will the next leap second occur and what is the -current offset between TAI and UTC. It will periodically check if 23:59:59 and -23:59:60 are valid times in the timezone. This typically works with the +This directive specifies a timezone in the system timezone database which +*chronyd* can use to determine when will the next leap second occur and what is +the current offset between TAI and UTC. It will periodically check if 23:59:59 +and 23:59:60 are valid times in the timezone. This normally works with the _right/UTC_ timezone. + When a leap second is announced, the timezone needs to be updated at least 12 @@ -923,16 +1164,17 @@ [[makestep]]*makestep* _threshold_ _limit_:: Normally *chronyd* will cause the system to gradually correct any time offset, by slowing down or speeding up the clock as required. In certain situations, -the system clock might be so far adrift that this slewing process would take a -very long time to correct the system clock. +e.g. when *chronyd* is initially started, the system clock might be so far +adrift that this slewing process would take a very long time to correct the +system clock. + This directive forces *chronyd* to step the system clock if the adjustment is larger than a threshold value, but only if there were no more clock updates -since *chronyd* was started than a specified limit (a negative value can be -used to disable the limit). +since *chronyd* was started than the specified limit. A negative value disables +the limit. + -This is particularly useful when using reference clocks, because the -<> directive works only with NTP sources. +On most systems it is desirable to step the system clock only on boot, before +starting programs that rely on time advancing monotonically forwards. + An example of the use of this directive is: + @@ -994,8 +1236,8 @@ estimate might be so unreliable that it should not be used. By default, the threshold is 1000 ppm. + -Typical values for _skew-in-ppm_ might be 100 for a dial-up connection to -servers over a phone line, and 5 or 10 for a computer on a LAN. +Typical values for _skew-in-ppm_ might be 100 for NTP sources polled over a +wireless network, and 10 or smaller for sources on a local wired network. + It should be noted that this is not the only means of protection against using unreliable estimates. At all times, *chronyd* keeps track of both the estimated @@ -1050,7 +1292,7 @@ The frequency compensation is calculated (in ppm) as + ---- -k0 + (T - T0) * k1 + (T - T0)^2 * k2 +comp = k0 + (T - T0) * k1 + (T - T0)^2 * k2 ---- + The result has to be between -10 ppm and 10 ppm, otherwise the measurement is @@ -1101,38 +1343,34 @@ [[allow]]*allow* [*all*] [_subnet_]:: The *allow* directive is used to designate a particular subnet from which NTP -clients are allowed to access the computer as an NTP server. +clients are allowed to access the computer as an NTP server. It also controls +access of NTS-KE clients when NTS is enabled on the server. + The default is that no clients are allowed access, i.e. *chronyd* operates purely as an NTP client. If the *allow* directive is used, *chronyd* will be both a client of its servers, and a server to other clients. + +This directive can be used multiple times. ++ Examples of the use of the directive are as follows: + ---- allow 1.2.3.4 -allow 1.2 +allow 3.4.5.0/24 allow 3.4.5 -allow 6.7.8/22 -allow 6.7.8.9/22 allow 2001:db8::/32 allow 0/0 allow ::/0 allow ---- + -The first directive allows a node with IPv4 address _1.2.3.4_ to be an NTP -client of this computer. -The second directive allows any node with an IPv4 address of the form _1.2.x.y_ -(with _x_ and _y_ arbitrary) to be an NTP client of this computer. Likewise, -the third directive allows any node with an IPv4 address of the form _3.4.5.x_ -to have client NTP access. The fourth and fifth forms allow access from any -node with an IPv4 address of the form _6.7.8.x_, _6.7.9.x_, _6.7.10.x_ or -_6.7.11.x_ (with _x_ arbitrary), i.e. the value 22 is the number of bits -defining the specified subnet. In the fifth form, the final byte is ignored. -The sixth form is used for IPv6 addresses. The seventh and eighth forms allow -access by any IPv4 and IPv6 node respectively. The ninth forms allows access by -any node (IPv4 or IPv6). +The first directive allows access from an IPv4 address. The second directive +allows access from all computers in an IPv4 subnet specified in the CIDR +notation. The third directive specifies the same subnet using a simpler +notation where the prefix length is determined by the number of dots. The +fourth directive specifies an IPv6 subnet. The fifth and sixth directives allow +access from all IPv4 and IPv6 addresses respectively. The seventh directive +allows access from all addresses (both IPv4 or IPv6). + A second form of the directive, *allow all*, has a greater effect, depending on the ordering of directives in the configuration file. To illustrate the effect, @@ -1140,32 +1378,43 @@ + ---- allow 1.2.3.4 -deny 1.2.3 -allow 1.2 +deny 1.2.3.0/24 +allow 1.2.0.0/16 ---- + and + ---- allow 1.2.3.4 -deny 1.2.3 -allow all 1.2 +deny 1.2.3.0/24 +allow all 1.2.0.0/16 ---- + In the first example, the effect is the same regardless of what order the three -directives are given in. So the _1.2.x.y_ subnet is allowed access, except for -the _1.2.3.x_ subnet, which is denied access, however the host _1.2.3.4_ is -allowed access. -+ -In the second example, the *allow all 1.2* directives overrides the effect of -_any_ previous directive relating to a subnet within the specified subnet. -Within a configuration file this capability is probably rather moot; however, -it is of greater use for reconfiguration at run-time via *chronyc* with the -<> command. -+ -The directive allows a hostname to be specified instead of an IP address, but -the name must be resolvable when *chronyd* is started (i.e. *chronyd* needs -to be started when the network is already up and DNS is working). +directives are given in. So the _1.2.0.0/16_ subnet is allowed access, except +for the _1.2.3.0/24_ subnet, which is denied access, however the host _1.2.3.4_ +is allowed access. ++ +In the second example, the *allow all 1.2.0.0/16* directive overrides the +effect of _any_ previous directive relating to a subnet within the specified +subnet. Within a configuration file this capability is probably rather moot; +however, it is of greater use for reconfiguration at run-time via *chronyc* +with the <> command. ++ +The rules are internally represented as a tree of tables with one level per +four bits of the IPv4 or IPv6 address. The order of the *allow* and *deny* +directives matters if they modify the same records of one table, i.e. if one +subnet is included in the other subnet and their prefix lengths are at the same +level. For example, _1.2.3.0/28_ and _1.2.3.0/29_ are in different tables, but +_1.2.3.0/25_ and _1.2.3.0/28_ are in the same table. The configuration can be +verified for individual addresses with the <> +command in *chronyc*. ++ +A hostname can be specified in the directives instead of the IP address, but +the name must be resolvable when *chronyd* is started, i.e. the network is +already up and DNS is working. If the hostname resolves to multiple addresses, +only the first address (in the order returned by the system resolver) will be +allowed or denied. + Note, if the <> directive is used in the configuration file, each of the computers listed in that directive must allow @@ -1173,18 +1422,19 @@ [[deny]]*deny* [*all*] [_subnet_]:: This is similar to the <> directive, except that it denies NTP -client access to a particular subnet or host, rather than allowing it. +and NTS-KE client access to a particular subnet or host, rather than allowing +it. + -The syntax is identical. +The syntax is identical and the directive can be used multiple times too. + There is also a *deny all* directive with similar behaviour to the *allow all* directive. [[bindaddress]]*bindaddress* _address_:: -The *bindaddress* directive binds the socket on which *chronyd* listens for NTP -requests to a local address of the computer. On systems other than Linux, the -address of the computer needs to be already configured when *chronyd* is -started. +The *bindaddress* directive binds the sockets on which *chronyd* listens for +NTP and NTS-KE requests to a local address of the computer. On systems other +than Linux, the address of the computer needs to be already configured when +*chronyd* is started. + An example of the use of the directive is: + @@ -1196,23 +1446,36 @@ directive can be specified. Therefore, it is not useful on computers which should serve NTP on multiple network interfaces. +[[binddevice]]*binddevice* _interface_:: +The *binddevice* directive binds the NTP and NTS-KE server sockets to a network +device specified by the interface name. This directive can specify only one +interface and it is supported on Linux only. ++ +An example of the directive is: ++ +---- +binddevice eth0 +---- + [[broadcast]]*broadcast* _interval_ _address_ [_port_]:: The *broadcast* directive is used to declare a broadcast address to which chronyd should send packets in the NTP broadcast mode (i.e. make *chronyd* act as a broadcast server). Broadcast clients on that subnet will be able to synchronise. + +This directive can be used multiple times to specify multiple addresses. ++ The syntax is as follows: + ---- -broadcast 30 192.168.1.255 -broadcast 60 192.168.2.255 12123 -broadcast 60 ff02::101 +broadcast 32 192.168.1.255 +broadcast 64 192.168.2.255 12123 +broadcast 64 ff02::101 ---- + In the first example, the destination port defaults to UDP port 123 (the normal NTP port). In the second example, the destination port is specified as 12123. The -first parameter in each case (30 or 60 respectively) is the interval in seconds +first parameter in each case (32 or 64 respectively) is the interval in seconds between broadcast packets being sent. The second parameter in each case is the broadcast address to send the packet to. This should correspond to the broadcast address of one of the network interfaces on the computer where @@ -1236,10 +1499,8 @@ to allocate for logging of client accesses and the state that *chronyd* as an NTP server needs to support the interleaved mode for its clients. The default limit is 524288 bytes, which is sufficient for monitoring about four thousand -clients at the same time. -+ -In older *chrony* versions if the limit was set to 0, the memory allocation was -unlimited. +clients at the same time. The maximum value is 2^32-1 (4 GB) on 32-bit systems +and 2^35 (32 GB) on 64-bit systems. + An example of the use of this directive is: + @@ -1301,16 +1562,18 @@ that poll more than one server. Each server needs to be configured to poll all other servers with the *local* directive. This ensures only the server with the smallest reference ID has the local reference active and others are -synchronised to it. When that server fails, another will take over. +synchronised to it. If that server stops responding, the server with the second +smallest reference ID will take over when its local reference mode activates +(root distance reaches the threshold configured by the *distance* option). + The *orphan* mode is compatible with the *ntpd*'s orphan mode (enabled by the *tos orphan* command). -:: +{blank}:: + An example of the directive is: + ---- -local stratum 10 orphan +local stratum 10 orphan distance 0.1 ---- [[ntpsigndsocket]]*ntpsigndsocket* _directory_:: @@ -1330,6 +1593,97 @@ ntpsigndsocket /var/lib/samba/ntp_signd ---- +[[ntsport]]*ntsport* _port_:: +This directive specifies the TCP port on which *chronyd* will provide the NTS +Key Establishment (NTS-KE) service. The default port is 4460. ++ +The port will be open only when a certificate and key is specified by the +*ntsservercert* and *ntsserverkey* directives. + +[[ntsservercert]]*ntsservercert* _file_:: +This directive specifies a file containing a certificate in the PEM format +for *chronyd* to operate as an NTS server. The file should also include +any intermediate certificates that the clients will need to validate the +server's certificate. ++ +This directive can be used multiple times to specify multiple certificates for +different names of the server. ++ +The files are loaded only once. *chronyd* needs to be restarted in order to +load a renewed certificate. The <> and +<> directives with the *-r* option of *chronyd* are +recommended for a near-seamless server operation. + +[[ntsserverkey]]*ntsserverkey* _file_:: +This directive specifies a file containing a private key in the PEM format +for *chronyd* to operate as an NTS server. ++ +This directive can be used multiple times to specify multiple keys. The number +of keys must be the same as the number of certificates and the corresponding +files must be specified in the same order. + +[[ntsprocesses]]*ntsprocesses* _processes_:: +This directive specifies how many helper processes will *chronyd* operating +as an NTS server start for handling client NTS-KE requests in order to improve +performance with multi-core CPUs and multithreading. If set to 0, no helper +process will be started and all NTS-KE requests will be handled by the main +*chronyd* process. The default value is 1. + +[[maxntsconnections]]*maxntsconnections* _connections_:: +This directive specifies the maximum number of concurrent NTS-KE connections +per process that the NTS server will accept. The default value is 100. The +maximum practical value is half of the system *FD_SETSIZE* constant (usually +1024). + +[[ntsdumpdir2]]*ntsdumpdir* _directory_:: +This directive specifies a directory where *chronyd* operating as an NTS server +can save the keys which encrypt NTS cookies provided to clients. The keys are +saved to a single file named _ntskeys_. When *chronyd* is restarted, reloading +the keys allows the clients to continue using old cookies and avoids a storm of +NTS-KE requests. By default, the server does not save the keys. ++ +An example of the directive is: ++ +---- +ntsdumpdir @CHRONYVARDIR@ +---- ++ +This directory is used also by the <> to save NTS cookies. + +[[ntsntpserver]]*ntsntpserver* _hostname_:: +This directive specifies the hostname (as a fully qualified domain name) or +address of the NTP server(s) which is +provided in the NTS-KE response to the clients. It allows the NTS-KE server to +be separated from the NTP server. However, the servers need to share the keys, +i.e. external key management needs to be enabled by setting +<> to 0. By default, no hostname or address is provided +to the clients, which means they should use the same server for NTS-KE and NTP. + +[[ntsrotate]]*ntsrotate* _interval_:: +This directive specifies the rotation interval (in seconds) of the server key +which encrypts the NTS cookies. New keys are generated automatically from the +_/dev/urandom_ device. The server keeps two previous keys to give the clients +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 maximum value is 2^31-1 (68 years). ++ +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 +save the keys to the _ntskeys_ file and will reload the keys from the file when +the <> command is issued in *chronyc*. The file can +be periodically copied from another server running *chronyd* (which does +not have *ntsrotate* set to 0) in order to have one or more servers dedicated +to NTS-KE. The NTS-KE servers need to be configured with the +<> directive to point the clients to the right NTP +server. ++ +An example of the directive is: ++ +---- +ntsrotate 2592000 +---- + [[port]]*port* _port_:: This option allows you to configure the port on which *chronyd* will listen for NTP requests. The port will be open only when an address is allowed by the @@ -1377,7 +1731,7 @@ rate is defined as a power of 1/2 and it is 2 by default, i.e. on average at least every fourth request has a response. The minimum value is 1 and the maximum value is 4. -:: +{blank}:: + An example use of the directive is: + @@ -1389,6 +1743,17 @@ more than once per 2 seconds, or sending packets in bursts of more than 16 packets, by up to 75% (with default *leak* of 2). +[[ntsratelimit]]*ntsratelimit* [_option_]...:: +This directive enables rate limiting of NTS-KE requests. It is similar to the +<> directive, except the default interval is 6 +(1 connection per 64 seconds). ++ +An example of the use of the directive is: ++ +---- +ntsratelimit interval 3 burst 1 +---- + [[smoothtime]]*smoothtime* _max-freq_ _max-wander_ [*leaponly*]:: The *smoothtime* directive can be used to enable smoothing of the time that *chronyd* serves to its clients to make it easier for them to track it and keep @@ -1442,8 +1807,8 @@ === Command and monitoring access [[bindcmdaddress]]*bindcmdaddress* _address_:: -The *bindcmdaddress* directive allows you to specify an IP address of an -interface on which *chronyd* will listen for monitoring command packets (issued +The *bindcmdaddress* directive specifies a local IP address to which *chronyd* +will bind the UDP socket listening for monitoring command packets (issued by *chronyc*). On systems other than Linux, the address of the interface needs to be already configured when *chronyd* is started. + @@ -1454,9 +1819,10 @@ path of the socket is _@CHRONYRUNDIR@/chronyd.sock_. The socket can be disabled by setting the path to _/_. + -By default, *chronyd* binds to the loopback interface (with addresses -_127.0.0.1_ and _::1_). This blocks all access except from localhost. To listen -for command packets on all interfaces, you can add the lines: +By default, *chronyd* binds the UDP sockets to the addresses _127.0.0.1_ and +_::1_ (i.e. the loopback interface). This blocks all access except from +localhost. To listen for command packets on all interfaces, you can add the +lines: + ---- bindcmdaddress 0.0.0.0 @@ -1474,6 +1840,17 @@ bindcmdaddress /var/run/chrony/chronyd.sock ---- +[[bindcmddevice]]*bindcmddevice* _interface_:: +The *bindcmddevice* directive binds the UDP command sockets to a network device +specified by the interface name. This directive can specify only one interface +and it is supported on Linux only. ++ +An example of the directive is: ++ +---- +bindcmddevice eth0 +---- + [[cmdallow]]*cmdallow* [*all*] [_subnet_]:: This is similar to the <> directive, except that it allows monitoring access (rather than NTP client access) to a particular subnet or @@ -1546,7 +1923,8 @@ automatically. When the system clock is synchronised and the estimated error between the two clocks is larger than the specified threshold, *chronyd* will trim the RTC as if the <> command in *chronyc* -was issued. +was issued. The trimming operation is accurate to only about 1 second, which is +the minimum effective threshold. + This directive is effective only with the <> directive. + @@ -1852,7 +2230,7 @@ . Applied compensation in ppm, positive means the system clock is running faster than it would be without the compensation. [3.6600e-01] + -:: +{blank}:: An example of the directive is: + ---- @@ -1885,8 +2263,9 @@ 0.1 seconds starts to be compensated. [[logdir]]*logdir* _directory_:: -This directive allows the directory where log files are written to be -specified. +This directive specifies the directory for writing log files enabled by the +*log* directive. If the directory does not exist, it will be created +automatically. + An example of the use of this directive is: + @@ -1914,6 +2293,57 @@ === Miscellaneous +[[confdir]]*confdir* _directory_...:: +The *confdir* directive includes configuration files with the _.conf_ suffix +from a directory. The files are included in the lexicographical order of the +file names. ++ +Multiple directories (up to 10) can be specified with a single *confdir* +directive. In this case, if multiple directories contain a file with the same +name, only the first file in the order of the specified directories will be +included. This enables a fragmented configuration where existing fragments can +be replaced by adding files to a different directory. ++ +This directive can be used multiple times. ++ +An example of the directive is: ++ +---- +confdir @SYSCONFDIR@/chrony.d +---- + +[[sourcedir]]*sourcedir* _directory_...:: +The *sourcedir* directive is identical to the *confdir* directive, except the +configuration files have the _.sources_ suffix, they can only specify NTP +sources (i.e. the *server*, *pool*, and *peer* directives), they are expected +to have all lines terminated by the newline character, and they can be +reloaded by the <> command in +*chronyc*. It is particularly useful with dynamic sources like NTP servers +received from a DHCP server, which can be written to a file specific to the +network interface by a networking script. ++ +This directive can be used multiple times. ++ +An example of the directive is: ++ +---- +sourcedir /var/run/chrony-dhcp +---- + +[[include]]*include* _pattern_:: +The *include* directive includes a configuration file, or multiple configuration +files if a wildcard pattern is specified. Unlike with the *confdir* directive, +the full name of the files needs to be specified and at least one file is +required to exist. ++ +This directive can be used multiple times. ++ +An example of the directive is: ++ +---- +include @SYSCONFDIR@/chrony.d/*.conf +---- + [[hwtimestamp]]*hwtimestamp* _interface_ [_option_]...:: This directive enables hardware timestamping of NTP packets sent to and received from the specified network interface. The network interface controller @@ -1943,8 +2373,9 @@ measurements*>> directive, and the <> report in *chronyc*. + -If the specified interface is _*_, *chronyd* will try to enable HW timestamping -on all available interfaces. +This directive can be used multiple times to enable HW timestamping on multiple +interfaces. If the specified interface is _*_, *chronyd* will try to enable HW +timestamping on all available interfaces. + The *hwtimestamp* directive has the following options: + @@ -1984,14 +2415,14 @@ Enables timestamping of received NTP packets. _none_:::: Disables timestamping of received packets. -::: +{blank}::: The most specific filter for timestamping NTP packets which is supported by the NIC is selected by default. Some NICs can timestamp only PTP packets, which limits the selection to the _none_ filter. Forcing timestamping of all packets with the _all_ filter when the NIC supports both _all_ and _ntp_ filters can be useful when packets are received from or on a non-standard UDP port (e.g. specified by the *port* directive). -:: +{blank}:: + Examples of the directive are: + @@ -2001,20 +2432,11 @@ hwtimestamp * ---- -[[include]]*include* _pattern_:: -The *include* directive includes a configuration file or multiple configuration -files if a wildcard pattern is specified. This can be useful when maintaining -configuration on multiple hosts to keep the differences in separate files. -+ -An example of the directive is: -+ ----- -include @SYSCONFDIR@/chrony.d/*.conf ----- - [[keyfile]]*keyfile* _file_:: -This directive is used to specify the location of the file containing ID-key -pairs for authentication of NTP packets. +This directive is used to specify the location of the file containing symmetric +keys which are shared between NTP servers and clients, or peers, in order to +authenticate NTP packets with a message authentication code (MAC) using a +cryptographic hash function or cipher. + The format of the directive is shown in the example below: + @@ -2029,31 +2451,41 @@ 10 tulip 11 hyacinth 20 MD5 ASCII:crocus -25 SHA1 HEX:1dc764e0791b11fa67efc7ecbc4b0d73f68a070c +25 SHA1 HEX:933F62BE1D604E68A81B557F18CFA200483F5B70 +30 AES128 HEX:7EA62AE64D190114D46D5A082F948EC1 +31 AES256 HEX:37DDCBC67BB902BCB8E995977FAB4D2B5642F5B32EBCEEE421921D97E5CBFE39 ... ---- + -Each line consists of an ID, name of an authentication hash function (optional), -and a password. The ID can be any unsigned integer in the range 1 through -2^32-1. The default hash function is *MD5*, which is always supported. +Each line consists of an ID, optional type, and key. + +The ID can be any positive integer in the range 1 through 2^32-1. ++ +The type is a name of a cryptographic hash function or cipher which is used to +generate and verify the MAC. The default type is *MD5*, which is always +supported. If *chronyd* was built with enabled support for hashing using a crypto library (nettle, nss, or libtomcrypt), the following functions are available: *MD5*, *SHA1*, *SHA256*, *SHA384*, *SHA512*. Depending on which library and version is -*chronyd* using, some or all of the following functions may also be available: -*SHA3-224*, *SHA3-256*, *SHA3-384*, *SHA3-512*, *RMD128*, *RMD160*, *RMD256*, -*RMD320*, *TIGER*, *WHIRLPOOL*. +*chronyd* using, some of the following hash functions and ciphers may +also be available: +*SHA3-224*, *SHA3-256*, *SHA3-384*, *SHA3-512*, *TIGER*, *WHIRLPOOL*, *AES128*, +*AES256*. + -The password can be specified as a string of characters not containing white +The key can be specified as a string of ASCII characters not containing white space with an optional *ASCII:* prefix, or as a hexadecimal number with the *HEX:* prefix. The maximum length of the line is 2047 characters. +If the type is a cipher, the length of the key must match the cipher (i.e. 128 +bits for AES128 and 256 bits for AES256). + -The password is used with the hash function to generate and verify a message -authentication code (MAC) in NTP packets. It is recommended to use SHA1, or -stronger, hash function with random passwords specified in the hexadecimal -format that have at least 128 bits. *chronyd* will log a warning to -syslog on start if a source is specified in the configuration file with a key -that has password shorter than 80 bits. +It is recommended to use randomly generated keys, specified in the hexadecimal +format, which are at least 128 bits long (i.e. they have at least 32 characters +after the *HEX:* prefix). *chronyd* will log a warning to syslog on start if a +source is specified in the configuration file with a key shorter than 80 bits. ++ +The recommended key types are AES ciphers and SHA3 hash functions. MD5 should +be avoided unless no other type is supported on the server and client, or +peers. + The <> command of *chronyc* can be used to generate random keys for the key file. By default, it generates 160-bit MD5 or @@ -2136,7 +2568,7 @@ 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 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_ and _baz.example.net_, your _chrony.conf_ file could contain as a minimum: @@ -2175,6 +2607,20 @@ rtcsync ---- +If the servers (or pool) support the Network Time Security (NTS) +authentication mechanism and *chronyd* is compiled with NTS support, the *nts* +option will enable a secure synchronisation to the servers. The configuration +file could look like: + +---- +server foo.example.net iburst nts +server bar.example.net iburst nts +server baz.example.net iburst nts +driftfile @CHRONYVARDIR@/drift +makestep 1.0 3 +rtcsync +---- + === NTP client with infrequent connection to NTP servers This section shows how to configure *chronyd* for computers that have @@ -2222,25 +2668,25 @@ === Isolated networks This section shows how to configure *chronyd* for computers that never have -network conectivity to any computer which ultimately derives its time from a +network connectivity to any computer which ultimately derives its time from a reference clock. -In this situation, one computer is selected to be the master timeserver. The -other computers are either direct clients of the master, or clients of clients. +In this situation, one computer is selected to be the primary timeserver. The +other computers are either direct clients of the server, or clients of clients. The <> directive enables a local reference mode, which allows *chronyd* to appear synchronised even when it is not. -The rate value in the master's drift file needs to be set to the average rate -at which the master gains or loses time. *chronyd* includes support for this, +The rate value in the server's drift file needs to be set to the average rate +at which the server gains or loses time. *chronyd* includes support for this, in the form of the <> directive and the <> command in the *chronyc* program. -If the master is rebooted, *chronyd* can re-read the drift rate from the drift -file. However, the master has no accurate estimate of the current time. To get -around this, the system can be configured so that the master can initially set +If the server is rebooted, *chronyd* can re-read the drift rate from the drift +file. However, the server has no accurate estimate of the current time. To get +around this, the system can be configured so that the server can initially set itself to a '`majority-vote`' of selected clients' times; this allows the -clients to '`flywheel`' the master while it is rebooting. +clients to '`flywheel`' the server while it is rebooting. The <> directive is useful when the clocks of the clients need to stay close together when the local time is adjusted by the @@ -2249,8 +2695,8 @@ the local time is ready to be served. After that point, any adjustments will be smoothed out. -A typical configuration file for the master (called _master_) might be -(assuming the clients and the master are in the _192.168.165.x_ subnet): +A typical configuration file for the server (called _ntp.local_) might be +(assuming the clients and the server are in the _192.168.165.x_ subnet): ---- initstepslew 1 client1 client3 client6 @@ -2262,11 +2708,11 @@ rtcsync ---- -For the clients that have to resynchronise the master when it restarts, +For the clients that have to resynchronise the server when it restarts, the configuration file might be: ---- -server master iburst +server ntp.local iburst driftfile @CHRONYVARDIR@/drift allow 192.168.165.0/24 makestep 1.0 3 @@ -2276,22 +2722,22 @@ The rest of the clients would be the same, except that the *allow* directive is not required. -If there is no suitable computer to be designated as the master, or there is a -requirement to keep the clients synchronised even when it fails, the *orphan* -option of the *local* directive enables a special mode where the master is -selected from multiple computers automatically. They all need to use the same -*local* configuration and poll one another. The server with the smallest -reference ID (which is based on its IP address) will take the role of the -master and others will be synchronised to it. When it fails, the server with -the second smallest reference ID will take over and so on. +If there is no suitable computer to be designated as the primary server, or +there is a requirement to keep the clients synchronised even when it fails, the +*orphan* option of the *local* directive enables a special mode where the +server is selected from multiple computers automatically. They all need to use +the same *local* configuration and poll one another. The server with the +smallest reference ID (which is based on its IP address) will take the role of +the primary server and others will be synchronised to it. When it fails, the +server with the second smallest reference ID will take over and so on. A configuration file for the first server might be (assuming there are three -servers called _master1_, _master2_, and _master3_): +servers called _ntp1.local_, _ntp2.local_, and _ntp3.local_): ---- -initstepslew 1 master2 master3 -server master2 -server master3 +initstepslew 1 ntp2.local ntp3.local +server ntp2.local +server ntp3.local driftfile @CHRONYVARDIR@/drift local stratum 8 orphan manual @@ -2414,7 +2860,7 @@ === Public NTP server *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 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 diff -Nru chrony-3.5/doc/chrony.conf.man.in chrony-4.1/doc/chrony.conf.man.in --- chrony-3.5/doc/chrony.conf.man.in 2019-05-14 11:00:03.000000000 +0000 +++ chrony-4.1/doc/chrony.conf.man.in 2021-05-13 10:48:12.000000000 +0000 @@ -1,23 +1,32 @@ '\" t .\" Title: chrony.conf -.\" Author: [see the "AUTHORS" section] -.\" Generator: Asciidoctor 1.5.6.1 -.\" Date: 2019-05-10 +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.10 +.\" Date: 2021-05-12 .\" Manual: Configuration Files .\" Source: chrony @CHRONY_VERSION@ .\" Language: English .\" -.TH "CHRONY.CONF" "5" "2019-05-10" "chrony @CHRONY_VERSION@" "Configuration Files" +.TH "CHRONY.CONF" "5" "2021-05-12" "chrony @CHRONY_VERSION@" "Configuration Files" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 .nh .ad l .de URL -\\$2 \(laURL: \\$1 \(ra\\$3 +\fI\\$2\fP <\\$1>\\$3 .. -.if \n[.g] .mso www.tmac -.LINKSTYLE blue R < > +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} .SH "NAME" chrony.conf \- chronyd configuration file .SH "SYNOPSIS" @@ -26,12 +35,14 @@ .SH "DESCRIPTION" .sp This file configures the \fBchronyd\fP daemon. The compiled\-in location is -\fI@SYSCONFDIR@/chrony.conf\fP, but other locations can be specified on the +\fI@SYSCONFDIR@/chrony.conf\fP. Other locations can be specified on the \fBchronyd\fP command line with the \fB\-f\fP option. .sp Each directive in the configuration file is placed on a separate line. The -following sections describe each of the directives in turn. The directives can -occur in any order in the file and they are not case\-sensitive. +following sections describe each of the directives in turn. The directives are +not case\-sensitive. Generally, the directives can occur in any order in the file +and if a directive is specified multiple times, only the last one will be +effective. Exceptions are noted in the descriptions. .sp The configuration directives can also be specified directly on the \fBchronyd\fP command line. In this case each argument is parsed as a new line and the @@ -54,9 +65,10 @@ synchronise its system time to that of the server, but the server\(cqs system time will never be influenced by that of a client. .sp -The \fBserver\fP directive is immediately followed by either the name of the -server, or its IP address. The \fBserver\fP directive supports the following -options: +This directive can be used multiple times to specify multiple servers. +.sp +The directive is immediately followed by either the name of the +server, or its IP address. It supports the following options: .sp \fBminpoll\fP \fIpoll\fP .RS 4 @@ -82,16 +94,16 @@ .sp \fBiburst\fP .RS 4 -With this option, the interval between the first four requests sent to the -server will be 2 seconds or less instead of the interval specified by the -\fBminpoll\fP option, which allows \fBchronyd\fP to make the first update of the clock -shortly after start. +With this option, \fBchronyd\fP will start with a burst of 4\-8 requests in order to +make the first update of the clock sooner. It will also repeat the burst every +time the source is switched from the offline state to online with the +\fBonline\fP command in \fBchronyc\fP. .RE .sp \fBburst\fP .RS 4 -With this option, \fBchronyd\fP will shorten the interval between up to four -requests to 2 seconds or less when it cannot get a good measurement from the +With this option, \fBchronyd\fP will send a burst of up to 4 requests when it +cannot get a good measurement from the server. The number of requests in the burst is limited by the current polling interval to keep the average interval at or above the minimum interval, i.e. the current interval needs to be at least two times longer than the minimum @@ -102,7 +114,7 @@ .RS 4 The NTP protocol supports a message authentication code (MAC) to prevent computers having their system time upset by rogue packets being sent to them. -The MAC is generated as a function of a password specified in the key file, +The MAC is generated as a function of a key specified in the key file, which is specified by the \fBkeyfile\fP directive. .sp The \fBkey\fP option specifies which key (with an ID in the range 1 through 2^32\-1) @@ -115,6 +127,24 @@ be set to 4 for compatibility. .RE .sp +\fBnts\fP +.RS 4 +This option enables authentication using the Network Time Security (NTS) +mechanism. Unlike with the \fBkey\fP option, the server and client do not need to +share a key in a key file. NTS has a Key Establishment (NTS\-KE) protocol using +the Transport Layer Security (TLS) protocol to get the keys and cookies +required by NTS for authentication of NTP packets. +.RE +.sp +\fBcertset\fP \fIID\fP +.RS 4 +This option specifies which set of trusted certificates should be used to verify +the server\(cqs certificate when the \fBnts\fP option is enabled. Sets of certificates +can be specified with the \fBntstrustedcerts\fP directive. The +default set is 0, which by default contains certificates of the system\(cqs +default trusted certificate authorities. +.RE +.sp \fBmaxdelay\fP \fIdelay\fP .RS 4 \fBchronyd\fP uses the network round\-trip delay to the server to determine how @@ -125,9 +155,9 @@ For small variations in the round\-trip delay, \fBchronyd\fP uses a weighting scheme when processing the measurements. However, beyond a certain level of delay the measurements are likely to be so corrupted as to be useless. (This is -particularly so on dial\-up or other slow links, where a long delay probably -indicates a highly asymmetric delay caused by the response waiting behind a lot -of packets related to a download of some sort). +particularly so on wireless networks and other slow links, where a long delay +probably indicates a highly asymmetric delay caused by the response waiting +behind a lot of packets related to a download of some sort). .sp If the user knows that round trip delays above a certain level should cause the measurement to be ignored, this level can be defined with the \fBmaxdelay\fP @@ -141,7 +171,7 @@ This option is similar to the \fBmaxdelay\fP option above. \fBchronyd\fP keeps a record of the minimum round\-trip delay amongst the previous measurements that it has buffered. If a measurement has a round trip delay that is greater than the -maxdelayratio times the minimum delay, it will be rejected. +specified ratio times the minimum delay, it will be rejected. .RE .sp \fBmaxdelaydevratio\fP \fIratio\fP @@ -248,14 +278,14 @@ .sp \fBxleave\fP .RS 4 -This option enables an interleaved mode which allows the server or the peer to -send transmit timestamps captured after the actual transmission (e.g. when the -server or the peer is running \fBchronyd\fP with software (kernel) or hardware -timestamping). This can significantly improve the accuracy of the measurements. +This option enables the interleaved mode of NTP. It enables the server to +respond with more accurate transmit timestamps (e.g. kernel or hardware +timestamps), which cannot be contained in the transmitted packet itself and +need to refer to a previous packet instead. This can significantly improve the +accuracy and stability of the measurements. .sp The interleaved mode is compatible with servers that support only the basic -mode, but peers must both support and have enabled the interleaved mode, -otherwise the synchronisation will work only in one direction. Note that even +mode. Note that even servers that support the interleaved mode might respond in the basic mode as the interleaved mode requires the servers to keep some state for each client and the state might be dropped when there are too many clients (e.g. @@ -283,6 +313,12 @@ default is 123, the standard NTP port). .RE .sp +\fBntsport\fP \fIport\fP +.RS 4 +This option specifies the TCP port on which the server is listening for NTS\-KE +connections when the \fBnts\fP option is enabled. The default is 4460. +.RE +.sp \fBpresend\fP \fIpoll\fP .RS 4 If the timing measurements being made by \fBchronyd\fP are the only network data @@ -298,22 +334,18 @@ prior to the actual measurement. For example, with the following option included in a \fBserver\fP directive: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf presend 9 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp when the polling interval is 512 seconds or more, an extra NTP client packet will be sent to the server a short time (2 seconds) before making the actual measurement. .sp -The \fBpresend\fP option cannot be used in the \fBpeer\fP directive. If it is used -with the \fBxleave\fP option, \fBchronyd\fP will send two extra packets instead of one. +If the \fBpresend\fP option is used together with the \fBxleave\fP option, \fBchronyd\fP +will send two extra packets instead of one. .RE .sp \fBminstratum\fP \fIstratum\fP @@ -321,7 +353,7 @@ When the synchronisation source is selected from available sources, sources with lower stratum are normally slightly preferred. This option can be used to increase stratum of the source to the specified minimum, so \fBchronyd\fP will -avoid selecting that source. This is useful with low stratum sources that are +avoid selecting that source. This is useful with low\-stratum sources that are known to be unreliable or inaccurate and which should be used only when other sources are unreachable. .RE @@ -336,6 +368,18 @@ default version is 3 for compatibility with older \fBchronyd\fP servers. Otherwise, the default version is 4. .RE +.sp +\fBcopy\fP +.RS 4 +This option specifies that the server and client are closely related, their +configuration does not allow a synchronisation loop to form between them, and +the client is allowed to assume the reference ID and stratum of the server. +This is useful when multiple instances of \f(CRchronyd\fP are running on one computer +(e.g. for security or performance reasons), one primarily operating as a client +to synchronise the system clock and other instances started with the \fB\-x\fP +option to operate as NTP servers for other computers with their NTP clocks +synchronised to the first instance. +.RE .RE .sp \fBpool\fP \fIname\fP [\fIoption\fP]... @@ -345,30 +389,34 @@ a single NTP server. The pool name is expected to resolve to multiple addresses which might change over time. .sp +This directive can be used multiple times to specify multiple pools. +.sp All options valid in the \fBserver\fP directive can be used in this directive too. There is one option specific to the \fBpool\fP directive: -\fBmaxsources\fP sets the maximum number of sources that can be used from the pool, -the default value is 4. .sp -On start, when the pool name is resolved, \fBchronyd\fP will add up to 16 sources, -one for each resolved address. When the number of sources from which at least -one valid reply was received reaches the number specified by the \fBmaxsources\fP -option, the other sources will be removed. When a pool source is unreachable, +\fBmaxsources\fP \fIsources\fP +.RS 4 +This option sets the desired number of sources to be used from the pool. +\fBchronyd\fP will repeatedly try to resolve the name until it gets this number of +sources responding to requests. The default value is 4 and the maximum value is +16. +.RE +.RE +.sp + +.RS 4 +When an NTP source is unreachable, marked as a falseticker, or has a distance larger than the limit set by the \fBmaxdistance\fP directive, \fBchronyd\fP will try to replace the -source with a newly resolved address from the pool. +source with a newly resolved address of the name. .sp An example of the \fBpool\fP directive is .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf pool pool.ntp.org iburst maxsources 3 .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBpeer\fP \fIhostname\fP [\fIoption\fP]... @@ -381,6 +429,14 @@ ephemeral symmetric associations and does not need to be configured with an address of this host. \fBchronyd\fP does not support ephemeral associations. .sp +This directive can be used multiple times to specify multiple peers. +.sp +The following options of the \fBserver\fP directive do not work in the \fBpeer\fP +directive: \fBiburst\fP, \fBburst\fP, \fBnts\fP, \fBpresend\fP, \fBcopy\fP. +.sp +When using the \fBxleave\fP option, both peers must support and have enabled the +interleaved mode, otherwise the synchronisation will work in one direction +only. When a key is specified by the \fBkey\fP option to enable authentication, both peers must use the same key and the same key number. .sp @@ -405,19 +461,8 @@ .sp \fBinitstepslew\fP \fIstep\-threshold\fP [\fIhostname\fP]... .RS 4 -In normal operation, \fBchronyd\fP slews the time when it needs to adjust the -system clock. For example, to correct a system clock which is 1 second slow, -\fBchronyd\fP slightly increases the amount by which the system clock is advanced -on each clock interrupt, until the error is removed. Note that at no time does -time run backwards with this method. -.sp -On most Unix systems it is not desirable to step the system clock, because many -programs rely on time advancing monotonically forwards. -.sp -When the \fBchronyd\fP daemon is initially started, it is possible that the system -clock is considerably in error. Attempting to correct such an error by slewing -might not be sensible, since it might take several hours to correct the error by -this means. +(This directive is deprecated in favour of the \fBmakestep\fP +directive.) .sp The purpose of the \fBinitstepslew\fP directive is to allow \fBchronyd\fP to make a rapid measurement of the system clock error at boot time, and to correct the @@ -437,36 +482,33 @@ .sp An example of the use of the directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf -initstepslew 30 foo.example.net bar.example.net +initstepslew 30 foo.example.net bar.example.net baz.example.net .fi -.if n \{\ -.RE -.\} +.if n .RE .sp -where 2 NTP servers are used to make the measurement. The \fI30\fP indicates that +where 3 NTP servers are used to make the measurement. The \fI30\fP indicates that if the system\(cqs error is found to be 30 seconds or less, a slew will be used to correct it; if the error is above 30 seconds, a step will be used. .sp The \fBinitstepslew\fP directive can also be used in an isolated LAN environment, where the clocks are set manually. The most stable computer is chosen as the -master, and the other computers are slaved to it. If each of the slaves is -configured with the \fBlocal\fP directive, the master can be set up with -an \fBinitstepslew\fP directive which references some or all of the slaves. Then, -if the master machine has to be rebooted, the slaves can be relied on to act -analogously to a flywheel and preserve the time for a short period while the -master completes its reboot. +primary server and the other computers are its clients. If each of the clients +is configured with the \fBlocal\fP directive, the server can be set up +with an \fBinitstepslew\fP directive which references some or all of the clients. +Then, if the server machine has to be rebooted, the clients can be relied on to +act analogously to a flywheel and preserve the time for a short period while +the server completes its reboot. .sp The \fBinitstepslew\fP directive is functionally similar to a combination of the \fBmakestep\fP and \fBserver\fP directives with the \fBiburst\fP option. The main difference is that the \fBinitstepslew\fP servers are used only before normal operation begins and that the foreground \fBchronyd\fP process waits -for \fBinitstepslew\fP to finish before exiting. This is useful to prevent programs -started in the boot sequence after \fBchronyd\fP from reading the clock before it -has been stepped. +for \fBinitstepslew\fP to finish before exiting. This prevent programs started in +the boot sequence after \fBchronyd\fP from reading the clock before it has been +stepped. With the \fBmakestep\fP directive, the +\fBwaitsync\fP command of \fBchronyc\fP can be used instead. .RE .sp \fBrefclock\fP \fIdriver\fP \fIparameter\fP[:\fIoption\fP]... [\fIoption\fP]... @@ -477,6 +519,8 @@ refclock options. Some drivers have special options, which can be appended to the driver\-specific parameter using the \fB:\fP character. .sp +This directive can be used multiple times to specify multiple reference clocks. +.sp There are four drivers included in \fBchronyd\fP: .sp \fBPPS\fP @@ -500,17 +544,13 @@ .RS 4 Examples: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf refclock PPS /dev/pps0 lock NMEA refid GPS refclock SHM 0 offset 0.5 delay 0.2 refid NMEA noselect refclock PPS /dev/pps1:clear refid GPS2 .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBSHM\fP @@ -533,16 +573,12 @@ .sp Examples: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf refclock SHM 0 poll 3 refid GPS1 refclock SHM 1:perm=0644 refid GPS2 .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBSOCK\fP @@ -559,15 +595,11 @@ where \fBgpsd\fP expects the socket to be created is described in the \fBgpsd(8)\fP man page. For example: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf refclock SOCK /var/run/chrony.ttyS0.sock .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBPHC\fP @@ -620,17 +652,13 @@ .sp Examples: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf refclock PHC /dev/ptp0 poll 0 dpoll \-2 offset \-37 refclock PHC /dev/ptp1:nocrossts poll 3 pps refclock PHC /dev/ptp2:extpps:pin=1 width 0.2 poll 2 .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .RE .sp @@ -815,12 +843,13 @@ .sp \fBacquisitionport\fP \fIport\fP .RS 4 -By default, \fBchronyd\fP uses a separate client socket for each configured server -and their source port is chosen arbitrarily by the operating system. However, -you can use the \fBacquisitionport\fP directive to explicitly specify a port and -use only one socket (per IPv4 or IPv6 address family) for all configured servers. -This can be useful for getting through some firewalls. If set to 0, the source -port of the socket will be chosen arbitrarily. +By default, \fBchronyd\fP as an NTP client opens a new socket for each request with +the source port chosen randomly by the operating system. The \fBacquisitionport\fP +directive can be used to specify the source port and use only one socket (per +IPv4 or IPv6 address family) for all configured servers. This can be useful for +getting through some firewalls. It should not be used if not necessary as there +is a small impact on security of the client. If set to 0, the source port of +the permanent socket will be chosen randomly by the operating system. .sp It can be set to the same port as is used by the NTP server (which can be configured with the \fBport\fP directive) to use only one socket for all @@ -828,15 +857,11 @@ .sp An example of the \fBacquisitionport\fP directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf acquisitionport 1123 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp This would change the source port used for client requests to UDP port 1123. You could then persuade the firewall administrator to open that port. @@ -844,15 +869,49 @@ .sp \fBbindacqaddress\fP \fIaddress\fP .RS 4 -The \fBbindacqaddress\fP directive sets the network interface to which -\fBchronyd\fP will bind its NTP client sockets. The syntax is similar to the -\fBbindaddress\fP and \fBbindcmdaddress\fP +The \fBbindacqaddress\fP directive specifies a local IP address to which +\fBchronyd\fP will bind its NTP and NTS\-KE client sockets. The syntax is similar to +the \fBbindaddress\fP and \fBbindcmdaddress\fP directives. .sp For each of the IPv4 and IPv6 protocols, only one \fBbindacqaddress\fP directive can be specified. .RE .sp +\fBbindacqdevice\fP \fIinterface\fP +.RS 4 +The \fBbindacqdevice\fP directive binds the client sockets to a network device +specified by the interface name. This can be useful when the local address is +dynamic, or to enable an NTP source specified with a link\-local IPv6 address. +This directive can specify only one interface and it is supported on Linux +only. +.sp +An example of the directive is: +.sp +.if n .RS 4 +.nf +bindacqdevice eth0 +.fi +.if n .RE +.RE +.sp +\fBdscp\fP \fIpoint\fP +.RS 4 +The \fBdscp\fP directive sets the Differentiated Services Code Point (DSCP) in +transmitted NTP packets to the specified value. It can improve stability of NTP +measurements in local networks where switches or routers are configured to +prioritise forwarding of packets with specific DSCP values. The default value +is 0 and the maximum value is 63. +.sp +An example of the directive (setting the Expedited Forwarding class) is: +.sp +.if n .RS 4 +.nf +dscp 46 +.fi +.if n .RE +.RE +.sp \fBdumpdir\fP \fIdirectory\fP .RS 4 To compute the rate of gain or loss of time, \fBchronyd\fP has to store a @@ -871,17 +930,19 @@ directory where the measurement histories are saved when \fBchronyd\fP exits, or the \fBdump\fP command in \fBchronyc\fP is issued. .sp +If the directory does not exist, it will be created automatically. +.sp +The \fB\-r\fP option of \fBchronyd\fP enables loading of the dump files on start. All +dump files found in the directory will be removed after start, even if the \fB\-r\fP +option is not present. +.sp An example of the directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf dumpdir @CHRONYRUNDIR@ .fi -.if n \{\ -.RE -.\} +.if n .RE .sp A source whose IP address is \fI1.2.3.4\fP would have its measurement history saved in the file \fI@CHRONYRUNDIR@/1.2.3.4.dat\fP. History of reference clocks is saved @@ -895,6 +956,10 @@ individual sources in the \fBserver\fP and \fBrefclock\fP directives. The default value is 0, which disables the configurable limit. The useful range is 4 to 64. +.sp +As a special case, setting \fBmaxsamples\fP to 1 disables frequency tracking in +order to make the sources immediately selectable with only one sample. This can +be useful when \fBchronyd\fP is started with the \fB\-q\fP or \fB\-Q\fP option. .RE .sp \fBminsamples\fP \fIsamples\fP @@ -911,8 +976,190 @@ \fBsourcestats\fP reports (and the \fItracking.log\fP and \fIstatistics.log\fP files) may be smaller than the actual offsets. .RE +.sp +\fBntsdumpdir\fP \fIdirectory\fP +.RS 4 +This directive specifies a directory for the client to save NTS cookies it +received from the server in order to avoid making an NTS\-KE request when +\fBchronyd\fP is started again. The cookies are saved separately for each NTP +source in files named by the IP address of the NTS\-KE server (e.g. +\fI1.2.3.4.nts\fP). By default, the client does not save the cookies. +.sp +If the directory does not exist, it will be created automatically. +.sp +An example of the directive is: +.sp +.if n .RS 4 +.nf +ntsdumpdir @CHRONYVARDIR@ +.fi +.if n .RE +.sp +This directory is used also by the NTS server to save keys. +.RE +.sp +\fBntsrefresh\fP \fIinterval\fP +.RS 4 +This directive specifies the maximum interval between NTS\-KE handshakes (in +seconds) in order to refresh the keys authenticating NTP packets. The default +value is 2419200 (4 weeks) and the maximum value is 2^31\-1 (68 years). +.RE +.sp +\fBntstrustedcerts\fP [\fIset\-ID\fP] \fIfile\fP|\fIdirectory\fP +.RS 4 +This directive specifies a file or directory containing certificates (in the +PEM format) of trusted certificate authorities (CA) which can be used to +verify certificates of NTS servers. +.sp +The optional \fIset\-ID\fP argument is a number in the range 0 through 2^32\-1, which +selects the set of certificates where certificates from the specified file +or directory are added. The default ID is 0, which is a set containing the +system\(cqs default trusted CAs (unless the \fBnosystemcert\fP directive is present). +All other sets are empty by default. A set of certificates can be selected for +verification of an NTS server by the \fBcertset\fP option in the \fBserver\fP or \fBpool\fP +directive. +.sp +This directive can be used multiple times to specify one or more sets of +trusted certificates, each containing certificates from one or more files +and/or directories. +.sp +It is not necessary to restart \fBchronyd\fP in order to reload the certificates if +they change (e.g. after a renewal). +.sp +An example is: +.sp +.if n .RS 4 +.nf +ntstrustedcerts /etc/pki/nts/foo.crt +ntstrustedcerts 1 /etc/pki/nts/bar.crt +ntstrustedcerts 1 /etc/pki/nts/baz.crt +ntstrustedcerts 2 /etc/pki/nts/qux.crt +.fi +.if n .RE +.RE +.sp +\fBnosystemcert\fP +.RS 4 +This directive disables the system\(cqs default trusted CAs. Only certificates +specified by the \fBntstrustedcerts\fP directive will be trusted. +.RE +.sp +\fBnocerttimecheck\fP \fIlimit\fP +.RS 4 +This directive disables the checks of the activation and expiration times of +certificates for the specified number of clock updates. It allows the NTS +authentication mechanism to be used on computers which start with wrong time +(e.g. due to not having an RTC or backup battery). Disabling the time checks +has important security implications and should be used only as a last resort, +preferably with a minimal number of trusted certificates. The default value is +0, which means the time checks are always enabled. +.sp +An example of the directive is: +.sp +.if n .RS 4 +.nf +nocerttimecheck 1 +.fi +.if n .RE +.sp +This would disable the time checks until the clock is updated for the first +time, assuming the first update corrects the clock and later checks can work +with correct time. +.RE .SS "Source selection" .sp +\fBauthselectmode\fP \fImode\fP +.RS 4 +NTP sources can be specified with the \fBkey\fP or \fBnts\fP option to enable +authentication to limit the impact of man\-in\-the\-middle attacks. The +attackers can drop or delay NTP packets (up to the \fBmaxdelay\fP and +\fBmaxdistance\fP limits), but they cannot modify the timestamps +contained in the packets. The attack can cause only a limited slew or step, and +also cause the clock to run faster or slower than real time (up to double of +the \fBmaxdrift\fP limit). +.sp +When authentication is enabled for an NTP source, it is important to disable +unauthenticated NTP sources which could be exploited in the attack, e.g. if +they are not reachable only over a trusted network. Alternatively, the source +selection can be configured with the \fBrequire\fP and \fBtrust\fP options to +synchronise to the unauthenticated sources only if they agree with the +authenticated sources and might have a positive impact on the accuracy of the +clock. Note that in this case the impact of the attack is higher. The attackers +cannot cause an arbitrarily large step or slew, but they have more control over +the frequency of the clock and can cause \fBchronyd\fP to report false information, +e.g. a significantly smaller root delay and dispersion. +.sp +This directive determines the default selection options for authenticated and +unauthenticated sources in order to simplify the configuration with the +configuration file and \fBchronyc\fP commands. It sets a policy for authentication. +.sp +Sources specified with the \fBnoselect\fP option are ignored (not counted as either +authenticated or unauthenticated), and they always have only the selection +options specified in the configuration. +.sp +There are four modes: +.sp +\fBrequire\fP +.RS 4 +Authentication is strictly required for NTP sources in this mode. If any +unauthenticated NTP sources are specified, they will automatically get the +\fBnoselect\fP option to prevent them from being selected for synchronisation. +.RE +.sp +\fBprefer\fP +.RS 4 +In this mode, authentication is optional and preferred. If it is enabled for at +least one NTP source, all unauthenticated NTP sources will get the \fBnoselect\fP +option. +.RE +.sp +\fBmix\fP +.RS 4 +In this mode, authentication is optional and synchronisation to a mix of +authenticated and unauthenticated NTP sources is allowed. If both authenticated +and unauthenticated NTP sources are specified, all authenticated NTP sources +and reference clocks will get the \fBrequire\fP and \fBtrust\fP options to prevent +synchronisation to unauthenticated NTP sources if they do not agree with a +majority of the authenticated sources and reference clocks. This is the default +mode. +.RE +.sp +\fBignore\fP +.RS 4 +In this mode, authentication is ignored in the source selection. All sources +will have only the selection options that were specified in the configuration +file, or \fBchronyc\fP command. This was the behaviour of \fBchronyd\fP in versions +before 4.0. +.RE +.RE +.sp + +.RS 4 +.sp +As an example, the following configuration using the default \fBmix\fP mode: +.sp +.if n .RS 4 +.nf +server foo.example.net nts +server bar.example.net nts +server baz.example.net +refclock SHM 0 +.fi +.if n .RE +.sp +is equivalent to the following configuration using the \fBignore\fP mode: +.sp +.if n .RS 4 +.nf +authselectmode ignore +server foo.example.net nts require trust +server bar.example.net nts require trust +server baz.example.net +refclock SHM 0 require trust +.fi +.if n .RE +.RE +.sp \fBcombinelimit\fP \fIlimit\fP .RS 4 When \fBchronyd\fP has multiple sources available for synchronisation, it has to @@ -934,11 +1181,11 @@ .sp \fBmaxdistance\fP \fIdistance\fP .RS 4 -The \fBmaxdistance\fP directive sets the maximum allowed root distance of the -sources to not be rejected by the source selection algorithm. The distance -includes the accumulated dispersion, which might be large when the source is no -longer synchronised, and half of the total round\-trip delay to the primary -source. +The \fBmaxdistance\fP directive sets the maximum root distance of a source to be +acceptable for synchronisation of the clock. Sources that have a distance +larger than the specified distance will be rejected. The distance estimates the +maximum error of the source. It includes the root dispersion and half of the +root delay (round\-trip time) accumulated on the path to the primary source. .sp By default, the maximum root distance is 3 seconds. .sp @@ -990,6 +1237,31 @@ .RE .SS "System clock" .sp +\fBclockprecision\fP \fIprecision\fP +.RS 4 +The \fBclockprecision\fP directive specifies the precision of the system clock (in +seconds). It is used by \fBchronyd\fP to estimate the minimum noise in NTP +measurements and randomise low\-order bits of timestamps in NTP responses. By +default, the precision is measured on start as the minimum time to read the +clock. +.sp +The measured value works well in most cases. However, it generally +overestimates the precision and it can be sensitive to the CPU speed, which can +change over time to save power. In some cases with a high\-precision clocksource +(e.g. the Time Stamp Counter of the CPU) and hardware timestamping, setting the +precision on the server to a smaller value can improve stability of clients\(aq +NTP measurements. The server\(cqs precision is reported on clients by the +\fBntpdata\fP command. +.sp +An example setting the precision to 8 nanoseconds is: +.sp +.if n .RS 4 +.nf +clockprecision 8e\-9 +.fi +.if n .RE +.RE +.sp \fBcorrtimeratio\fP \fIratio\fP .RS 4 When \fBchronyd\fP is slewing the system clock to correct an offset, the rate at @@ -1037,15 +1309,11 @@ .sp An example of the driftfile directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf driftfile @CHRONYVARDIR@/drift .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBfallbackdrift\fP \fImin\-interval\fP \fImax\-interval\fP @@ -1060,15 +1328,11 @@ update to switch between fallback drifts. They are defined as a power of 2 (in seconds). The syntax is as follows: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf fallbackdrift 16 19 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp In this example, the minimum interval is 16 (18 hours) and the maximum interval is 19 (6 days). The system clock frequency will be set to the first fallback 18 @@ -1140,7 +1404,7 @@ enable a server leap smear. .sp When smearing a leap second, the leap status is suppressed on the server and -the served time is corrected slowly be slewing instead of stepping. The clients +the served time is corrected slowly by slewing instead of stepping. The clients do not need any special configuration as they do not know there is any leap second and they follow the server time which eventually brings them back to UTC. Care must be taken to ensure they use only NTP servers which smear the @@ -1151,36 +1415,40 @@ .sp A recommended configuration to enable a server leap smear is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf leapsecmode slew maxslewrate 1000 -smoothtime 400 0.001 leaponly +smoothtime 400 0.001024 leaponly .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The first directive is necessary to disable the clock step which would reset the smoothing process. The second directive limits the slewing rate of the local clock to 1000 ppm, which improves the stability of the smoothing process when the local correction starts and ends. The third directive enables the server time smoothing process. It will start when the clock gets to 00:00:00 -UTC and it will take 17 hours 34 minutes to finish. The frequency offset will -be changing by 0.001 ppm per second and will reach a maximum of 31.623 ppm. The -\fBleaponly\fP option makes the duration of the leap smear constant and allows the -clients to safely synchronise with multiple identically configured leap -smearing servers. +UTC and it will take 62500 seconds (about 17.36 hours) to finish. The frequency +offset will be changing by 0.001024 ppm per second and will reach a maximum of +32 ppm in 31250 seconds. The \fBleaponly\fP option makes the duration of the leap +smear constant and allows the clients to safely synchronise with multiple +identically configured leap smearing servers. +.sp +The duration of the leap smear can be calculated from the specified wander as +.sp +.if n .RS 4 +.nf +duration = sqrt(4 / wander) +.fi +.if n .RE .RE .sp \fBleapsectz\fP \fItimezone\fP .RS 4 -This directive specifies a timezone in the system tz database which \fBchronyd\fP -can use to determine when will the next leap second occur and what is the -current offset between TAI and UTC. It will periodically check if 23:59:59 and -23:59:60 are valid times in the timezone. This typically works with the +This directive specifies a timezone in the system timezone database which +\fBchronyd\fP can use to determine when will the next leap second occur and what is +the current offset between TAI and UTC. It will periodically check if 23:59:59 +and 23:59:60 are valid times in the timezone. This normally works with the \fIright/UTC\fP timezone. .sp When a leap second is announced, the timezone needs to be updated at least 12 @@ -1202,57 +1470,46 @@ .sp An example of the directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf leapsectz right/UTC .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The following shell command verifies that the timezone contains leap seconds and can be used with this directive: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf $ TZ=right/UTC date \-d \(aqDec 31 2008 23:59:60\(aq Wed Dec 31 23:59:60 UTC 2008 .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBmakestep\fP \fIthreshold\fP \fIlimit\fP .RS 4 Normally \fBchronyd\fP will cause the system to gradually correct any time offset, by slowing down or speeding up the clock as required. In certain situations, -the system clock might be so far adrift that this slewing process would take a -very long time to correct the system clock. +e.g. when \fBchronyd\fP is initially started, the system clock might be so far +adrift that this slewing process would take a very long time to correct the +system clock. .sp This directive forces \fBchronyd\fP to step the system clock if the adjustment is larger than a threshold value, but only if there were no more clock updates -since \fBchronyd\fP was started than a specified limit (a negative value can be -used to disable the limit). +since \fBchronyd\fP was started than the specified limit. A negative value disables +the limit. .sp -This is particularly useful when using reference clocks, because the -\fBinitstepslew\fP directive works only with NTP sources. +On most systems it is desirable to step the system clock only on boot, before +starting programs that rely on time advancing monotonically forwards. .sp An example of the use of this directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf makestep 0.1 3 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp This would step the system clock if the adjustment is larger than 0.1 seconds, but only in the first three clock updates. @@ -1269,15 +1526,11 @@ .sp An example of the use of this directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf maxchange 1000 1 2 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp After the first clock update, \fBchronyd\fP will check the offset on every clock update, it will ignore two adjustments larger than 1000 seconds and exit on @@ -1322,8 +1575,8 @@ estimate might be so unreliable that it should not be used. By default, the threshold is 1000 ppm. .sp -Typical values for \fIskew\-in\-ppm\fP might be 100 for a dial\-up connection to -servers over a phone line, and 5 or 10 for a computer on a LAN. +Typical values for \fIskew\-in\-ppm\fP might be 100 for NTP sources polled over a +wireless network, and 10 or smaller for sources on a local wired network. .sp It should be noted that this is not the only means of protection against using unreliable estimates. At all times, \fBchronyd\fP keeps track of both the estimated @@ -1379,15 +1632,11 @@ .sp The frequency compensation is calculated (in ppm) as .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf -k0 + (T \- T0) * k1 + (T \- T0)^2 * k2 +comp = k0 + (T \- T0) * k1 + (T \- T0)^2 * k2 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The result has to be between \-10 ppm and 10 ppm, otherwise the measurement is considered invalid and will be ignored. The \fIk0\fP coefficient can be adjusted to @@ -1395,15 +1644,11 @@ .sp An example of the use is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf tempcomp /sys/class/hwmon/hwmon0/temp2_input 30 26000 0.0 0.000183 0.0 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The measured temperature will be read from the file in the Linux sysfs filesystem every 30 seconds. When the temperature is 26000 (26 degrees @@ -1416,21 +1661,15 @@ .sp An example is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf tempcomp /sys/class/hwmon/hwmon0/temp2_input 30 /etc/chrony.tempcomp .fi -.if n \{\ -.RE -.\} +.if n .RE .sp where the \fI/etc/chrony.tempcomp\fP file could have .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf 20000 1.0 21000 0.64 @@ -1444,9 +1683,7 @@ 29000 0.64 30000 1.0 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp Valid measurements with corresponding compensations are logged to the \fItempcomp.log\fP file if enabled by the \fBlog tempcomp\fP directive. @@ -1456,89 +1693,84 @@ \fBallow\fP [\fBall\fP] [\fIsubnet\fP] .RS 4 The \fBallow\fP directive is used to designate a particular subnet from which NTP -clients are allowed to access the computer as an NTP server. +clients are allowed to access the computer as an NTP server. It also controls +access of NTS\-KE clients when NTS is enabled on the server. .sp The default is that no clients are allowed access, i.e. \fBchronyd\fP operates purely as an NTP client. If the \fBallow\fP directive is used, \fBchronyd\fP will be both a client of its servers, and a server to other clients. .sp +This directive can be used multiple times. +.sp Examples of the use of the directive are as follows: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf allow 1.2.3.4 -allow 1.2 +allow 3.4.5.0/24 allow 3.4.5 -allow 6.7.8/22 -allow 6.7.8.9/22 allow 2001:db8::/32 allow 0/0 allow ::/0 allow .fi -.if n \{\ -.RE -.\} +.if n .RE .sp -The first directive allows a node with IPv4 address \fI1.2.3.4\fP to be an NTP -client of this computer. -The second directive allows any node with an IPv4 address of the form \fI1.2.x.y\fP -(with \fIx\fP and \fIy\fP arbitrary) to be an NTP client of this computer. Likewise, -the third directive allows any node with an IPv4 address of the form \fI3.4.5.x\fP -to have client NTP access. The fourth and fifth forms allow access from any -node with an IPv4 address of the form \fI6.7.8.x\fP, \fI6.7.9.x\fP, \fI6.7.10.x\fP or -\fI6.7.11.x\fP (with \fIx\fP arbitrary), i.e. the value 22 is the number of bits -defining the specified subnet. In the fifth form, the final byte is ignored. -The sixth form is used for IPv6 addresses. The seventh and eighth forms allow -access by any IPv4 and IPv6 node respectively. The ninth forms allows access by -any node (IPv4 or IPv6). +The first directive allows access from an IPv4 address. The second directive +allows access from all computers in an IPv4 subnet specified in the CIDR +notation. The third directive specifies the same subnet using a simpler +notation where the prefix length is determined by the number of dots. The +fourth directive specifies an IPv6 subnet. The fifth and sixth directives allow +access from all IPv4 and IPv6 addresses respectively. The seventh directive +allows access from all addresses (both IPv4 or IPv6). .sp A second form of the directive, \fBallow all\fP, has a greater effect, depending on the ordering of directives in the configuration file. To illustrate the effect, consider the two examples: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf allow 1.2.3.4 -deny 1.2.3 -allow 1.2 +deny 1.2.3.0/24 +allow 1.2.0.0/16 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp and .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf allow 1.2.3.4 -deny 1.2.3 -allow all 1.2 +deny 1.2.3.0/24 +allow all 1.2.0.0/16 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp In the first example, the effect is the same regardless of what order the three -directives are given in. So the \fI1.2.x.y\fP subnet is allowed access, except for -the \fI1.2.3.x\fP subnet, which is denied access, however the host \fI1.2.3.4\fP is -allowed access. -.sp -In the second example, the \fBallow all 1.2\fP directives overrides the effect of -\fIany\fP previous directive relating to a subnet within the specified subnet. -Within a configuration file this capability is probably rather moot; however, -it is of greater use for reconfiguration at run\-time via \fBchronyc\fP with the -\fBallow all\fP command. -.sp -The directive allows a hostname to be specified instead of an IP address, but -the name must be resolvable when \fBchronyd\fP is started (i.e. \fBchronyd\fP needs -to be started when the network is already up and DNS is working). +directives are given in. So the \fI1.2.0.0/16\fP subnet is allowed access, except +for the \fI1.2.3.0/24\fP subnet, which is denied access, however the host \fI1.2.3.4\fP +is allowed access. +.sp +In the second example, the \fBallow all 1.2.0.0/16\fP directive overrides the +effect of \fIany\fP previous directive relating to a subnet within the specified +subnet. Within a configuration file this capability is probably rather moot; +however, it is of greater use for reconfiguration at run\-time via \fBchronyc\fP +with the \fBallow all\fP command. +.sp +The rules are internally represented as a tree of tables with one level per +four bits of the IPv4 or IPv6 address. The order of the \fBallow\fP and \fBdeny\fP +directives matters if they modify the same records of one table, i.e. if one +subnet is included in the other subnet and their prefix lengths are at the same +level. For example, \fI1.2.3.0/28\fP and \fI1.2.3.0/29\fP are in different tables, but +\fI1.2.3.0/25\fP and \fI1.2.3.0/28\fP are in the same table. The configuration can be +verified for individual addresses with the \fBaccheck\fP +command in \fBchronyc\fP. +.sp +A hostname can be specified in the directives instead of the IP address, but +the name must be resolvable when \fBchronyd\fP is started, i.e. the network is +already up and DNS is working. If the hostname resolves to multiple addresses, +only the first address (in the order returned by the system resolver) will be +allowed or denied. .sp Note, if the \fBinitstepslew\fP directive is used in the configuration file, each of the computers listed in that directive must allow @@ -1548,9 +1780,10 @@ \fBdeny\fP [\fBall\fP] [\fIsubnet\fP] .RS 4 This is similar to the \fBallow\fP directive, except that it denies NTP -client access to a particular subnet or host, rather than allowing it. +and NTS\-KE client access to a particular subnet or host, rather than allowing +it. .sp -The syntax is identical. +The syntax is identical and the directive can be used multiple times too. .sp There is also a \fBdeny all\fP directive with similar behaviour to the \fBallow all\fP directive. @@ -1558,28 +1791,39 @@ .sp \fBbindaddress\fP \fIaddress\fP .RS 4 -The \fBbindaddress\fP directive binds the socket on which \fBchronyd\fP listens for NTP -requests to a local address of the computer. On systems other than Linux, the -address of the computer needs to be already configured when \fBchronyd\fP is -started. +The \fBbindaddress\fP directive binds the sockets on which \fBchronyd\fP listens for +NTP and NTS\-KE requests to a local address of the computer. On systems other +than Linux, the address of the computer needs to be already configured when +\fBchronyd\fP is started. .sp An example of the use of the directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf bindaddress 192.168.1.1 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp Currently, for each of the IPv4 and IPv6 protocols, only one \fBbindaddress\fP directive can be specified. Therefore, it is not useful on computers which should serve NTP on multiple network interfaces. .RE .sp +\fBbinddevice\fP \fIinterface\fP +.RS 4 +The \fBbinddevice\fP directive binds the NTP and NTS\-KE server sockets to a network +device specified by the interface name. This directive can specify only one +interface and it is supported on Linux only. +.sp +An example of the directive is: +.sp +.if n .RS 4 +.nf +binddevice eth0 +.fi +.if n .RE +.RE +.sp \fBbroadcast\fP \fIinterval\fP \fIaddress\fP [\fIport\fP] .RS 4 The \fBbroadcast\fP directive is used to declare a broadcast address to which @@ -1587,23 +1831,21 @@ as a broadcast server). Broadcast clients on that subnet will be able to synchronise. .sp +This directive can be used multiple times to specify multiple addresses. +.sp The syntax is as follows: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf -broadcast 30 192.168.1.255 -broadcast 60 192.168.2.255 12123 -broadcast 60 ff02::101 +broadcast 32 192.168.1.255 +broadcast 64 192.168.2.255 12123 +broadcast 64 ff02::101 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp In the first example, the destination port defaults to UDP port 123 (the normal NTP port). In the second example, the destination port is specified as 12123. The -first parameter in each case (30 or 60 respectively) is the interval in seconds +first parameter in each case (32 or 64 respectively) is the interval in seconds between broadcast packets being sent. The second parameter in each case is the broadcast address to send the packet to. This should correspond to the broadcast address of one of the network interfaces on the computer where @@ -1629,22 +1871,16 @@ to allocate for logging of client accesses and the state that \fBchronyd\fP as an NTP server needs to support the interleaved mode for its clients. The default limit is 524288 bytes, which is sufficient for monitoring about four thousand -clients at the same time. -.sp -In older \fBchrony\fP versions if the limit was set to 0, the memory allocation was -unlimited. +clients at the same time. The maximum value is 2^32\-1 (4 GB) on 32\-bit systems +and 2^35 (32 GB) on 64\-bit systems. .sp An example of the use of this directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf clientloglimit 1048576 .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBnoclientlog\fP @@ -1694,15 +1930,11 @@ The current root distance can be calculated from root delay and root dispersion (reported by the \fBtracking\fP command in \fBchronyc\fP) as: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf distance = delay / 2 + dispersion .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBorphan\fP @@ -1717,7 +1949,9 @@ that poll more than one server. Each server needs to be configured to poll all other servers with the \fBlocal\fP directive. This ensures only the server with the smallest reference ID has the local reference active and others are -synchronised to it. When that server fails, another will take over. +synchronised to it. If that server stops responding, the server with the second +smallest reference ID will take over when its local reference mode activates +(root distance reaches the threshold configured by the \fBdistance\fP option). .sp The \fBorphan\fP mode is compatible with the \fBntpd\fP\(cqs orphan mode (enabled by the \fBtos orphan\fP command). @@ -1729,15 +1963,11 @@ .sp An example of the directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf -local stratum 10 orphan +local stratum 10 orphan distance 0.1 .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBntpsigndsocket\fP \fIdirectory\fP @@ -1754,15 +1984,122 @@ .sp An example of the directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf ntpsigndsocket /var/lib/samba/ntp_signd .fi -.if n \{\ +.if n .RE .RE -.\} +.sp +\fBntsport\fP \fIport\fP +.RS 4 +This directive specifies the TCP port on which \fBchronyd\fP will provide the NTS +Key Establishment (NTS\-KE) service. The default port is 4460. +.sp +The port will be open only when a certificate and key is specified by the +\fBntsservercert\fP and \fBntsserverkey\fP directives. +.RE +.sp +\fBntsservercert\fP \fIfile\fP +.RS 4 +This directive specifies a file containing a certificate in the PEM format +for \fBchronyd\fP to operate as an NTS server. The file should also include +any intermediate certificates that the clients will need to validate the +server\(cqs certificate. +.sp +This directive can be used multiple times to specify multiple certificates for +different names of the server. +.sp +The files are loaded only once. \fBchronyd\fP needs to be restarted in order to +load a renewed certificate. The \fBntsdumpdir\fP and +\fBdumpdir\fP directives with the \fB\-r\fP option of \fBchronyd\fP are +recommended for a near\-seamless server operation. +.RE +.sp +\fBntsserverkey\fP \fIfile\fP +.RS 4 +This directive specifies a file containing a private key in the PEM format +for \fBchronyd\fP to operate as an NTS server. +.sp +This directive can be used multiple times to specify multiple keys. The number +of keys must be the same as the number of certificates and the corresponding +files must be specified in the same order. +.RE +.sp +\fBntsprocesses\fP \fIprocesses\fP +.RS 4 +This directive specifies how many helper processes will \fBchronyd\fP operating +as an NTS server start for handling client NTS\-KE requests in order to improve +performance with multi\-core CPUs and multithreading. If set to 0, no helper +process will be started and all NTS\-KE requests will be handled by the main +\fBchronyd\fP process. The default value is 1. +.RE +.sp +\fBmaxntsconnections\fP \fIconnections\fP +.RS 4 +This directive specifies the maximum number of concurrent NTS\-KE connections +per process that the NTS server will accept. The default value is 100. The +maximum practical value is half of the system \fBFD_SETSIZE\fP constant (usually +1024). +.RE +.sp +\fBntsdumpdir\fP \fIdirectory\fP +.RS 4 +This directive specifies a directory where \fBchronyd\fP operating as an NTS server +can save the keys which encrypt NTS cookies provided to clients. The keys are +saved to a single file named \fIntskeys\fP. When \fBchronyd\fP is restarted, reloading +the keys allows the clients to continue using old cookies and avoids a storm of +NTS\-KE requests. By default, the server does not save the keys. +.sp +An example of the directive is: +.sp +.if n .RS 4 +.nf +ntsdumpdir @CHRONYVARDIR@ +.fi +.if n .RE +.sp +This directory is used also by the NTS client to save NTS cookies. +.RE +.sp +\fBntsntpserver\fP \fIhostname\fP +.RS 4 +This directive specifies the hostname (as a fully qualified domain name) or +address of the NTP server(s) which is +provided in the NTS\-KE response to the clients. It allows the NTS\-KE server to +be separated from the NTP server. However, the servers need to share the keys, +i.e. external key management needs to be enabled by setting +\fBntsrotate\fP to 0. By default, no hostname or address is provided +to the clients, which means they should use the same server for NTS\-KE and NTP. +.RE +.sp +\fBntsrotate\fP \fIinterval\fP +.RS 4 +This directive specifies the rotation interval (in seconds) of the server key +which encrypts the NTS cookies. New keys are generated automatically from the +\fI/dev/urandom\fP device. The server keeps two previous keys to give the clients +time to get new cookies encrypted by the latest key. The interval is measured +as the server\(cqs operating time, i.e. the actual interval can be longer if +\fBchronyd\fP is not running continuously. The default interval is 604800 seconds +(1 week). The maximum value is 2^31\-1 (68 years). +.sp +The automatic rotation of the keys can be disabled by setting \fBntsrotate\fP to 0. +In this case the keys are assumed to be managed externally. \fBchronyd\fP will not +save the keys to the \fIntskeys\fP file and will reload the keys from the file when +the \fBrekey\fP command is issued in \fBchronyc\fP. The file can +be periodically copied from another server running \fBchronyd\fP (which does +not have \fBntsrotate\fP set to 0) in order to have one or more servers dedicated +to NTS\-KE. The NTS\-KE servers need to be configured with the +\fBntsntpserver\fP directive to point the clients to the right NTP +server. +.sp +An example of the directive is: +.sp +.if n .RS 4 +.nf +ntsrotate 2592000 +.fi +.if n .RE .RE .sp \fBport\fP \fIport\fP @@ -1830,21 +2167,32 @@ .sp An example use of the directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf ratelimit interval 1 burst 16 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp This would reduce the response rate for IP addresses sending packets on average more than once per 2 seconds, or sending packets in bursts of more than 16 packets, by up to 75% (with default \fBleak\fP of 2). .RE .sp +\fBntsratelimit\fP [\fIoption\fP]... +.RS 4 +This directive enables rate limiting of NTS\-KE requests. It is similar to the +\fBratelimit\fP directive, except the default interval is 6 +(1 connection per 64 seconds). +.sp +An example of the use of the directive is: +.sp +.if n .RS 4 +.nf +ntsratelimit interval 3 burst 1 +.fi +.if n .RE +.RE +.sp \fBsmoothtime\fP \fImax\-freq\fP \fImax\-wander\fP [\fBleaponly\fP] .RS 4 The \fBsmoothtime\fP directive can be used to enable smoothing of the time that @@ -1886,34 +2234,26 @@ An example suitable for clients using \fBntpd\fP and 1024 second polling interval could be: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf smoothtime 400 0.001 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp An example suitable for clients using \fBchronyd\fP on Linux could be: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf smoothtime 50000 0.01 .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .SS "Command and monitoring access" .sp \fBbindcmdaddress\fP \fIaddress\fP .RS 4 -The \fBbindcmdaddress\fP directive allows you to specify an IP address of an -interface on which \fBchronyd\fP will listen for monitoring command packets (issued +The \fBbindcmdaddress\fP directive specifies a local IP address to which \fBchronyd\fP +will bind the UDP socket listening for monitoring command packets (issued by \fBchronyc\fP). On systems other than Linux, the address of the interface needs to be already configured when \fBchronyd\fP is started. .sp @@ -1924,20 +2264,17 @@ path of the socket is \fI@CHRONYRUNDIR@/chronyd.sock\fP. The socket can be disabled by setting the path to \fI/\fP. .sp -By default, \fBchronyd\fP binds to the loopback interface (with addresses -\fI127.0.0.1\fP and \fI::1\fP). This blocks all access except from localhost. To listen -for command packets on all interfaces, you can add the lines: +By default, \fBchronyd\fP binds the UDP sockets to the addresses \fI127.0.0.1\fP and +\fI::1\fP (i.e. the loopback interface). This blocks all access except from +localhost. To listen for command packets on all interfaces, you can add the +lines: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf bindcmdaddress 0.0.0.0 bindcmdaddress :: .fi -.if n \{\ -.RE -.\} +.if n .RE .sp to the configuration file. .sp @@ -1946,15 +2283,26 @@ .sp An example that sets the path of the Unix domain command socket is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf bindcmdaddress /var/run/chrony/chronyd.sock .fi -.if n \{\ +.if n .RE .RE -.\} +.sp +\fBbindcmddevice\fP \fIinterface\fP +.RS 4 +The \fBbindcmddevice\fP directive binds the UDP command sockets to a network device +specified by the interface name. This directive can specify only one interface +and it is supported on Linux only. +.sp +An example of the directive is: +.sp +.if n .RS 4 +.nf +bindcmddevice eth0 +.fi +.if n .RE .RE .sp \fBcmdallow\fP [\fBall\fP] [\fIsubnet\fP] @@ -1994,15 +2342,11 @@ .sp An example shows the syntax: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf cmdport 257 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp This would make \fBchronyd\fP use UDP 257 as its command port. (\fBchronyc\fP would need to be run with the \fB\-p 257\fP switch to inter\-operate correctly.) @@ -2017,15 +2361,11 @@ .sp An example of the use of the directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf cmdratelimit interval 2 .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .SS "Real\-time clock (RTC)" .sp @@ -2040,15 +2380,11 @@ .sp An example of the directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf hwclockfile /etc/adjtime .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBrtcautotrim\fP \fIthreshold\fP @@ -2057,21 +2393,18 @@ automatically. When the system clock is synchronised and the estimated error between the two clocks is larger than the specified threshold, \fBchronyd\fP will trim the RTC as if the \fBtrimrtc\fP command in \fBchronyc\fP -was issued. +was issued. The trimming operation is accurate to only about 1 second, which is +the minimum effective threshold. .sp This directive is effective only with the \fBrtcfile\fP directive. .sp An example of the use of this directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf rtcautotrim 30 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp This would set the threshold error to 30 seconds. .RE @@ -2089,15 +2422,11 @@ .sp An example of the directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf rtcfile @CHRONYVARDIR@/rtc .fi -.if n \{\ -.RE -.\} +.if n .RE .sp \fBchronyd\fP saves information in this file when it exits and when the \fBwritertc\fP command is issued in \fBchronyc\fP. The information saved is the RTC\(cqs error at @@ -2114,8 +2443,8 @@ \h'-04' 1.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 1." 4.2 +. sp -1 +. IP " 1." 4.2 .\} You are running Linux. .RE @@ -2125,8 +2454,8 @@ \h'-04' 2.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 2." 4.2 +. sp -1 +. IP " 2." 4.2 .\} The kernel is compiled with extended real\-time clock support (i.e. the \fI/dev/rtc\fP device is capable of doing useful things). @@ -2137,8 +2466,8 @@ \h'-04' 3.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 3." 4.2 +. sp -1 +. IP " 3." 4.2 .\} You do not have other applications that need to make use of \fI/dev/rtc\fP at all. .RE @@ -2195,16 +2524,12 @@ actually appears as a single line in the file) from the log file is shown below. .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf 2016\-11\-09 05:40:50 203.0.113.15 N 2 111 111 1111 10 10 1.0 \(rs \-4.966e\-03 2.296e\-01 1.577e\-05 1.615e\-01 7.446e\-03 CB00717B 4B D K .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The columns are as follows (the quantities in square brackets are the values from the example line above): @@ -2214,8 +2539,8 @@ \h'-04' 1.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 1." 4.2 +. sp -1 +. IP " 1." 4.2 .\} Date [2015\-10\-13] .RE @@ -2225,8 +2550,8 @@ \h'-04' 2.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 2." 4.2 +. sp -1 +. IP " 2." 4.2 .\} Hour:Minute:Second. Note that the date\-time pair is expressed in UTC, not the local time zone. [05:40:50] @@ -2237,8 +2562,8 @@ \h'-04' 3.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 3." 4.2 +. sp -1 +. IP " 3." 4.2 .\} IP address of server or peer from which measurement came [203.0.113.15] .RE @@ -2248,8 +2573,8 @@ \h'-04' 4.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 4." 4.2 +. sp -1 +. IP " 4." 4.2 .\} Leap status (\fIN\fP means normal, \fI+\fP means that the last minute of the current month has 61 seconds, \fI\-\fP means that the last minute of the month has 59 @@ -2261,8 +2586,8 @@ \h'-04' 5.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 5." 4.2 +. sp -1 +. IP " 5." 4.2 .\} Stratum of remote computer. [2] .RE @@ -2272,8 +2597,8 @@ \h'-04' 6.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 6." 4.2 +. sp -1 +. IP " 6." 4.2 .\} RFC 5905 tests 1 through 3 (1=pass, 0=fail) [111] .RE @@ -2283,8 +2608,8 @@ \h'-04' 7.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 7." 4.2 +. sp -1 +. IP " 7." 4.2 .\} RFC 5905 tests 5 through 7 (1=pass, 0=fail) [111] .RE @@ -2294,8 +2619,8 @@ \h'-04' 8.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 8." 4.2 +. sp -1 +. IP " 8." 4.2 .\} Tests for maximum delay, maximum delay ratio and maximum delay dev ratio, against defined parameters, and a test for synchronisation loop (1=pass, @@ -2307,8 +2632,8 @@ \h'-04' 9.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 9." 4.2 +. sp -1 +. IP " 9." 4.2 .\} Local poll [10] .RE @@ -2318,8 +2643,8 @@ \h'-04' 10.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 10." 4.2 +. sp -1 +. IP " 10." 4.2 .\} Remote poll [10] .RE @@ -2329,8 +2654,8 @@ \h'-04' 11.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 11." 4.2 +. sp -1 +. IP " 11." 4.2 .\} \(oqScore\(cq (an internal score within each polling level used to decide when to increase or decrease the polling level. This is adjusted based on number of @@ -2342,8 +2667,8 @@ \h'-04' 12.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 12." 4.2 +. sp -1 +. IP " 12." 4.2 .\} The estimated local clock error (\fItheta\fP in RFC 5905). Positive indicates that the local clock is slow of the remote source. [\-4.966e\-03] @@ -2354,8 +2679,8 @@ \h'-04' 13.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 13." 4.2 +. sp -1 +. IP " 13." 4.2 .\} The peer delay (\fIdelta\fP in RFC 5905). [2.296e\-01] .RE @@ -2365,8 +2690,8 @@ \h'-04' 14.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 14." 4.2 +. sp -1 +. IP " 14." 4.2 .\} The peer dispersion (\fIepsilon\fP in RFC 5905). [1.577e\-05] .RE @@ -2376,8 +2701,8 @@ \h'-04' 15.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 15." 4.2 +. sp -1 +. IP " 15." 4.2 .\} The root delay (\fIDELTA\fP in RFC 5905). [1.615e\-01] .RE @@ -2387,8 +2712,8 @@ \h'-04' 16.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 16." 4.2 +. sp -1 +. IP " 16." 4.2 .\} The root dispersion (\fIEPSILON\fP in RFC 5905). [7.446e\-03] .RE @@ -2398,8 +2723,8 @@ \h'-04' 17.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 17." 4.2 +. sp -1 +. IP " 17." 4.2 .\} Reference ID of the server\(cqs source as a hexadecimal number. [CB00717B] .RE @@ -2409,8 +2734,8 @@ \h'-04' 18.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 18." 4.2 +. sp -1 +. IP " 18." 4.2 .\} NTP mode of the received packet (\fI1\fP=active peer, \fI2\fP=passive peer, \fI4\fP=server, \fIB\fP=basic, \fII\fP=interleaved). [4B] @@ -2421,8 +2746,8 @@ \h'-04' 19.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 19." 4.2 +. sp -1 +. IP " 19." 4.2 .\} Source of the local transmit timestamp (\fID\fP=daemon, \fIK\fP=kernel, \fIH\fP=hardware). [D] @@ -2433,8 +2758,8 @@ \h'-04' 20.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 20." 4.2 +. sp -1 +. IP " 20." 4.2 .\} Source of the local receive timestamp (\fID\fP=daemon, \fIK\fP=kernel, \fIH\fP=hardware). [K] @@ -2455,16 +2780,12 @@ \fIstatistics.log\fP. An example line (which actually appears as a single line in the file) from the log file is shown below. .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf 2016\-08\-10 05:40:50 203.0.113.15 6.261e\-03 \-3.247e\-03 \(rs 2.220e\-03 1.874e\-06 1.080e\-06 7.8e\-02 16 0 8 0.00 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The columns are as follows (the quantities in square brackets are the values from the example line above): @@ -2474,8 +2795,8 @@ \h'-04' 1.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 1." 4.2 +. sp -1 +. IP " 1." 4.2 .\} Date [2015\-07\-22] .RE @@ -2485,8 +2806,8 @@ \h'-04' 2.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 2." 4.2 +. sp -1 +. IP " 2." 4.2 .\} Hour:Minute:Second. Note that the date\-time pair is expressed in UTC, not the local time zone. [05:40:50] @@ -2497,8 +2818,8 @@ \h'-04' 3.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 3." 4.2 +. sp -1 +. IP " 3." 4.2 .\} IP address of server or peer from which measurement comes [203.0.113.15] .RE @@ -2508,8 +2829,8 @@ \h'-04' 4.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 4." 4.2 +. sp -1 +. IP " 4." 4.2 .\} The estimated standard deviation of the measurements from the source (in seconds). [6.261e\-03] @@ -2520,8 +2841,8 @@ \h'-04' 5.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 5." 4.2 +. sp -1 +. IP " 5." 4.2 .\} The estimated offset of the source (in seconds, positive means the local clock is estimated to be fast, in this case). [\-3.247e\-03] @@ -2532,8 +2853,8 @@ \h'-04' 6.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 6." 4.2 +. sp -1 +. IP " 6." 4.2 .\} The estimated standard deviation of the offset estimate (in seconds). [2.220e\-03] @@ -2544,8 +2865,8 @@ \h'-04' 7.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 7." 4.2 +. sp -1 +. IP " 7." 4.2 .\} The estimated rate at which the local clock is gaining or losing time relative to the source (in seconds per second, positive means the local clock @@ -2559,8 +2880,8 @@ \h'-04' 8.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 8." 4.2 +. sp -1 +. IP " 8." 4.2 .\} The estimated error in the rate value (in seconds per second). [1.080e\-06]. .RE @@ -2570,8 +2891,8 @@ \h'-04' 9.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 9." 4.2 +. sp -1 +. IP " 9." 4.2 .\} The ratio of |old_rate \- new_rate| / old_rate_error. Large values indicate the statistics are not modelling the source very well. [7.8e\-02] @@ -2582,8 +2903,8 @@ \h'-04' 10.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 10." 4.2 +. sp -1 +. IP " 10." 4.2 .\} The number of measurements currently being used for the regression algorithm. [16] @@ -2594,8 +2915,8 @@ \h'-04' 11.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 11." 4.2 +. sp -1 +. IP " 11." 4.2 .\} The new starting index (the oldest sample has index 0; this is the method used to prune old samples when it no longer looks like the measurements fit a @@ -2607,8 +2928,8 @@ \h'-04' 12.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 12." 4.2 +. sp -1 +. IP " 12." 4.2 .\} The number of runs. The number of runs of regression residuals with the same sign is computed. If this is too small it indicates that the measurements are @@ -2623,8 +2944,8 @@ \h'-04' 13.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 13." 4.2 +. sp -1 +. IP " 13." 4.2 .\} The estimated or configured asymmetry of network jitter on the path to the source which was used to correct the measured offsets. The asymmetry can be @@ -2641,16 +2962,12 @@ actually appears as a single line in the file) from the log file is shown below. .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf 2017\-08\-22 13:22:36 203.0.113.15 2 \-3.541 0.075 \-8.621e\-06 N \(rs 2 2.940e\-03 \-2.084e\-04 1.534e\-02 3.472e\-04 8.304e\-03 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The columns are as follows (the quantities in square brackets are the values from the example line above) : @@ -2660,8 +2977,8 @@ \h'-04' 1.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 1." 4.2 +. sp -1 +. IP " 1." 4.2 .\} Date [2017\-08\-22] .RE @@ -2671,8 +2988,8 @@ \h'-04' 2.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 2." 4.2 +. sp -1 +. IP " 2." 4.2 .\} Hour:Minute:Second. Note that the date\-time pair is expressed in UTC, not the local time zone. [13:22:36] @@ -2683,8 +3000,8 @@ \h'-04' 3.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 3." 4.2 +. sp -1 +. IP " 3." 4.2 .\} The IP address of the server or peer to which the local system is synchronised. [203.0.113.15] @@ -2695,8 +3012,8 @@ \h'-04' 4.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 4." 4.2 +. sp -1 +. IP " 4." 4.2 .\} The stratum of the local system. [2] .RE @@ -2706,8 +3023,8 @@ \h'-04' 5.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 5." 4.2 +. sp -1 +. IP " 5." 4.2 .\} The local system frequency (in ppm, positive means the local system runs fast of UTC). [\-3.541] @@ -2718,8 +3035,8 @@ \h'-04' 6.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 6." 4.2 +. sp -1 +. IP " 6." 4.2 .\} The error bounds on the frequency (in ppm). [0.075] .RE @@ -2729,8 +3046,8 @@ \h'-04' 7.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 7." 4.2 +. sp -1 +. IP " 7." 4.2 .\} The estimated local offset at the epoch, which is normally corrected by slewing the local clock (in seconds, positive indicates the clock is fast of @@ -2742,8 +3059,8 @@ \h'-04' 8.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 8." 4.2 +. sp -1 +. IP " 8." 4.2 .\} Leap status (\fIN\fP means normal, \fI+\fP means that the last minute of this month has 61 seconds, \fI\-\fP means that the last minute of the month has 59 seconds, @@ -2755,8 +3072,8 @@ \h'-04' 9.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 9." 4.2 +. sp -1 +. IP " 9." 4.2 .\} The number of combined sources. [2] .RE @@ -2766,8 +3083,8 @@ \h'-04' 10.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 10." 4.2 +. sp -1 +. IP " 10." 4.2 .\} The estimated standard deviation of the combined offset (in seconds). [2.940e\-03] @@ -2778,8 +3095,8 @@ \h'-04' 11.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 11." 4.2 +. sp -1 +. IP " 11." 4.2 .\} The remaining offset correction from the previous update (in seconds, positive means the system clock is slow of UTC). [\-2.084e\-04] @@ -2790,8 +3107,8 @@ \h'-04' 12.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 12." 4.2 +. sp -1 +. IP " 12." 4.2 .\} The total of the network path delays to the reference clock to which the local clock is ultimately synchronised (in seconds). [1.534e\-02] @@ -2802,8 +3119,8 @@ \h'-04' 13.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 13." 4.2 +. sp -1 +. IP " 13." 4.2 .\} The total dispersion accumulated through all the servers back to the reference clock to which the local clock is ultimately synchronised @@ -2815,8 +3132,8 @@ \h'-04' 14.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 14." 4.2 +. sp -1 +. IP " 14." 4.2 .\} The maximum estimated error of the system clock in the interval since the previous update (in seconds). It includes the offset, remaining offset @@ -2831,16 +3148,12 @@ line (which actually appears as a single line in the file) from the \fIrtc.log\fP file is shown below. .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf 2015\-07\-22 05:40:50 \-0.037360 1 \-0.037434\(rs \-37.948 12 5 120 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The columns are as follows (the quantities in square brackets are the values from the example line above): @@ -2850,8 +3163,8 @@ \h'-04' 1.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 1." 4.2 +. sp -1 +. IP " 1." 4.2 .\} Date [2015\-07\-22] .RE @@ -2861,8 +3174,8 @@ \h'-04' 2.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 2." 4.2 +. sp -1 +. IP " 2." 4.2 .\} Hour:Minute:Second. Note that the date\-time pair is expressed in UTC, not the local time zone. [05:40:50] @@ -2873,8 +3186,8 @@ \h'-04' 3.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 3." 4.2 +. sp -1 +. IP " 3." 4.2 .\} The measured offset between the RTC and the system clock in seconds. Positive indicates that the RTC is fast of the system time [\-0.037360]. @@ -2885,8 +3198,8 @@ \h'-04' 4.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 4." 4.2 +. sp -1 +. IP " 4." 4.2 .\} Flag indicating whether the regression has produced valid coefficients. (1 for yes, 0 for no). [1] @@ -2897,8 +3210,8 @@ \h'-04' 5.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 5." 4.2 +. sp -1 +. IP " 5." 4.2 .\} Offset at the current time predicted by the regression process. A large difference between this value and the measured offset tends to indicate that @@ -2910,8 +3223,8 @@ \h'-04' 6.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 6." 4.2 +. sp -1 +. IP " 6." 4.2 .\} The rate at which the RTC is losing or gaining time relative to the system clock. In ppm, with positive indicating that the RTC is gaining time. @@ -2923,8 +3236,8 @@ \h'-04' 7.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 7." 4.2 +. sp -1 +. IP " 7." 4.2 .\} The number of measurements used in the regression. [12] .RE @@ -2934,8 +3247,8 @@ \h'-04' 8.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 8." 4.2 +. sp -1 +. IP " 8." 4.2 .\} The number of runs of regression residuals of the same sign. Low values indicate that a straight line is no longer a good model of the measured data @@ -2947,8 +3260,8 @@ \h'-04' 9.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 9." 4.2 +. sp -1 +. IP " 9." 4.2 .\} The measurement interval used prior to the measurement being made (in seconds). [120] @@ -2961,15 +3274,11 @@ called \fIrefclocks.log\fP. An example line (which actually appears as a single line in the file) from the log file is shown below. .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf 2009\-11\-30 14:33:27.000000 PPS2 7 N 1 4.900000e\-07 \-6.741777e\-07 1.000e\-06 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The columns are as follows (the quantities in square brackets are the values from the example line above): @@ -2979,8 +3288,8 @@ \h'-04' 1.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 1." 4.2 +. sp -1 +. IP " 1." 4.2 .\} Date [2009\-11\-30] .RE @@ -2990,8 +3299,8 @@ \h'-04' 2.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 2." 4.2 +. sp -1 +. IP " 2." 4.2 .\} Hour:Minute:Second.Microsecond. Note that the date\-time pair is expressed in UTC, not the local time zone. [14:33:27.000000] @@ -3002,8 +3311,8 @@ \h'-04' 3.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 3." 4.2 +. sp -1 +. IP " 3." 4.2 .\} Reference ID of the reference clock from which the measurement came. [PPS2] .RE @@ -3013,8 +3322,8 @@ \h'-04' 4.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 4." 4.2 +. sp -1 +. IP " 4." 4.2 .\} Sequence number of driver poll within one polling interval for raw samples, or \fI\-\fP for filtered samples. [7] @@ -3025,8 +3334,8 @@ \h'-04' 5.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 5." 4.2 +. sp -1 +. IP " 5." 4.2 .\} Leap status (\fIN\fP means normal, \fI+\fP means that the last minute of the current month has 61 seconds, \fI\-\fP means that the last minute of the month has 59 @@ -3038,8 +3347,8 @@ \h'-04' 6.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 6." 4.2 +. sp -1 +. IP " 6." 4.2 .\} Flag indicating whether the sample comes from PPS source. (1 for yes, 0 for no, or \fI\-\fP for filtered sample). [1] @@ -3050,8 +3359,8 @@ \h'-04' 7.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 7." 4.2 +. sp -1 +. IP " 7." 4.2 .\} Local clock error measured by reference clock driver, or \fI\-\fP for filtered sample. [4.900000e\-07] @@ -3062,8 +3371,8 @@ \h'-04' 8.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 8." 4.2 +. sp -1 +. IP " 8." 4.2 .\} Local clock error with applied corrections. Positive indicates that the local clock is slow. [\-6.741777e\-07] @@ -3074,8 +3383,8 @@ \h'-04' 9.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 9." 4.2 +. sp -1 +. IP " 9." 4.2 .\} Assumed dispersion of the sample. [1.000e\-06] .RE @@ -3087,15 +3396,11 @@ a file called \fItempcomp.log\fP. An example line (which actually appears as a single line in the file) from the log file is shown below. .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf 2015\-04\-19 10:39:48 2.8000e+04 3.6600e\-01 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The columns are as follows (the quantities in square brackets are the values from the example line above): @@ -3105,8 +3410,8 @@ \h'-04' 1.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 1." 4.2 +. sp -1 +. IP " 1." 4.2 .\} Date [2015\-04\-19] .RE @@ -3116,8 +3421,8 @@ \h'-04' 2.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 2." 4.2 +. sp -1 +. IP " 2." 4.2 .\} Hour:Minute:Second. Note that the date\-time pair is expressed in UTC, not the local time zone. [10:39:48] @@ -3128,8 +3433,8 @@ \h'-04' 3.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 3." 4.2 +. sp -1 +. IP " 3." 4.2 .\} Temperature read from the sensor. [2.8000e+04] .RE @@ -3139,8 +3444,8 @@ \h'-04' 4.\h'+01'\c .\} .el \{\ -.sp -1 -.IP " 4." 4.2 +. sp -1 +. IP " 4." 4.2 .\} Applied compensation in ppm, positive means the system clock is running faster than it would be without the compensation. [3.6600e\-01] @@ -3152,15 +3457,11 @@ .RS 4 An example of the directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf log measurements statistics tracking .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBlogbanner\fP \fIentries\fP @@ -3184,15 +3485,11 @@ .sp An example of the use is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf logchange 0.1 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp which would cause a syslog message to be generated if a system clock error of over 0.1 seconds starts to be compensated. @@ -3200,20 +3497,17 @@ .sp \fBlogdir\fP \fIdirectory\fP .RS 4 -This directive allows the directory where log files are written to be -specified. +This directive specifies the directory for writing log files enabled by the +\fBlog\fP directive. If the directory does not exist, it will be created +automatically. .sp An example of the use of this directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf logdir /var/log/chrony .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBmailonchange\fP \fIemail\fP \fIthreshold\fP @@ -3224,15 +3518,11 @@ .sp An example of the use of this directive is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf mailonchange root@localhost 0.5 .fi -.if n \{\ -.RE -.\} +.if n .RE .sp This would send a mail message to root if a change of more than 0.5 seconds were applied to the system clock. @@ -3243,6 +3533,69 @@ .RE .SS "Miscellaneous" .sp +\fBconfdir\fP \fIdirectory\fP... +.RS 4 +The \fBconfdir\fP directive includes configuration files with the \fI.conf\fP suffix +from a directory. The files are included in the lexicographical order of the +file names. +.sp +Multiple directories (up to 10) can be specified with a single \fBconfdir\fP +directive. In this case, if multiple directories contain a file with the same +name, only the first file in the order of the specified directories will be +included. This enables a fragmented configuration where existing fragments can +be replaced by adding files to a different directory. +.sp +This directive can be used multiple times. +.sp +An example of the directive is: +.sp +.if n .RS 4 +.nf +confdir @SYSCONFDIR@/chrony.d +.fi +.if n .RE +.RE +.sp +\fBsourcedir\fP \fIdirectory\fP... +.RS 4 +The \fBsourcedir\fP directive is identical to the \fBconfdir\fP directive, except the +configuration files have the \fI.sources\fP suffix, they can only specify NTP +sources (i.e. the \fBserver\fP, \fBpool\fP, and \fBpeer\fP directives), they are expected +to have all lines terminated by the newline character, and they can be +reloaded by the \fBreload sources\fP command in +\fBchronyc\fP. It is particularly useful with dynamic sources like NTP servers +received from a DHCP server, which can be written to a file specific to the +network interface by a networking script. +.sp +This directive can be used multiple times. +.sp +An example of the directive is: +.sp +.if n .RS 4 +.nf +sourcedir /var/run/chrony\-dhcp +.fi +.if n .RE +.RE +.sp +\fBinclude\fP \fIpattern\fP +.RS 4 +The \fBinclude\fP directive includes a configuration file, or multiple configuration +files if a wildcard pattern is specified. Unlike with the \fBconfdir\fP directive, +the full name of the files needs to be specified and at least one file is +required to exist. +.sp +This directive can be used multiple times. +.sp +An example of the directive is: +.sp +.if n .RS 4 +.nf +include @SYSCONFDIR@/chrony.d/*.conf +.fi +.if n .RE +.RE +.sp \fBhwtimestamp\fP \fIinterface\fP [\fIoption\fP]... .RS 4 This directive enables hardware timestamping of NTP packets sent to and @@ -3273,8 +3626,9 @@ measurements\fP directive, and the \fBntpdata\fP report in \fBchronyc\fP. .sp -If the specified interface is \fI*\fP, \fBchronyd\fP will try to enable HW timestamping -on all available interfaces. +This directive can be used multiple times to enable HW timestamping on multiple +interfaces. If the specified interface is \fI*\fP, \fBchronyd\fP will try to enable HW +timestamping on all available interfaces. .sp The \fBhwtimestamp\fP directive has the following options: .sp @@ -3363,93 +3717,74 @@ .sp Examples of the directive are: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf hwtimestamp eth0 hwtimestamp eth1 txcomp 300e\-9 rxcomp 645e\-9 hwtimestamp * .fi -.if n \{\ -.RE -.\} -.RE -.sp -\fBinclude\fP \fIpattern\fP -.RS 4 -The \fBinclude\fP directive includes a configuration file or multiple configuration -files if a wildcard pattern is specified. This can be useful when maintaining -configuration on multiple hosts to keep the differences in separate files. -.sp -An example of the directive is: -.sp -.if n \{\ -.RS 4 -.\} -.nf -include @SYSCONFDIR@/chrony.d/*.conf -.fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBkeyfile\fP \fIfile\fP .RS 4 -This directive is used to specify the location of the file containing ID\-key -pairs for authentication of NTP packets. +This directive is used to specify the location of the file containing symmetric +keys which are shared between NTP servers and clients, or peers, in order to +authenticate NTP packets with a message authentication code (MAC) using a +cryptographic hash function or cipher. .sp The format of the directive is shown in the example below: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf keyfile @SYSCONFDIR@/chrony.keys .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The argument is simply the name of the file containing the ID\-key pairs. The format of the file is shown below: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf 10 tulip 11 hyacinth 20 MD5 ASCII:crocus -25 SHA1 HEX:1dc764e0791b11fa67efc7ecbc4b0d73f68a070c +25 SHA1 HEX:933F62BE1D604E68A81B557F18CFA200483F5B70 +30 AES128 HEX:7EA62AE64D190114D46D5A082F948EC1 +31 AES256 HEX:37DDCBC67BB902BCB8E995977FAB4D2B5642F5B32EBCEEE421921D97E5CBFE39 ... .fi -.if n \{\ -.RE -.\} +.if n .RE .sp -Each line consists of an ID, name of an authentication hash function (optional), -and a password. The ID can be any unsigned integer in the range 1 through -2^32\-1. The default hash function is \fBMD5\fP, which is always supported. +Each line consists of an ID, optional type, and key. .sp +The ID can be any positive integer in the range 1 through 2^32\-1. +.sp +The type is a name of a cryptographic hash function or cipher which is used to +generate and verify the MAC. The default type is \fBMD5\fP, which is always +supported. If \fBchronyd\fP was built with enabled support for hashing using a crypto library (nettle, nss, or libtomcrypt), the following functions are available: \fBMD5\fP, \fBSHA1\fP, \fBSHA256\fP, \fBSHA384\fP, \fBSHA512\fP. Depending on which library and version is -\fBchronyd\fP using, some or all of the following functions may also be available: -\fBSHA3\-224\fP, \fBSHA3\-256\fP, \fBSHA3\-384\fP, \fBSHA3\-512\fP, \fBRMD128\fP, \fBRMD160\fP, \fBRMD256\fP, -\fBRMD320\fP, \fBTIGER\fP, \fBWHIRLPOOL\fP. +\fBchronyd\fP using, some of the following hash functions and ciphers may +also be available: +\fBSHA3\-224\fP, \fBSHA3\-256\fP, \fBSHA3\-384\fP, \fBSHA3\-512\fP, \fBTIGER\fP, \fBWHIRLPOOL\fP, \fBAES128\fP, +\fBAES256\fP. .sp -The password can be specified as a string of characters not containing white +The key can be specified as a string of ASCII characters not containing white space with an optional \fBASCII:\fP prefix, or as a hexadecimal number with the \fBHEX:\fP prefix. The maximum length of the line is 2047 characters. +If the type is a cipher, the length of the key must match the cipher (i.e. 128 +bits for AES128 and 256 bits for AES256). .sp -The password is used with the hash function to generate and verify a message -authentication code (MAC) in NTP packets. It is recommended to use SHA1, or -stronger, hash function with random passwords specified in the hexadecimal -format that have at least 128 bits. \fBchronyd\fP will log a warning to -syslog on start if a source is specified in the configuration file with a key -that has password shorter than 80 bits. +It is recommended to use randomly generated keys, specified in the hexadecimal +format, which are at least 128 bits long (i.e. they have at least 32 characters +after the \fBHEX:\fP prefix). \fBchronyd\fP will log a warning to syslog on start if a +source is specified in the configuration file with a key shorter than 80 bits. +.sp +The recommended key types are AES ciphers and SHA3 hash functions. MD5 should +be avoided unless no other type is supported on the server and client, or +peers. .sp The \fBkeygen\fP command of \fBchronyc\fP can be used to generate random keys for the key file. By default, it generates 160\-bit MD5 or @@ -3478,15 +3813,11 @@ \fI@DEFAULT_PID_FILE@\fP. The \fBpidfile\fP directive allows the name to be changed, e.g.: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf pidfile /run/chronyd.pid .fi -.if n \{\ -.RE -.\} +.if n .RE .RE .sp \fBsched_priority\fP \fIpriority\fP @@ -3541,8 +3872,8 @@ \h'-04'\(bu\h'+03'\c .\} .el \{\ -.sp -1 -.IP \(bu 2.3 +. sp -1 +. IP \(bu 2.3 .\} Your institution might already operate servers on its network. Contact your system administrator to find out. @@ -3553,8 +3884,8 @@ \h'-04'\(bu\h'+03'\c .\} .el \{\ -.sp -1 -.IP \(bu 2.3 +. sp -1 +. IP \(bu 2.3 .\} Your ISP probably has one or more NTP servers available for its customers. @@ -3565,8 +3896,8 @@ \h'-04'\(bu\h'+03'\c .\} .el \{\ -.sp -1 -.IP \(bu 2.3 +. sp -1 +. IP \(bu 2.3 .\} Somewhere under the NTP homepage there is a list of public stratum 1 and stratum 2 servers. You should find one or more servers that are @@ -3579,28 +3910,24 @@ \h'-04'\(bu\h'+03'\c .\} .el \{\ -.sp -1 -.IP \(bu 2.3 +. sp -1 +. IP \(bu 2.3 .\} Use public servers from the \c -.URL "http://www.pool.ntp.org/" "pool.ntp.org" " " +.URL "https://www.pool.ntp.org/" "pool.ntp.org" " " project. .RE .sp Assuming that your NTP servers are called \fIfoo.example.net\fP, \fIbar.example.net\fP and \fIbaz.example.net\fP, your \fIchrony.conf\fP file could contain as a minimum: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf server foo.example.net server bar.example.net server baz.example.net .fi -.if n \{\ -.RE -.\} +.if n .RE .sp However, you will probably want to include some of the other directives. The \fBdriftfile\fP, \fBmakestep\fP and \fBrtcsync\fP @@ -3609,9 +3936,7 @@ synchronisation. The smallest useful configuration file would look something like: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf server foo.example.net iburst server bar.example.net iburst @@ -3620,27 +3945,37 @@ makestep 1.0 3 rtcsync .fi -.if n \{\ -.RE -.\} +.if n .RE .sp When using a pool of NTP servers (one name is used for multiple servers which might change over time), it is better to specify them with the \fBpool\fP directive instead of multiple \fBserver\fP directives. The configuration file could in this case look like: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf pool pool.ntp.org iburst driftfile @CHRONYVARDIR@/drift makestep 1.0 3 rtcsync .fi -.if n \{\ -.RE -.\} +.if n .RE +.sp +If the servers (or pool) support the Network Time Security (NTS) +authentication mechanism and \fBchronyd\fP is compiled with NTS support, the \fBnts\fP +option will enable a secure synchronisation to the servers. The configuration +file could look like: +.sp +.if n .RS 4 +.nf +server foo.example.net iburst nts +server bar.example.net iburst nts +server baz.example.net iburst nts +driftfile @CHRONYVARDIR@/drift +makestep 1.0 3 +rtcsync +.fi +.if n .RE .SS "NTP client with infrequent connection to NTP servers" .sp This section shows how to configure \fBchronyd\fP for computers that have @@ -3653,9 +3988,7 @@ \fIbar.example.net\fP and \fIbaz.example.net\fP, your \fIchrony.conf\fP file would now contain: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf server foo.example.net offline server bar.example.net offline @@ -3664,9 +3997,7 @@ makestep 1.0 3 rtcsync .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The \fBoffline\fP keyword indicates that the servers start in an offline state, and that they should not be contacted until \fBchronyd\fP receives notification from @@ -3678,52 +4009,44 @@ used to connect to the Internet and that \fBchronyc\fP has been installed at \fI@BINDIR@/chronyc\fP, the script \fI/etc/ppp/ip\-up\fP would include: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf @BINDIR@/chronyc online .fi -.if n \{\ -.RE -.\} +.if n .RE .sp and the script \fI/etc/ppp/ip\-down\fP would include: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf @BINDIR@/chronyc offline .fi -.if n \{\ -.RE -.\} +.if n .RE .sp \fBchronyd\fP\(cqs polling of the servers would now only occur whilst the machine is actually connected to the Internet. .SS "Isolated networks" .sp This section shows how to configure \fBchronyd\fP for computers that never have -network conectivity to any computer which ultimately derives its time from a +network connectivity to any computer which ultimately derives its time from a reference clock. .sp -In this situation, one computer is selected to be the master timeserver. The -other computers are either direct clients of the master, or clients of clients. +In this situation, one computer is selected to be the primary timeserver. The +other computers are either direct clients of the server, or clients of clients. .sp The \fBlocal\fP directive enables a local reference mode, which allows \fBchronyd\fP to appear synchronised even when it is not. .sp -The rate value in the master\(cqs drift file needs to be set to the average rate -at which the master gains or loses time. \fBchronyd\fP includes support for this, +The rate value in the server\(cqs drift file needs to be set to the average rate +at which the server gains or loses time. \fBchronyd\fP includes support for this, in the form of the \fBmanual\fP directive and the \fBsettime\fP command in the \fBchronyc\fP program. .sp -If the master is rebooted, \fBchronyd\fP can re\-read the drift rate from the drift -file. However, the master has no accurate estimate of the current time. To get -around this, the system can be configured so that the master can initially set +If the server is rebooted, \fBchronyd\fP can re\-read the drift rate from the drift +file. However, the server has no accurate estimate of the current time. To get +around this, the system can be configured so that the server can initially set itself to a \(oqmajority\-vote\(cq of selected clients\(aq times; this allows the -clients to \(oqflywheel\(cq the master while it is rebooting. +clients to \(oqflywheel\(cq the server while it is rebooting. .sp The \fBsmoothtime\fP directive is useful when the clocks of the clients need to stay close together when the local time is adjusted by the @@ -3732,12 +4055,10 @@ the local time is ready to be served. After that point, any adjustments will be smoothed out. .sp -A typical configuration file for the master (called \fImaster\fP) might be -(assuming the clients and the master are in the \fI192.168.165.x\fP subnet): +A typical configuration file for the server (called \fIntp.local\fP) might be +(assuming the clients and the server are in the \fI192.168.165.x\fP subnet): .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf initstepslew 1 client1 client3 client6 driftfile @CHRONYVARDIR@/drift @@ -3747,58 +4068,48 @@ smoothtime 400 0.01 rtcsync .fi -.if n \{\ -.RE -.\} +.if n .RE .sp -For the clients that have to resynchronise the master when it restarts, +For the clients that have to resynchronise the server when it restarts, the configuration file might be: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf -server master iburst +server ntp.local iburst driftfile @CHRONYVARDIR@/drift allow 192.168.165.0/24 makestep 1.0 3 rtcsync .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The rest of the clients would be the same, except that the \fBallow\fP directive is not required. .sp -If there is no suitable computer to be designated as the master, or there is a -requirement to keep the clients synchronised even when it fails, the \fBorphan\fP -option of the \fBlocal\fP directive enables a special mode where the master is -selected from multiple computers automatically. They all need to use the same -\fBlocal\fP configuration and poll one another. The server with the smallest -reference ID (which is based on its IP address) will take the role of the -master and others will be synchronised to it. When it fails, the server with -the second smallest reference ID will take over and so on. +If there is no suitable computer to be designated as the primary server, or +there is a requirement to keep the clients synchronised even when it fails, the +\fBorphan\fP option of the \fBlocal\fP directive enables a special mode where the +server is selected from multiple computers automatically. They all need to use +the same \fBlocal\fP configuration and poll one another. The server with the +smallest reference ID (which is based on its IP address) will take the role of +the primary server and others will be synchronised to it. When it fails, the +server with the second smallest reference ID will take over and so on. .sp A configuration file for the first server might be (assuming there are three -servers called \fImaster1\fP, \fImaster2\fP, and \fImaster3\fP): +servers called \fIntp1.local\fP, \fIntp2.local\fP, and \fIntp3.local\fP): .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf -initstepslew 1 master2 master3 -server master2 -server master3 +initstepslew 1 ntp2.local ntp3.local +server ntp2.local +server ntp3.local driftfile @CHRONYVARDIR@/drift local stratum 8 orphan manual allow 192.168.165.0/24 rtcsync .fi -.if n \{\ -.RE -.\} +.if n .RE .sp The other servers would be the same, except the hostnames in the \fBinitstepslew\fP and \fBserver\fP directives would be modified to specify the other servers. Their @@ -3873,9 +4184,7 @@ .sp For the \fIchrony.conf\fP file, the following can be used as an example. .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf server foo.example.net maxdelay 0.4 offline server bar.example.net maxdelay 0.4 offline @@ -3888,9 +4197,7 @@ dumpdir @CHRONYVARDIR@ rtcfile @CHRONYVARDIR@/rtc .fi -.if n \{\ -.RE -.\} +.if n .RE .sp \fBpppd\fP is used for connecting to the Internet. This runs two scripts \fI/etc/ppp/ip\-up\fP and \fI/etc/ppp/ip\-down\fP when the link goes online and offline @@ -3898,27 +4205,19 @@ .sp The relevant part of the \fI/etc/ppp/ip\-up\fP file is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf @BINDIR@/chronyc online .fi -.if n \{\ -.RE -.\} +.if n .RE .sp and the relevant part of the \fI/etc/ppp/ip\-down\fP script is: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf @BINDIR@/chronyc \-m offline dump writertc .fi -.if n \{\ -.RE -.\} +.if n .RE .sp \fBchronyd\fP is started during the boot sequence with the \fB\-r\fP and \fB\-s\fP options. It might need to be started before any software that depends on the system clock @@ -3931,7 +4230,7 @@ .SS "Public NTP server" .sp \fBchronyd\fP can be configured to operate as a public NTP server, e.g. to join the -.URL "http://www.pool.ntp.org/en/join.html" "pool.ntp.org" " " +.URL "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 allow client access from all addresses. It is recommended to find at least four @@ -3952,9 +4251,7 @@ .sp The configuration file could look like: .sp -.if n \{\ -.RS 4 -.\} +.if n .RS 4 .nf server foo.example.net iburst server bar.example.net iburst @@ -3968,9 +4265,7 @@ driftfile @CHRONYVARDIR@/drift dumpdir @CHRONYRUNDIR@ .fi -.if n \{\ -.RE -.\} +.if n .RE .SH "SEE ALSO" .sp \fBchronyc(1)\fP, \fBchronyd(8)\fP diff -Nru chrony-3.5/doc/chronyd.adoc chrony-4.1/doc/chronyd.adoc --- chrony-3.5/doc/chronyd.adoc 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/doc/chronyd.adoc 2021-05-12 11:06:15.000000000 +0000 @@ -41,7 +41,7 @@ will read them from a configuration file. The compiled-in default location of the file is _@SYSCONFDIR@/chrony.conf_. -Information messages and warnings will be logged to syslog. +Informational messages, warnings, and errors will be logged to syslog. == OPTIONS @@ -55,20 +55,32 @@ *-f* _file_:: This option can be used to specify an alternate location for the configuration -file (default _@SYSCONFDIR@/chrony.conf_). +file. The compiled-in default value is _@SYSCONFDIR@/chrony.conf_. *-n*:: When run in this mode, the program will not detach itself from the terminal. *-d*:: When run in this mode, the program will not detach itself from the terminal, -and all messages will be written to the terminal instead of syslog. When -*chronyd* was compiled with debugging support, this option can be used twice to -print also debugging messages. +and all messages will be written to the terminal instead of syslog. If +*chronyd* was compiled with enabled support for debugging, this option can be +used twice to enable debug messages. *-l* _file_:: -This option specifies a file which should be used for logging instead of syslog -or terminal. +This option enables writing of log messages to a file instead of syslog or the +terminal. + +*-L* _level_:: +This option specifies the minimum severity level of messages to be written to +the log file, syslog, or terminal. The following levels can be specified: +0 (informational), 1 (warning), 2 (non-fatal error), and 3 (fatal error). The +default value is 0. + +*-p*:: +When run in this mode, *chronyd* will print the configuration and exit. It will +not detach from the terminal. This option can be used to verify the syntax of +the configuration and get the whole configuration, even if it is split into +multiple files and read by the *include* or *confdir* directive. *-q*:: When run in this mode, *chronyd* will set the system clock once and exit. It @@ -125,32 +137,55 @@ *-u* _user_:: This option sets the name of the system user to which *chronyd* will switch after start in order to drop root privileges. It overrides the -<> directive (default _@DEFAULT_USER@_). +<> directive. The compiled-in default value is +_@DEFAULT_USER@_. + On Linux, *chronyd* needs to be compiled with support for the *libcap* library. On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes. The child process retains root privileges, but can only perform a very limited range of privileged system calls on behalf of the parent. +*-U*:: +This option disables a check for root privileges to allow *chronyd* to be +started under a non-root user, assuming the process will have all capabilities +(e.g. provided by the service manager) and access to all files, directories, +and devices, needed to operate correctly in the specified configuration. Note +that different capabilities might be needed with different configurations and +different Linux kernel versions. Starting *chronyd* under a non-root user is +not recommended when the configuration is not known, or at least limited to +specific directives. + *-F* _level_:: -This option configures a system call filter when *chronyd* is compiled with -support for the Linux secure computing (seccomp) facility. In level 1 the -process is killed when a forbidden system call is made, in level -1 the SIGSYS -signal is thrown instead and in level 0 the filter is disabled (default 0). -+ -It's recommended to enable the filter only when it's known to work on the -version of the system where *chrony* is installed as the filter needs to allow -also system calls made from libraries that *chronyd* is using (e.g. libc) and -different versions or implementations of the libraries may make different -system calls. If the filter is missing some system call, *chronyd* could be -killed even in normal operation. +This option configures system call filters loaded by *chronyd* processes if it +was compiled with support for the Linux secure computing (seccomp) facility. +Three levels are defined: 0, 1, 2. The filters are disabled at level 0. At +levels 1 and 2, *chronyd* will be killed if it makes a system call which is +blocked by the filters. The level can be specified as a negative number to +trigger the SIGSYS signal instead of SIGKILL, which can be useful for +debugging. The default value is 0. ++ +At level 1, the filters allow only selected system calls that are normally +expected to be made by *chronyd*. Other system calls are blocked. This level is +recommended only if it is known to work on the version of the system where +*chrony* is installed. The filters need to allow also system calls made by +libraries that *chronyd* is using (e.g. libc), but different versions or +implementations of the libraries might make different system calls. If the +filters are missing a system call, *chronyd* could be killed even in normal +operation. ++ +At level 2, the filters block only a small number of specific system calls +(e.g. fork and exec). This approach should avoid false positives, but the +protection of the system against a compromised *chronyd* process is much more +limited. ++ +The filters cannot be enabled with the *mailonchange* directive. *-P* _priority_:: On Linux, this option will select the SCHED_FIFO real-time scheduler at the specified priority (which must be between 0 and 100). On macOS, this option -must have either a value of 0 (the default) to disable the thread time +must have either a value of 0 to disable the thread time constraint policy or 1 for the policy to be enabled. Other systems do not -support this option. +support this option. The default value is 0. *-m*:: This option will lock *chronyd* into RAM so that it will never be paged out. @@ -160,14 +195,15 @@ This option disables the control of the system clock. *chronyd* will not try to make any adjustments of the clock. It will assume the clock is free running and still track its offset and frequency relative to the estimated true time. This -option allows *chronyd* to run without the capability to adjust or set the -system clock (e.g. in some containers) in order to operate as an NTP server. It -is not recommended to run *chronyd* (with or without *-x*) when another process -is controlling the system clock. +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. -*-v*:: +*-v*, *--version*:: 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 _@SYSCONFDIR@/chrony.conf_ diff -Nru chrony-3.5/doc/chronyd.man.in chrony-4.1/doc/chronyd.man.in --- chrony-3.5/doc/chronyd.man.in 2019-05-14 11:00:04.000000000 +0000 +++ chrony-4.1/doc/chronyd.man.in 2021-05-13 10:48:12.000000000 +0000 @@ -1,23 +1,32 @@ '\" t .\" Title: chronyd -.\" Author: [see the "AUTHORS" section] -.\" Generator: Asciidoctor 1.5.6.1 -.\" Date: 2019-05-10 +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.10 +.\" Date: 2021-05-12 .\" Manual: System Administration .\" Source: chrony @CHRONY_VERSION@ .\" Language: English .\" -.TH "CHRONYD" "8" "2019-05-10" "chrony @CHRONY_VERSION@" "System Administration" +.TH "CHRONYD" "8" "2021-05-12" "chrony @CHRONY_VERSION@" "System Administration" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 .nh .ad l .de URL -\\$2 \(laURL: \\$1 \(ra\\$3 +\fI\\$2\fP <\\$1>\\$3 .. -.if \n[.g] .mso www.tmac -.LINKSTYLE blue R < > +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} .SH "NAME" chronyd \- chrony daemon .SH "SYNOPSIS" @@ -35,7 +44,7 @@ will read them from a configuration file. The compiled\-in default location of the file is \fI@SYSCONFDIR@/chrony.conf\fP. .sp -Information messages and warnings will be logged to syslog. +Informational messages, warnings, and errors will be logged to syslog. .SH "OPTIONS" .sp \fB\-4\fP @@ -53,7 +62,7 @@ \fB\-f\fP \fIfile\fP .RS 4 This option can be used to specify an alternate location for the configuration -file (default \fI@SYSCONFDIR@/chrony.conf\fP). +file. The compiled\-in default value is \fI@SYSCONFDIR@/chrony.conf\fP. .RE .sp \fB\-n\fP @@ -64,15 +73,31 @@ \fB\-d\fP .RS 4 When run in this mode, the program will not detach itself from the terminal, -and all messages will be written to the terminal instead of syslog. When -\fBchronyd\fP was compiled with debugging support, this option can be used twice to -print also debugging messages. +and all messages will be written to the terminal instead of syslog. If +\fBchronyd\fP was compiled with enabled support for debugging, this option can be +used twice to enable debug messages. .RE .sp \fB\-l\fP \fIfile\fP .RS 4 -This option specifies a file which should be used for logging instead of syslog -or terminal. +This option enables writing of log messages to a file instead of syslog or the +terminal. +.RE +.sp +\fB\-L\fP \fIlevel\fP +.RS 4 +This option specifies the minimum severity level of messages to be written to +the log file, syslog, or terminal. The following levels can be specified: +0 (informational), 1 (warning), 2 (non\-fatal error), and 3 (fatal error). The +default value is 0. +.RE +.sp +\fB\-p\fP +.RS 4 +When run in this mode, \fBchronyd\fP will print the configuration and exit. It will +not detach from the terminal. This option can be used to verify the syntax of +the configuration and get the whole configuration, even if it is split into +multiple files and read by the \fBinclude\fP or \fBconfdir\fP directive. .RE .sp \fB\-q\fP @@ -143,7 +168,8 @@ .RS 4 This option sets the name of the system user to which \fBchronyd\fP will switch after start in order to drop root privileges. It overrides the -\fBuser\fP directive (default \fI@DEFAULT_USER@\fP). +\fBuser\fP directive. The compiled\-in default value is +\fI@DEFAULT_USER@\fP. .sp On Linux, \fBchronyd\fP needs to be compiled with support for the \fBlibcap\fP library. On macOS, FreeBSD, NetBSD and Solaris \fBchronyd\fP forks into two processes. @@ -151,28 +177,52 @@ range of privileged system calls on behalf of the parent. .RE .sp +\fB\-U\fP +.RS 4 +This option disables a check for root privileges to allow \fBchronyd\fP to be +started under a non\-root user, assuming the process will have all capabilities +(e.g. provided by the service manager) and access to all files, directories, +and devices, needed to operate correctly in the specified configuration. Note +that different capabilities might be needed with different configurations and +different Linux kernel versions. Starting \fBchronyd\fP under a non\-root user is +not recommended when the configuration is not known, or at least limited to +specific directives. +.RE +.sp \fB\-F\fP \fIlevel\fP .RS 4 -This option configures a system call filter when \fBchronyd\fP is compiled with -support for the Linux secure computing (seccomp) facility. In level 1 the -process is killed when a forbidden system call is made, in level \-1 the SIGSYS -signal is thrown instead and in level 0 the filter is disabled (default 0). -.sp -It\(cqs recommended to enable the filter only when it\(cqs known to work on the -version of the system where \fBchrony\fP is installed as the filter needs to allow -also system calls made from libraries that \fBchronyd\fP is using (e.g. libc) and -different versions or implementations of the libraries may make different -system calls. If the filter is missing some system call, \fBchronyd\fP could be -killed even in normal operation. +This option configures system call filters loaded by \fBchronyd\fP processes if it +was compiled with support for the Linux secure computing (seccomp) facility. +Three levels are defined: 0, 1, 2. The filters are disabled at level 0. At +levels 1 and 2, \fBchronyd\fP will be killed if it makes a system call which is +blocked by the filters. The level can be specified as a negative number to +trigger the SIGSYS signal instead of SIGKILL, which can be useful for +debugging. The default value is 0. +.sp +At level 1, the filters allow only selected system calls that are normally +expected to be made by \fBchronyd\fP. Other system calls are blocked. This level is +recommended only if it is known to work on the version of the system where +\fBchrony\fP is installed. The filters need to allow also system calls made by +libraries that \fBchronyd\fP is using (e.g. libc), but different versions or +implementations of the libraries might make different system calls. If the +filters are missing a system call, \fBchronyd\fP could be killed even in normal +operation. +.sp +At level 2, the filters block only a small number of specific system calls +(e.g. fork and exec). This approach should avoid false positives, but the +protection of the system against a compromised \fBchronyd\fP process is much more +limited. +.sp +The filters cannot be enabled with the \fBmailonchange\fP directive. .RE .sp \fB\-P\fP \fIpriority\fP .RS 4 On Linux, this option will select the SCHED_FIFO real\-time scheduler at the specified priority (which must be between 0 and 100). On macOS, this option -must have either a value of 0 (the default) to disable the thread time +must have either a value of 0 to disable the thread time constraint policy or 1 for the policy to be enabled. Other systems do not -support this option. +support this option. The default value is 0. .RE .sp \fB\-m\fP @@ -186,16 +236,19 @@ This option disables the control of the system clock. \fBchronyd\fP will not try to make any adjustments of the clock. It will assume the clock is free running and still track its offset and frequency relative to the estimated true time. This -option allows \fBchronyd\fP to run without the capability to adjust or set the -system clock (e.g. in some containers) in order to operate as an NTP server. It -is not recommended to run \fBchronyd\fP (with or without \fB\-x\fP) when another process -is controlling the system clock. +option allows \fBchronyd\fP to be started without the capability to adjust or set +the system clock (e.g. in some containers) to operate as an NTP server. .RE .sp -\fB\-v\fP +\fB\-v\fP, \fB\-\-version\fP .RS 4 With this option \fBchronyd\fP will print version number to the terminal and exit. .RE +.sp +\fB\-h\fP, \fB\-\-help\fP +.RS 4 +With this option \fBchronyd\fP will print a help message to the terminal and exit. +.RE .SH "FILES" .sp \fI@SYSCONFDIR@/chrony.conf\fP diff -Nru chrony-3.5/doc/faq.adoc chrony-4.1/doc/faq.adoc --- chrony-3.5/doc/faq.adoc 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/doc/faq.adoc 2021-05-12 11:06:15.000000000 +0000 @@ -1,7 +1,7 @@ // This file is part of chrony // // Copyright (C) Richard P. Curnow 1997-2003 -// Copyright (C) Miroslav Lichvar 2014-2016 +// Copyright (C) Miroslav Lichvar 2014-2016, 2020-2021 // // This program is free software; you can redistribute it and/or modify // it under the terms of version 2 of the GNU General Public License as @@ -24,17 +24,20 @@ === How does `chrony` compare to `ntpd`? -`chronyd` was designed to work well in a wide range of conditions and it can -usually synchronise the system clock faster and with better time accuracy. It -doesn't implement some of the less useful NTP modes like broadcast client or -multicast server/client. +`chrony` and `ntpd` are two different implementations of the Network Time +Protocol (NTP). + +`chrony` is a newer implementation, which was designed to work well in a wider +range of conditions. It can usually synchronise the system clock faster and +with better time accuracy. It has many features, but it does not implement some +of the less useful NTP modes like broadcast client or multicast server/client. If your computer is connected to the Internet only for few minutes at a time, the network connection is often congested, you turn your computer off or suspend it frequently, the clock is not very stable (e.g. there are rapid -changes in the temperature or it's a virtual machine), or you want to use NTP +changes in the temperature or it is a virtual machine), or you want to use NTP on an isolated network with no hardware reference clocks in sight, `chrony` -will probably work much better for you. +will probably work better for you. For a more detailed comparison of features and performance, see the https://chrony.tuxfamily.org/comparison.html[comparison page] on the `chrony` @@ -46,9 +49,11 @@ First, the client needs to know which NTP servers it should ask for the current time. They are specified by the `server` or `pool` directive. The `pool` -directive can be used for names that resolve to multiple addresses. For good -reliability the client should have at least three servers. The `iburst` option -speeds up the initial synchronisation. +directive is used with names that resolve to multiple addresses of different +servers. For reliable operation, the client should have at least three servers. + +The `iburst` option enables a burst of requests to speed up the initial +synchronisation. To stabilise the initial synchronisation on the next start, the estimated drift of the system clock is saved to a file specified by the `driftfile` directive. @@ -59,13 +64,13 @@ that. In order to keep the real-time clock (RTC) close to the true time, so the -system time is reasonably close to the true time when it's initialised on the +system time is reasonably close to the true time when it is initialised on the next boot from the RTC, the `rtcsync` directive enables a mode in which the system time is periodically copied to the RTC. It is supported on Linux and macOS. -If you want to use public NTP servers from the -http://www.pool.ntp.org/[pool.ntp.org] project, the minimal _chrony.conf_ file +If you wanted to use public NTP servers from the +https://www.pool.ntp.org/[pool.ntp.org] project, the minimal _chrony.conf_ file could be: ---- @@ -75,52 +80,43 @@ rtcsync ---- -=== How do I make an NTP server from an NTP client? +=== How do I make an NTP server? -You need to add an `allow` directive to the _chrony.conf_ file in order to open -the NTP port and allow `chronyd` to reply to client requests. `allow` with no -specified subnet allows access from all IPv4 and IPv6 addresses. +By default, `chronyd` does not operate as an NTP server. You need to add an +`allow` directive to the _chrony.conf_ file in order for `chronyd` to open the +server NTP port and respond to client requests. -=== I have several computers on a LAN. Should be all clients of an external server? +---- +allow 192.168.1.0/24 +---- -The best configuration is usually to make one computer the server, with -the others as clients of it. Add a `local` directive to the server's -_chrony.conf_ file. This configuration will be better because +An `allow` directive with no specified subnet allows access from all IPv4 and +IPv6 addresses. + +=== Should all computers on a LAN be clients of an external server? + +It depends on the requirements. Usually, the best configuration is to make one +computer the server, with the others as clients of it. Add a `local` directive +to the server's _chrony.conf_ file. This configuration will be better because * the load on the external connection is less * the load on the external NTP server(s) is less * if your external connection goes down, the computers on the LAN will maintain a common time with each other. -=== Must I specify servers by IP address if DNS is not available on chronyd start? +=== Must I specify servers by IP address if DNS is not available on `chronyd` start? -No. Starting from version 1.25, `chronyd` will keep trying to resolve +No, `chronyd` will keep trying to resolve the names specified by the `server`, `pool`, and `peer` directives in an increasing interval until it succeeds. The `online` command can be issued from `chronyc` to force `chronyd` to try to resolve the names immediately. === How can I make `chronyd` more secure? -If you don't need to serve time to NTP clients or peers, you can add `port 0` -to the _chrony.conf_ file to completely disable the NTP server functionality -and prevent NTP requests from reaching `chronyd`. Starting from version 2.0, -the NTP server port is open only when client access is allowed by the `allow` -directive or command, an NTP peer is configured, or the `broadcast` directive -is used. - -If you don't need to use `chronyc` remotely, you can add the following -directives to the configuration file to bind the command sockets to the -loopback interface. This is done by default since version 2.0. - ----- -bindcmdaddress 127.0.0.1 -bindcmdaddress ::1 ----- - -If you don't need to use `chronyc` at all or you need to run `chronyc` only +If you do not need to use `chronyc`, or you want to run `chronyc` only under the root or _chrony_ user (which can access `chronyd` through a Unix -domain socket since version 2.2), you can disable the internet command sockets -completely by adding `cmdport 0` to the configuration file. +domain socket), you can disable the IPv4 and IPv6 command sockets (by default +listening on localhost) by adding `cmdport 0` to the configuration file. You can specify an unprivileged user with the `-u` option, or the `user` directive in the _chrony.conf_ file, to which `chronyd` will switch after start @@ -133,29 +129,144 @@ Also, if `chronyd` is compiled with support for the Linux secure computing (seccomp) facility, you can enable a system call filter with the `-F` option. It will significantly reduce the kernel attack surface and possibly prevent -kernel exploits from the `chronyd` process if it's compromised. It's -recommended to enable the filter only when it's known to work on the version of +kernel exploits from the `chronyd` process if it is compromised. It is +recommended to enable the filter only when it is known to work on the version of the system where `chrony` is installed as the filter needs to allow also system calls made from libraries that `chronyd` is using (e.g. libc) and different -versions or implementations of the libraries may make different system calls. +versions or implementations of the libraries might make different system calls. If the filter is missing some system call, `chronyd` could be killed even in normal operation. +=== How can I make the system clock more secure? + +An NTP client synchronising the system clock to an NTP server is susceptible to +various attacks, which can break applications and network protocols relying on +accuracy of the clock (e.g. DNSSEC, Kerberos, TLS, WireGuard). + +Generally, a man-in-the-middle (MITM) attacker between the client and server +can + +* make fake responses, or modify real responses from the server, to create an + arbitrarily large time and frequency offset, make the server appear more + accurate, insert a leap second, etc. +* delay the requests and/or responses to create a limited time offset and + temporarily also a limited frequency offset +* drop the requests or responses to prevent updates of the clock with new + measurements +* redirect the requests to a different server + +The attacks can be combined for a greater effect. The attacker can delay +packets to create a significant frequency offset first and then drop all +subsequent packets to let the clock quickly drift away from the true time. +The attacker might also be able to control the server's clock. + +Some attacks cannot be prevented. Monitoring is needed for detection, e.g. the +reachability register in the `sources` report shows missing packets. The extent +to which the attacker can control the client's clock depends on its +configuration. + +Enable authentication to prevent `chronyd` from accepting modified, fake, or +redirected packets. It can be enabled with a symmetric key specified by the +`key` option, or Network Time Security (NTS) by the `nts` option (supported +since `chrony` version 4.0). The server needs to support the selected +authentication mechanism. Symmetric keys have to be configured on both client +and server, and each client must have its own key (one per server). + +The maximum offset that the attacker can insert in an NTP measurement by +delaying packets can be limited by the `maxdelay` option. The default value is +3 seconds. The measured delay is reported as the peer delay in the `ntpdata` +report and `measurements` log. Set the `maxdelay` option to a value larger than +the maximum value that is normally observed. Note that the delay can increase +significantly even when not under an attack, e.g. when the network is congested +or the routing has changed. + +The maximum accepted change in time offset between clock updates can be limited +by the `maxchange` directive. Larger changes in the offset will be ignored or +cause `chronyd` to exit. Note that the attacker can get around this limit by +splitting the offset into multiple smaller offsets and/or creating a large +frequency offset. When this directive is used, `chronyd` will have to be +restarted after a successful attack. It will not be able to recover on its own. +It must not be restarted automatically (e.g. by the service manager). + +The impact of a large accepted time offset can be reduced by disabling clock +steps, i.e. by not using the `makestep` and `initstepslew` directives. The +offset will be slowly corrected by speeding up or slowing down the clock at a +rate which can be limited by the `maxslewrate` directive. Disabling clock steps +completely is practical only if the clock cannot gain a larger error on its +own, e.g. when the computer is shut down or suspended, and the `maxslewrate` +limit is large enough to correct an expected error in an acceptable time. The +`rtcfile` directive with the `-s` option can be used to compensate for the RTC +drift. + +A more practical approach is to enable `makestep` for a limited number of clock +updates (the 2nd argument of the directive) and limit the offset change in all +updates by the `maxchange` directive. The attacker will be able to make only a +limited step and only if the attack starts in a short window after booting the +computer, or when `chronyd` is restarted without the `-R` option. + +The frequency offset can be limited by the `maxdrift` directive. The measured +frequency offset is reported in the drift file, `tracking` report, and +`tracking` log. Set `maxdrift` to a value larger than the maximum absolute +value that is normally observed. Note that the frequency of the clock can +change due to aging of the crystal, differences in calibration of the clock +source between reboots, migrated virtual machine, etc. A typical computer clock +has a drift smaller than 100 parts per million (ppm), but much larger drifts +are possible (e.g. in some virtual machines). + +Use only trusted servers, which you expect to be well configured and managed, +using authentication for their own servers, etc. Use multiple servers, ideally +in different locations. The attacker will have to deal with a majority of the +servers in order to pass the source selection and update the clock with a large +offset. Use the `minsources` directive to increase the required number of +selectable sources to make the selection more robust. + +Do not specify servers as peers. The symmetric mode is less secure than the +client/server mode. If not authenticated, it is vulnerable to off-path +denial-of-service attacks, and even when it is authenticated, it is still +susceptible to replay attacks. + +Mixing of authenticated and unauthenticated servers should generally be +avoided. If mixing is necessary (e.g. for a more accurate and stable +synchronisation to a closer server which does not support authentication), the +authenticated servers should be configured as trusted and required to not allow +the unauthenticated servers to override the authenticated servers in the source +selection. Since `chrony` version 4.0, the selection options are enabled in +such a case automatically. This behaviour can be disabled or modified by the +`authselmode` directive. + +An example of a client configuration limiting the impact of the attacks could +be + +---- +server foo.example.net iburst nts maxdelay 0.1 +server bar.example.net iburst nts maxdelay 0.2 +server baz.example.net iburst nts maxdelay 0.05 +server qux.example.net iburst nts maxdelay 0.1 +server quux.example.net iburst nts maxdelay 0.1 +minsources 3 +maxchange 100 0 0 +makestep 0.001 1 +maxdrift 100 +maxslewrate 100 +driftfile /var/lib/chrony/drift +ntsdumpdir /var/lib/chrony +rtcsync +---- + === How can I improve the accuracy of the system clock with NTP sources? Select NTP servers that are well synchronised, stable and close to your -network. It's better to use more than one server, three or four is usually +network. It is better to use more than one server. Three or four is usually recommended as the minimum, so `chronyd` can detect servers that serve false time and combine measurements from multiple sources. If you have a network card with hardware timestamping supported on Linux, it -can be enabled by the *hwtimestamp* directive in the _chrony.conf_ file. It -should make local receive and transmit timestamps of NTP packets much more -accurate. - -There are also useful options which can be set in the `server` directive, they -are `minpoll`, `maxpoll`, `polltarget`, `maxdelay`, `maxdelayratio`, -`maxdelaydevratio`, and `xleave`. +can be enabled by the `hwtimestamp` directive. It should make local receive and +transmit timestamps of NTP packets much more stable and accurate. + +The `server` directive has some useful options: `minpoll`, `maxpoll`, +`polltarget`, `maxdelay`, `maxdelayratio`, `maxdelaydevratio`, `xleave`, +`filter`. The first three options set the minimum and maximum allowed polling interval, and how should be the actual interval adjusted in the specified range. Their @@ -163,8 +274,8 @@ `maxpoll` and 8 (samples) for `polltarget`. The default values should be used for general servers on the Internet. With your own NTP servers, or if you have permission to poll some servers more frequently, setting these options for -shorter polling intervals may significantly improve the accuracy of the system -clock. +shorter polling intervals might significantly improve the accuracy of the +system clock. The optimal polling interval depends mainly on two factors, stability of the network latency and stability of the system clock (which mainly depends on the @@ -174,7 +285,7 @@ Generally, if the `sourcestats` command usually reports a small number of samples retained for a source (e.g. fewer than 16), a shorter polling interval should be considered. If the number of samples is usually at the maximum of 64, -a longer polling interval may work better. +a longer polling interval might work better. An example of the directive for an NTP server on the Internet that you are allowed to poll frequently could be @@ -190,7 +301,7 @@ server ntp.local minpoll 2 maxpoll 4 polltarget 30 ---- -The maxdelay options are useful to ignore measurements with an unusally large +The maxdelay options are useful to ignore measurements with an unusually large delay (e.g. due to congestion in the network) and improve the stability of the synchronisation. The `maxdelaydevratio` option could be added to the example with local NTP server @@ -200,8 +311,8 @@ ---- If your server supports the interleaved mode (e.g. it is running `chronyd`), -the `xleave` option should be added to the `server` directive in order to allow -the server to send the client more accurate transmit timestamps (kernel or +the `xleave` option should be added to the `server` directive to enable the +server to provide the client with more accurate transmit timestamps (kernel or preferably hardware). For example: ---- @@ -210,7 +321,7 @@ When combined with local hardware timestamping, good network switches, and even shorter polling intervals, a sub-microsecond accuracy and stability of a few -tens of nanoseconds may be possible. For example: +tens of nanoseconds might be possible. For example: ---- server ntp.local minpoll 0 maxpoll 0 xleave @@ -221,10 +332,11 @@ disabled power saving and performance boosting). Energy-Efficient Ethernet (EEE) should be disabled in the network. The switches should be configured to prioritize NTP packets, especially if the network is expected to be heavily -loaded. +loaded. The `dscp` directive can be used to set the Differentiated Services +Code Point in transmitted NTP packets if needed. -If it is acceptable for NTP clients in the network to send requests at an -excessive rate, a sub-second polling interval may be specified. A median filter +If it is acceptable for NTP clients in the network to send requests at a high +rate, a sub-second polling interval can be specified. A median filter can be enabled in order to update the clock at a reduced rate with more stable measurements. For example: @@ -237,17 +349,40 @@ Yes. With the `-q` option `chronyd` will set the system clock once and exit. With the `-Q` option it will print the measured offset without setting the -clock. If you don't want to use a configuration file, NTP servers can be +clock. If you do not want to use a configuration file, NTP servers can be specified on the command line. For example: ---- # chronyd -q 'pool pool.ntp.org iburst' ---- +The command above would normally take about 5 seconds if the servers were +well synchronised and responding to all requests. If not synchronised or +responding, it would take about 10 seconds for `chronyd` to give up and exit +with a non-zero status. A faster configuration is possible. A single server can +be used instead of four servers, the number of measurements can be reduced with +the `maxsamples` option to one (supported since `chrony` version 4.0), and a +timeout can be specified with the `-t` option. The following command would take +only up to about one second. + +---- +# chronyd -q -t 1 'server pool.ntp.org iburst maxsamples 1' +---- + +It is not recommended to run `chronyd` with the `-q` option periodically (e.g. +from a cron job) as a replacement for the daemon mode, because it performs +significantly worse (e.g. the clock is stepped and its frequency is not +corrected). If you must run it this way and you are using a public NTP server, +make sure `chronyd` does not always start around the first second of a minute, +e.g. by adding a random sleep before the `chronyd` command. Public servers +typically receive large bursts of requests around the first second as there is +a large number of NTP clients started from cron with no delay. + === Can `chronyd` be configured to control the clock like `ntpd`? It is not possible to perfectly emulate `ntpd`, but there are some options that -can configure `chronyd` to behave more like `ntpd`. +can configure `chronyd` to behave more like `ntpd` if there is a reason to +prefer that. In the following example the `minsamples` directive slows down the response to changes in the frequency and offset of the clock. The `maxslewrate` and @@ -267,10 +402,87 @@ maxclockerror 15 ---- -Note that increasing `minsamples` may cause the offsets in the `tracking` and +Note that increasing `minsamples` might cause the offsets in the `tracking` and `sourcestats` reports/logs to be significantly smaller than the actual offsets and be unsuitable for monitoring. +=== Can NTP server be separated from NTP client? + +Yes, it is possible to run multiple instances of `chronyd` on a computer at the +same time. One can operate primarily as an NTP client to synchronise the system +clock and another as a server for other computers. If they use the same +filesystem, they need to be configured with different pidfiles, Unix domain +command sockets, and any other file or directory specified in the configuration +file. If they run in the same network namespace, they need to use different NTP +and command ports, or bind the ports to different addresses or interfaces. + +The server instance should be started with the `-x` option to prevent it from +adjusting the system clock and interfering with the client instance. It can be +configured as a client to synchronise its NTP clock to other servers, or the +client instance running on the same computer. In the latter case, the `copy` +option (added in `chrony` version 4.1) can be used to assume the reference ID +and stratum of the client instance, which enables detection of synchronisation +loops with its own clients. + +On Linux, starting with `chrony` version 4.0, it is possible to run multiple +server instances sharing a port to better utilise multiple cores of the CPU. +Note that for rate limiting and client/server interleaved mode to work well +it is necessary that all packets received from the same address are handled by +the same server instance. + +An example configuration of the client instance could be + +---- +pool pool.ntp.org iburst +allow 127.0.0.1 +port 11123 +driftfile /var/lib/chrony/drift +makestep 1 3 +rtcsync +---- + +and configuration of the first server instance could be + +---- +server 127.0.0.1 port 11123 minpoll 0 maxpoll 0 copy +allow +cmdport 11323 +bindcmdaddress /var/run/chrony/chronyd-server1.sock +pidfile /var/run/chronyd-server1.pid +driftfile /var/lib/chrony/drift-server1 +---- + +=== Should be a leap smear enabled on NTP server? + +With the `smoothtime` and `leapsecmode` directives it is possible to enable a +server leap smear in order to hide leap seconds from clients and force them to +follow a slow server's adjustment instead. + +This feature should be used only in local networks and only when necessary, +e.g. when the clients cannot be configured to handle the leap seconds as +needed, or their number is so large that configuring them all would be +impractical. The clients should use only one leap-smearing server, or multiple +identically configured leap-smearing servers. Note that some clients can get +leap seconds from other sources (e.g. with the `leapsectz` directive in +`chrony`) and they will not work correctly with a leap smearing server. + +=== Does `chrony` support PTP? + +No, the Precision Time Protocol (PTP) is not supported and there are no plans +to support it. It is a complex protocol, which shares some issues with the +NTP broadcast mode. One of the main differences between NTP and PTP is that PTP +was designed to be easily supported in hardware (e.g. network switches and +routers) in order to make more stable and accurate measurements. PTP relies on +the hardware support. NTP does not rely on any support in the hardware, but if +it had the same support as PTP, it could perform equally well. + +On Linux, `chrony` supports hardware clocks that some NICs have for PTP. They +are called PTP hardware clocks (PHC). They can be used as reference clocks +(specified by the `refclock` directive) and for hardware timestamping of NTP +packets (enabled by the `hwtimestamp` directive) if the NIC can timestamp other +packets than PTP, which is usually the case at least for transmitted packets. +The `ethtool -T` command can be used to verify the timestamping support. + === What happened to the `commandkey` and `generatecommandkey` directives? They were removed in version 2.2. Authentication is no longer supported in the @@ -287,18 +499,17 @@ === Behind a firewall? -Check the `Reach` value printed by the ``chronyc``'s `sources` command. If it's -zero, it means `chronyd` did not get any valid responses from the NTP server +Check the `Reach` value printed by the ``chronyc``'s `sources` command. If it +is zero, it means `chronyd` did not get any valid responses from the NTP server you are trying to use. If there is a firewall between you and the server, the -packets may be blocked. Try using a tool like `wireshark` or `tcpdump` to see -if you're getting any responses from the server. +packets might be blocked. Try using a tool like `wireshark` or `tcpdump` to see +if you are getting any responses from the server. When `chronyd` is receiving responses from the servers, the output of the `sources` command issued few minutes after `chronyd` start might look like this: ---- -210 Number of sources = 3 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== ^* foo.example.net 2 6 377 34 +484us[ -157us] +/- 30ms @@ -308,9 +519,10 @@ === Are NTP servers specified with the `offline` option? -Check that you're using ``chronyc``'s `online` and `offline` commands -appropriately. The `activity` command prints the number of sources that are -currently online and offline. For example: +Check that the ``chronyc``'s `online` and `offline` commands are used +appropriately (e.g. in the system networking scripts). The `activity` command +prints the number of sources that are currently online and offline. For +example: ---- 200 OK @@ -321,6 +533,19 @@ 0 sources with unknown address ---- +=== Is name resolution working correctly? + +NTP servers specified by their hostname (instead of an IP address) have to have +their names resolved before `chronyd` can send any requests to them. If the +`activity` command prints a non-zero number of sources with unknown address, +there is an issue with the resolution. Typically, a DNS server is specified in +_/etc/resolv.conf_. Make sure it is working correctly. + +Since `chrony` version 4.0, you can run `chronyc -N sources -a` command to +print all sources, even those that do not have a known address yet, with their +names as they were specified in the configuration. This can be useful to verify +that the names specified in the configuration are used as expected. + === Is `chronyd` allowed to step the system clock? By default, `chronyd` adjusts the clock gradually by slowing it down or @@ -337,9 +562,9 @@ ---- the clock would be stepped in the first three updates if its offset was larger -than one second. Normally, it's recommended to allow the step only in the first +than one second. Normally, it is recommended to allow the step only in the first few updates, but in some cases (e.g. a computer without an RTC or virtual -machine which can be suspended and resumed with an incorrect time) it may be +machine which can be suspended and resumed with an incorrect time) it might be necessary to allow the step on any clock update. The example above would change to @@ -347,11 +572,55 @@ makestep 1 -1 ---- +=== Using NTS? + +The Network Time Security (NTS) mechanism uses Transport Layer Security (TLS) +to establish the keys needed for authentication of NTP packets. + +Run the `authdata` command to check whether the key establishment was +successful: + +---- +# chronyc -N authdata +Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen +========================================================================= +foo.example.net NTS 1 15 256 33m 0 0 8 100 +bar.example.net NTS 1 15 256 33m 0 0 8 100 +baz.example.net NTS 1 15 256 33m 0 0 8 100 +---- + +The KeyID, Type, and KLen columns should have non-zero values. If they are +zero, check the system log for error messages from `chronyd`. One possible +cause of failure is a firewall blocking the client's connection to the server's +TCP port 4460. + +Another possible cause of failure is a certificate that is failing to verify +because the client's clock is wrong. This is a chicken-and-egg problem with NTS. +You might need to manually correct the date, or temporarily disable NTS, in +order to get NTS working. If your computer has an RTC and it is backed up by a +good battery, this operation should be needed only once, assuming the RTC will +be set periodically with the `rtcsync` directive, or compensated with the +`rtcfile` directive and the `-s` option. + +If the computer does not have an RTC or battery, you can use the `-s` option +without `rtcfile` directive to restore time of the last shutdown or reboot from +the drift file. The clock will start behind the true time, but if the computer +was not shut down for too long and the server's certificate was not renewed too +close to its expiration, it should be sufficient for the time checks to +succeed. + +As a last resort, you can disable the time checks by the `nocerttimecheck` +directive. This has some important security implications. To reduce the +security risk, you can use the `nosystemcert` and `ntstrustedcerts` directives +to disable the system's default trusted certificate authorities and trust only +a minimal set of selected authorities needed to validate the certificates of +used NTP servers. + === Using a Windows NTP server? A common issue with Windows NTP servers is that they report a very large root dispersion (e.g. three seconds or more), which causes `chronyd` to ignore the -server for being too inaccurate. The `sources` command may show a valid +server for being too inaccurate. The `sources` command might show a valid measurement, but the server is not selected for synchronisation. You can check the root dispersion of the server with the ``chronyc``'s `ntpdata` command. @@ -362,6 +631,52 @@ maxdistance 16.0 ---- +=== An unreachable source is selected? + +When `chronyd` is configured with multiple time sources, it tries to select the +most accurate and stable sources for synchronisation of the system clock. They +are marked with the _*_ or _+_ symbol in the report printed by the `sources` +command. + +When the best source (marked with the _*_ symbol) becomes unreachable (e.g. NTP +server stops responding), `chronyd` will not immediately switch +to the second best source in an attempt to minimise the error of the clock. It +will let the clock run free for as long as its estimated error (in terms of +root distance) based on previous measurements is smaller than the estimated +error of the second source, and there is still an interval which contains some +measurements from both sources. + +If the first source was significantly better than the second source, it can +take many hours before the second source is selected, depending on its polling +interval. You can force a faster reselection by increasing the clock error rate +(`maxclockerror` directive), shortening the polling interval (`maxpoll` +option), or reducing the number of samples (`maxsamples` option). + +=== Does selected source drop new measurements? + +`chronyd` can drop a large number of successive NTP measurements if they are +not passing some of the NTP tests. The `sources` command can report for a +selected source the fully-reachable value of 377 in the Reach column and at the +same time a LastRx value that is much larger than the current polling interval. +If the source is online, this indicates that a number of measurements was +dropped. You can use the `ntpdata` command to check the NTP tests for the last +measurement. Usually, it is the test C which fails. + +This can be an issue when there is a long-lasting increase in the measured +delay, e.g. due to a routing change in the network. Unfortunately, `chronyd` +does not know for how long it should wait for the delay to come back to the +original values, or whether it is a permanent increase and it should start from +scratch. + +The test C is an adaptive filter. It can take many hours before it accepts +a measurement with the larger delay, and even much longer before it drops all +measurements with smaller delay, which determine an expected delay used by the +test. You can use the `reset sources` command to drop all measurements +immediately (available in chrony 4.0 and later). If this issue happens +frequently, you can effectively disable the test by setting the +`maxdelaydevratio` option to a very large value (e.g. 1000000), or speed up the +recovery by increasing the clock error rate with the `maxclockerror` directive. + === Using a PPS reference clock? A pulse-per-second (PPS) reference clock requires a non-PPS time source to @@ -397,31 +712,33 @@ When accessing `chronyd` remotely, make sure that the _chrony.conf_ file (on the computer where `chronyd` is running) has a `cmdallow` entry for the computer you are running `chronyc` on and an appropriate `bindcmdaddress` -directive. This isn't necessary for localhost. +directive. This is not necessary for localhost. Perhaps `chronyd` is not running. Try using the `ps` command (e.g. on Linux, -`ps -auxw`) to see if it's running. Or try `netstat -a` and see if the ports -123/udp and 323/udp are listening. If `chronyd` is not running, you may have a -problem with the way you are trying to start it (e.g. at boot time). +`ps -auxw`) to see if it is running. Or try `netstat -a` and see if the UDP +port 323 is listening. If `chronyd` is not running, you might have a problem +with the way you are trying to start it (e.g. at boot time). -Perhaps you have a firewall set up in a way that blocks packets on port -323/udp. You need to amend the firewall configuration in this case. +Perhaps you have a firewall set up in a way that blocks packets on the UDP +port 323. You need to amend the firewall configuration in this case. === I keep getting the error `501 Not authorised` -Since version 2.2, the `password` command doesn't do anything and `chronyc` -needs to run locally under the root or _chrony_ user, which are allowed to -access the ``chronyd``'s Unix domain command socket. - -With older versions, you need to authenticate with the `password` command first -or use the `-a` option to authenticate automatically on start. The -configuration file needs to specify a file which contains keys (`keyfile` -directive) and which key in the key file should be used for `chronyc` -authentication (`commandkey` directive). +This error indicates that `chronyc` sent the command to `chronyd` using a UDP +socket instead of the Unix domain socket (e.g. _/var/run/chrony/chronyd.sock_), +which is required for some commands. For security reasons, only the root and +_chrony_ users are allowed to access the socket. -=== Why does `chronyc tracking` always print an IPv4 address as reference ID? +It is also possible that the socket does not exist. `chronyd` will not create +the socket if the directory has a wrong owner or permissions. In this case +there should be an error message from `chronyd` in the system log. -The reference ID is a 32-bit value and in versions before 3.0 it was printed in +=== What is the reference ID reported by the `tracking` command? + +The reference ID is a 32-bit value used in NTP to prevent synchronisation +loops. + +In `chrony` versions before 3.0 it was printed in the quad-dotted notation, even if the reference source did not actually have an IPv4 address. For IPv4 addresses, the reference ID is equal to the address, but for IPv6 addresses it is the first 32 bits of the MD5 sum of the address. For @@ -445,12 +762,12 @@ === What is the real-time clock (RTC)? This is the clock which keeps the time even when your computer is turned off. -It is used to initialise the system clock on boot. It normally doesn't drift +It is used to initialise the system clock on boot. It normally does not drift more than few seconds per day. There are two approaches how `chronyd` can work with it. One is to use the `rtcsync` directive, which tells `chronyd` to enable a kernel mode which sets -the RTC from the system clock every 11 minutes. `chronyd` itself won't touch +the RTC from the system clock every 11 minutes. `chronyd` itself will not touch the RTC. If the computer is not turned off for a long time, the RTC should still be close to the true time when the system clock will be initialised from it on the next boot. @@ -460,17 +777,17 @@ started with the `-s` option on the next boot, it will set the system time from the RTC and also compensate for the drift it has measured previously. The `rtcautotrim` directive can be used to keep the RTC close to the true time, but -it's not strictly necessary if its only purpose is to set the system clock when +it is not strictly necessary if its only purpose is to set the system clock when `chronyd` is started on boot. See the documentation for details. -=== I want to use ``chronyd``'s RTC support. Must I disable `hwclock`? +=== Does `hwclock` have to be disabled? -The `hwclock` program is often set-up by default in the boot and shutdown -scripts with many Linux installations. With the kernel RTC synchronisation +The `hwclock` program is run by default in the boot and/or shutdown +scripts in some Linux installations. With the kernel RTC synchronisation (`rtcsync` directive), the RTC will be set also every 11 minutes as long as the system clock is synchronised. If you want to use ``chronyd``'s RTC monitoring -(`rtcfile` directive), it's important to disable `hwclock` in the shutdown -procedure. If you don't, it will over-write the RTC with a new value, unknown +(`rtcfile` directive), it is important to disable `hwclock` in the shutdown +procedure. If you do not do that, it will overwrite the RTC with a new value, unknown to `chronyd`. At the next reboot, `chronyd` started with the `-s` option will compensate this (wrong) time with its estimate of how far the RTC has drifted whilst the power was off, giving a meaningless initial system time. @@ -489,7 +806,20 @@ === I get `Could not open /dev/rtc, Device or resource busy` in my syslog file -Some other program running on the system may be using the device. +Some other program running on the system might be using the device. + +=== When I start `chronyd`, the log says `Could not enable RTC interrupt : Invalid argument` (or it may say `disable`) + +Your real-time clock hardware might not support the required ioctl requests: + +* `RTC_UIE_ON` +* `RTC_UIE_OFF` + +A possible solution could be to build the Linux kernel with support for software +emulation instead; try enabling the following configuration option when building +the Linux kernel: + +* `CONFIG_RTC_INTF_DEV_UIE_EMUL` === What if my computer does not have an RTC or backup battery? @@ -504,7 +834,7 @@ === Can `chronyd` be driven from broadcast/multicast NTP servers? No, the broadcast/multicast client mode is not supported and there is currently -no plan to implement it. While the mode may be useful to simplify configuration +no plan to implement it. While this mode can simplify configuration of clients in large networks, it is inherently less accurate and less secure (even with authentication) than the ordinary client/server mode. @@ -521,7 +851,8 @@ Yes, the `broadcast` directive can be used to enable the broadcast server mode to serve time to clients in the network which support the broadcast client mode -(it's not supported in `chronyd`, see the previous question). +(it is not supported in `chronyd`). Note that this mode should generally be +avoided. See the previous question. === Can `chronyd` keep the system clock a fixed offset away from real time? @@ -537,9 +868,21 @@ `online` command. Unless the network connection lasts only few minutes (less than the maximum -polling interval), the delay is usually not a problem, and it may be acceptable +polling interval), the delay is usually not a problem, and it might be acceptable to keep all sources online all the time. +=== Why is an offset measured between two computers synchronised to each another? + +When two computers are synchronised to each other using the client/server or +symmetric NTP mode, there is an expectation that NTP measurements between the +two computers made on both ends show an average offset close to zero. + +With `chronyd` that can be expected only when the interleaved mode is enabled +by the `xleave` option. Otherwise, `chronyd` will use different transmit +timestamps (e.g. daemon timestamp vs kernel timestamp) for serving time and +synchronisation of its own clock, which will cause the other computer to +measure a significant offset. + == Operating systems === Does `chrony` support Windows? diff -Nru chrony-3.5/doc/installation.adoc chrony-4.1/doc/installation.adoc --- chrony-3.5/doc/installation.adoc 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/doc/installation.adoc 2021-05-12 11:06:15.000000000 +0000 @@ -22,18 +22,25 @@ code is supplied in the form of a gzipped tar file, which unpacks to a subdirectory identifying the name and version of the program. -The following programs and libraries with their development files are needed to -build `chrony`: +A C compiler (e.g. `gcc` or `clang`) and GNU Make are needed to build `chrony`. +The following libraries with their development files, and programs, are needed +to enable optional features: + +* pkg-config: detection of development libraries +* Nettle, NSS, or LibTomCrypt: secure hash functions (`SECHASH`) +* libcap: dropping root privileges on Linux (`DROPROOT`) +* libseccomp: system call filter on Linux (`SCFILTER`) +* GnuTLS and Nettle: Network Time Security (`NTS`) +* Editline: line editing in `chronyc` (`READLINE`) +* timepps.h header: PPS reference clock +* Asciidoctor: documentation in HTML format +* Bash: test suite -* C compiler (gcc or clang recommended) -* GNU Make -* Nettle, NSS, or LibTomCrypt (optional) -* Editline (optional) -* libcap (Linux only, optional) -* libseccomp (Linux only, optional) -* timepps.h header (optional) -* Asciidoctor (for HTML documentation) -* Bash (for testing) +The following programs are needed when building `chrony` from the git +repository instead of a released tar file: + +* Asciidoctor: manual pages +* Bison: parser for chronyc settime command After unpacking the source code, change directory into it, and type @@ -87,13 +94,13 @@ If development files for the https://www.lysator.liu.se/~nisse/nettle/[Nettle], https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS[NSS], or -http://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available, +https://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available, `chronyd` will be built with support for other cryptographic hash functions than MD5, which can be used for NTP authentication with a symmetric key. If you don't want to enable the support, specify the `--disable-sechash` flag to `configure`. -If development files for the editline or readline library are available, +If development files for the editline library are available, `chronyc` will be built with line editing support. If you don't want this, specify the `--disable-readline` flag to `configure`. @@ -163,43 +170,6 @@ the kernel attack surface and possibly prevent kernel exploits from `chronyd` if it is compromised. -== Support for line editing libraries - -`chronyc` can be built with support for line editing, this allows you to use -the cursor keys to replay and edit old commands. Two libraries are supported -which provide such functionality, editline and GNU readline. - -Please note that readline since version 6.0 is licensed under GPLv3+ which is -incompatible with chrony's license GPLv2. You should use editline instead if -you don't want to use older readline versions. - -The `configure` script will automatically enable the line editing support if -one of the supported libraries is available. If they are both available, the -editline library will be used. - -If you don't want to use it (in which case `chronyc` will use a minimal command -line interface), invoke `configure` like this: - ----- -./configure --disable-readline other-options... ----- - -If you have editline, readline or ncurses installed in locations that aren't -normally searched by the compiler and linker, you need to use extra options: - -`--with-readline-includes=directory_name`:: - This defines the name of the directory above the one where `readline.h` is. - `readline.h` is assumed to be in `editline` or `readline` subdirectory of the - named directory. - -`--with-readline-library=directory_name`:: - This defines the directory containing the `libedit.a` or `libedit.so` file, - or `libreadline.a` or `libreadline.so` file. - -`--with-ncurses-library=directory_name`:: - This defines the directory containing the `libncurses.a` or `libncurses.so` - file. - == Extra options for package builders The `configure` and `make` procedures have some extra options that may be diff -Nru chrony-3.5/examples/chrony.conf.example2 chrony-4.1/examples/chrony.conf.example2 --- chrony-3.5/examples/chrony.conf.example2 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/examples/chrony.conf.example2 2021-05-12 11:06:15.000000000 +0000 @@ -1,5 +1,5 @@ # 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 # Record the rate at which the system clock gains/losses time. @@ -25,9 +25,18 @@ # Serve time even if not synchronized to a time source. #local stratum 10 +# Require authentication (nts or key option) for all NTP sources. +#authselectmode require + # Specify file containing keys for NTP authentication. #keyfile /etc/chrony.keys +# Save NTS keys and cookies. +ntsdumpdir /var/lib/chrony + +# Insert/delete leap seconds by slewing instead of stepping. +#leapsecmode slew + # Get TAI-UTC offset and leap seconds from the system tz database. #leapsectz right/UTC diff -Nru chrony-3.5/examples/chrony.conf.example3 chrony-4.1/examples/chrony.conf.example3 --- chrony-3.5/examples/chrony.conf.example3 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/examples/chrony.conf.example3 2021-05-12 11:06:15.000000000 +0000 @@ -57,6 +57,20 @@ ! maxdrift 100 +# By default, chronyd allows synchronisation to an unauthenticated NTP +# source (i.e. specified without the nts and key options) if it agrees with +# a majority of authenticated NTP sources, or if no authenticated source is +# specified. If you don't want chronyd to ever synchronise to an +# unauthenticated NTP source, uncomment the first from the following lines. +# If you don't want to synchronise to an unauthenticated NTP source only +# when an authenticated source is specified, uncomment the second line. +# If you want chronyd to ignore authentication in the source selection, +# uncomment the third line. + +! authselectmode require +! authselectmode prefer +! authselectmode ignore + ####################################################################### ### FILENAMES ETC # Chrony likes to keep information about your computer's clock in files. @@ -72,22 +86,37 @@ ! keyfile /etc/chrony.keys +# If you specify an NTP server with the nts option to enable authentication +# with the Network Time Security (NTS) mechanism, or enable server NTS with +# the ntsservercert and ntsserverkey directives below, the following line will +# allow the client/server to save the NTS keys and cookies in order to reduce +# the number of key establishments (NTS-KE sessions). + +ntsdumpdir /var/lib/chrony + +# If chronyd is configured to act as an NTP server and you want to enable NTS +# for its clients, you will need a TLS certificate and private key. Uncomment +# and edit the following lines to specify the locations of the certificate and +# key. + +! ntsservercert /etc/.../foo.example.net.crt +! ntsserverkey /etc/.../foo.example.net.key + # chronyd can save the measurement history for the servers to files when -# it it exits. This is useful in 2 situations: +# it exits. This is useful in 2 situations: # -# 1. On Linux, if you stop chronyd and restart it with '-r' (e.g. after +# 1. If you stop chronyd and restart it with the '-r' option (e.g. after # an upgrade), the old measurements will still be relevant when chronyd # is restarted. This will reduce the time needed to get accurate -# gain/loss measurements, especially with a dial-up link. +# gain/loss measurements. # -# 2. Again on Linux, if you use the RTC support and start chronyd with +# 2. On Linux, if you use the RTC support and start chronyd with # '-r -s' on bootup, measurements from the last boot will still be # useful (the real time clock is used to 'flywheel' chronyd between # boots). # -# Enable these two options to use this. +# Uncomment the following line to use this. -! dumponexit ! dumpdir /var/lib/chrony # chronyd writes its process ID to a file. If you try to start a second @@ -117,6 +146,18 @@ ! makestep 1.0 3 ####################################################################### +### LEAP SECONDS +# A leap second is an occasional one-second correction of the UTC +# time scale. By default, chronyd tells the kernel to insert/delete +# the leap second, which makes a backward/forward step to correct the +# clock for it. As with the makestep directive, this jump can upset +# some applications. If you prefer chronyd to make a gradual +# correction, causing the clock to be off for a longer time, uncomment +# the following line. + +! leapsecmode slew + +####################################################################### ### LOGGING # If you want to log information about the time measurements chronyd has # gathered, you might want to enable the following lines. You probably @@ -135,8 +176,6 @@ ####################################################################### ### ACTING AS AN NTP SERVER # You might want the computer to be an NTP server for other computers. -# e.g. you might be running chronyd on a dial-up machine that has a LAN -# sitting behind it with several 'satellite' computers on it. # # By default, chronyd does not allow any clients to access it. You need # to explicitly enable access using 'allow' and 'deny' directives. @@ -152,15 +191,6 @@ # You can have as many allow and deny directives as you need. The order # is unimportant. -# If you want chronyd to act as an NTP broadcast server, enable and edit -# (and maybe copy) the following line. This means that a broadcast -# packet is sent to the address 192.168.1.255 every 60 seconds. The -# address MUST correspond to the broadcast address of one of the network -# interfaces on your machine. If you have multiple network interfaces, -# add a broadcast line for each. - -! broadcast 60 192.168.1.255 - # If you want to present your computer's time for others to synchronise # with, even if you don't seem to be synchronised to any NTP servers # yourself, enable the following line. The value 10 may be varied diff -Nru chrony-3.5/examples/chrony.nm-dispatcher chrony-4.1/examples/chrony.nm-dispatcher --- chrony-3.5/examples/chrony.nm-dispatcher 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/examples/chrony.nm-dispatcher 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -#!/bin/sh -# This is a NetworkManager dispatcher / networkd-dispatcher script for -# chronyd to set its NTP sources online or offline when a network interface -# is configured or removed - -export LC_ALL=C - -# For NetworkManager consider only up/down events -[ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0 - -# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off - -chronyc onoffline > /dev/null 2>&1 - -exit 0 diff -Nru chrony-3.5/examples/chrony.nm-dispatcher.dhcp chrony-4.1/examples/chrony.nm-dispatcher.dhcp --- chrony-3.5/examples/chrony.nm-dispatcher.dhcp 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/examples/chrony.nm-dispatcher.dhcp 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,43 @@ +#!/bin/sh +# This is a NetworkManager dispatcher script for chronyd to update +# its NTP sources passed from DHCP options. Note that this script is +# specific to NetworkManager-dispatcher due to use of the +# DHCP4_NTP_SERVERS environment variable. + +export LC_ALL=C + +interface=$1 +action=$2 + +chronyc=/usr/bin/chronyc +default_server_options=iburst +server_dir=/var/run/chrony-dhcp + +dhcp_server_file=$server_dir/$interface.sources +# DHCP4_NTP_SERVERS is passed from DHCP options by NetworkManager. +nm_dhcp_servers=$DHCP4_NTP_SERVERS + +add_servers_from_dhcp() { + rm -f "$dhcp_server_file" + for server in $nm_dhcp_servers; do + echo "server $server $default_server_options" >> "$dhcp_server_file" + done + $chronyc reload sources > /dev/null 2>&1 || : +} + +clear_servers_from_dhcp() { + if [ -f "$dhcp_server_file" ]; then + rm -f "$dhcp_server_file" + $chronyc reload sources > /dev/null 2>&1 || : + fi +} + +mkdir -p $server_dir + +if [ "$action" = "up" ] || [ "$action" = "dhcp4-change" ]; then + add_servers_from_dhcp +elif [ "$action" = "down" ]; then + clear_servers_from_dhcp +fi + +exit 0 diff -Nru chrony-3.5/examples/chrony.nm-dispatcher.onoffline chrony-4.1/examples/chrony.nm-dispatcher.onoffline --- chrony-3.5/examples/chrony.nm-dispatcher.onoffline 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/examples/chrony.nm-dispatcher.onoffline 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,17 @@ +#!/bin/sh +# This is a NetworkManager dispatcher / networkd-dispatcher script for +# chronyd to set its NTP sources online or offline when a network interface +# is configured or removed + +export LC_ALL=C + +chronyc=/usr/bin/chronyc + +# For NetworkManager consider only up/down events +[ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0 + +# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off + +$chronyc onoffline > /dev/null 2>&1 + +exit 0 diff -Nru chrony-3.5/examples/chrony-wait.service chrony-4.1/examples/chrony-wait.service --- chrony-3.5/examples/chrony-wait.service 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/examples/chrony-wait.service 2021-05-12 11:06:15.000000000 +0000 @@ -8,9 +8,11 @@ [Service] Type=oneshot -# Wait up to ~10 minutes for chronyd to synchronize and the remaining -# clock correction to be less than 0.1 seconds -ExecStart=/usr/bin/chronyc -h 127.0.0.1,::1 waitsync 600 0.1 0.0 1 +# Wait for chronyd to update the clock and the remaining +# correction to be less than 0.1 seconds +ExecStart=/usr/bin/chronyc -h 127.0.0.1,::1 waitsync 0 0.1 0.0 1 +# Wait for at most 3 minutes +TimeoutStartSec=180 RemainAfterExit=yes StandardOutput=null diff -Nru chrony-3.5/FAQ chrony-4.1/FAQ --- chrony-3.5/FAQ 2019-05-14 11:00:05.000000000 +0000 +++ chrony-4.1/FAQ 2021-05-13 10:48:12.000000000 +0000 @@ -6,36 +6,45 @@ ? 1.1. How does chrony compare to ntpd? o 2. Configuration issues ? 2.1. What is the minimum recommended configuration for an NTP client? - ? 2.2. How do I make an NTP server from an NTP client? - ? 2.3. I have several computers on a LAN. Should be all clients of an - external server? + ? 2.2. How do I make an NTP server? + ? 2.3. Should all computers on a LAN be clients of an external server? ? 2.4. Must I specify servers by IP address if DNS is not available on chronyd start? ? 2.5. How can I make chronyd more secure? - ? 2.6. How can I improve the accuracy of the system clock with NTP + ? 2.6. How can I make the system clock more secure? + ? 2.7. How can I improve the accuracy of the system clock with NTP sources? - ? 2.7. Does chronyd have an ntpdate mode? - ? 2.8. Can chronyd be configured to control the clock like ntpd? - ? 2.9. What happened to the commandkey and generatecommandkey directives? + ? 2.8. Does chronyd have an ntpdate mode? + ? 2.9. Can chronyd be configured to control the clock like ntpd? + ? 2.10. Can NTP server be separated from NTP client? + ? 2.11. Should be a leap smear enabled on NTP server? + ? 2.12. Does chrony support PTP? + ? 2.13. What happened to the commandkey and generatecommandkey + directives? o 3. Computer is not synchronising ? 3.1. Behind a firewall? ? 3.2. Are NTP servers specified with the offline option? - ? 3.3. Is chronyd allowed to step the system clock? - ? 3.4. Using a Windows NTP server? - ? 3.5. Using a PPS reference clock? + ? 3.3. Is name resolution working correctly? + ? 3.4. Is chronyd allowed to step the system clock? + ? 3.5. Using NTS? + ? 3.6. Using a Windows NTP server? + ? 3.7. An unreachable source is selected? + ? 3.8. Does selected source drop new measurements? + ? 3.9. Using a PPS reference clock? o 4. Issues with chronyc ? 4.1. I keep getting the error 506 Cannot talk to daemon ? 4.2. I keep getting the error 501 Not authorised - ? 4.3. Why does chronyc tracking always print an IPv4 address as - reference ID? + ? 4.3. What is the reference ID reported by the tracking command? ? 4.4. Is the chronyc / chronyd protocol documented anywhere? o 5. Real-time clock issues ? 5.1. What is the real-time clock (RTC)? - ? 5.2. I want to use chronyd's RTC support. Must I disable hwclock? + ? 5.2. Does hwclock have to be disabled? ? 5.3. I just keep getting the 513 RTC driver not running message ? 5.4. I get Could not open /dev/rtc, Device or resource busy in my syslog file - ? 5.5. What if my computer does not have an RTC or backup battery? + ? 5.5. When I start chronyd, the log says Could not enable RTC interrupt + : Invalid argument (or it may say disable) + ? 5.6. What if my computer does not have an RTC or backup battery? o 6. NTP-specific issues ? 6.1. Can chronyd be driven from broadcast/multicast NTP servers? ? 6.2. Can chronyd transmit broadcast NTP packets? @@ -43,6 +52,8 @@ time? ? 6.4. What happens if the network connection is dropped without using chronyc's offline command first? + ? 6.5. Why is an offset measured between two computers synchronised to + each another? o 7. Operating systems ? 7.1. Does chrony support Windows? ? 7.2. Are there any plans to support Windows? @@ -51,17 +62,20 @@ 1.1. How does chrony compare to ntpd? -chronyd was designed to work well in a wide range of conditions and it can -usually synchronise the system clock faster and with better time accuracy. It -doesn't implement some of the less useful NTP modes like broadcast client or -multicast server/client. +chrony and ntpd are two different implementations of the Network Time Protocol +(NTP). + +chrony is a newer implementation, which was designed to work well in a wider +range of conditions. It can usually synchronise the system clock faster and +with better time accuracy. It has many features, but it does not implement some +of the less useful NTP modes like broadcast client or multicast server/client. If your computer is connected to the Internet only for few minutes at a time, the network connection is often congested, you turn your computer off or suspend it frequently, the clock is not very stable (e.g. there are rapid -changes in the temperature or it's a virtual machine), or you want to use NTP +changes in the temperature or it is a virtual machine), or you want to use NTP on an isolated network with no hardware reference clocks in sight, chrony will -probably work much better for you. +probably work better for you. For a more detailed comparison of features and performance, see the comparison page on the chrony website. @@ -71,10 +85,12 @@ 2.1. What is the minimum recommended configuration for an NTP client? First, the client needs to know which NTP servers it should ask for the current -time. They are specified by the server or pool directive. The pool directive -can be used for names that resolve to multiple addresses. For good reliability -the client should have at least three servers. The iburst option speeds up the -initial synchronisation. +time. They are specified by the server or pool directive. The pool directive is +used with names that resolve to multiple addresses of different servers. For +reliable operation, the client should have at least three servers. + +The iburst option enables a burst of requests to speed up the initial +synchronisation. To stabilise the initial synchronisation on the next start, the estimated drift of the system clock is saved to a file specified by the driftfile directive. @@ -84,12 +100,12 @@ which would take a very long time. The makestep directive does that. In order to keep the real-time clock (RTC) close to the true time, so the -system time is reasonably close to the true time when it's initialised on the +system time is reasonably close to the true time when it is initialised on the next boot from the RTC, the rtcsync directive enables a mode in which the system time is periodically copied to the RTC. It is supported on Linux and macOS. -If you want to use public NTP servers from the pool.ntp.org project, the +If you wanted to use public NTP servers from the pool.ntp.org project, the minimal chrony.conf file could be: pool pool.ntp.org iburst @@ -97,18 +113,22 @@ makestep 1 3 rtcsync -2.2. How do I make an NTP server from an NTP client? +2.2. How do I make an NTP server? + +By default, chronyd does not operate as an NTP server. You need to add an allow +directive to the chrony.conf file in order for chronyd to open the server NTP +port and respond to client requests. + +allow 192.168.1.0/24 + +An allow directive with no specified subnet allows access from all IPv4 and +IPv6 addresses. -You need to add an allow directive to the chrony.conf file in order to open the -NTP port and allow chronyd to reply to client requests. allow with no specified -subnet allows access from all IPv4 and IPv6 addresses. - -2.3. I have several computers on a LAN. Should be all clients of an external -server? - -The best configuration is usually to make one computer the server, with the -others as clients of it. Add a local directive to the server's chrony.conf -file. This configuration will be better because +2.3. Should all computers on a LAN be clients of an external server? + +It depends on the requirements. Usually, the best configuration is to make one +computer the server, with the others as clients of it. Add a local directive to +the server's chrony.conf file. This configuration will be better because o the load on the external connection is less @@ -120,30 +140,17 @@ 2.4. Must I specify servers by IP address if DNS is not available on chronyd start? -No. Starting from version 1.25, chronyd will keep trying to resolve the names -specified by the server, pool, and peer directives in an increasing interval -until it succeeds. The online command can be issued from chronyc to force -chronyd to try to resolve the names immediately. +No, chronyd will keep trying to resolve the names specified by the server, +pool, and peer directives in an increasing interval until it succeeds. The +online command can be issued from chronyc to force chronyd to try to resolve +the names immediately. 2.5. How can I make chronyd more secure? -If you don't need to serve time to NTP clients or peers, you can add port 0 to -the chrony.conf file to completely disable the NTP server functionality and -prevent NTP requests from reaching chronyd. Starting from version 2.0, the NTP -server port is open only when client access is allowed by the allow directive -or command, an NTP peer is configured, or the broadcast directive is used. - -If you don't need to use chronyc remotely, you can add the following directives -to the configuration file to bind the command sockets to the loopback -interface. This is done by default since version 2.0. - -bindcmdaddress 127.0.0.1 -bindcmdaddress ::1 - -If you don't need to use chronyc at all or you need to run chronyc only under -the root or chrony user (which can access chronyd through a Unix domain socket -since version 2.2), you can disable the internet command sockets completely by -adding cmdport 0 to the configuration file. +If you do not need to use chronyc, or you want to run chronyc only under the +root or chrony user (which can access chronyd through a Unix domain socket), +you can disable the IPv4 and IPv6 command sockets (by default listening on +localhost) by adding cmdport 0 to the configuration file. You can specify an unprivileged user with the -u option, or the user directive in the chrony.conf file, to which chronyd will switch after start in order to @@ -156,27 +163,144 @@ Also, if chronyd is compiled with support for the Linux secure computing (seccomp) facility, you can enable a system call filter with the -F option. It will significantly reduce the kernel attack surface and possibly prevent kernel -exploits from the chronyd process if it's compromised. It's recommended to -enable the filter only when it's known to work on the version of the system +exploits from the chronyd process if it is compromised. It is recommended to +enable the filter only when it is known to work on the version of the system where chrony is installed as the filter needs to allow also system calls made from libraries that chronyd is using (e.g. libc) and different versions or -implementations of the libraries may make different system calls. If the filter -is missing some system call, chronyd could be killed even in normal operation. +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. + +2.6. How can I make the system clock more secure? + +An NTP client synchronising the system clock to an NTP server is susceptible to +various attacks, which can break applications and network protocols relying on +accuracy of the clock (e.g. DNSSEC, Kerberos, TLS, WireGuard). + +Generally, a man-in-the-middle (MITM) attacker between the client and server +can + + o make fake responses, or modify real responses from the server, to create an + arbitrarily large time and frequency offset, make the server appear more + accurate, insert a leap second, etc. + + o delay the requests and/or responses to create a limited time offset and + temporarily also a limited frequency offset + + o drop the requests or responses to prevent updates of the clock with new + measurements + + o redirect the requests to a different server + +The attacks can be combined for a greater effect. The attacker can delay +packets to create a significant frequency offset first and then drop all +subsequent packets to let the clock quickly drift away from the true time. The +attacker might also be able to control the server's clock. + +Some attacks cannot be prevented. Monitoring is needed for detection, e.g. the +reachability register in the sources report shows missing packets. The extent +to which the attacker can control the client's clock depends on its +configuration. + +Enable authentication to prevent chronyd from accepting modified, fake, or +redirected packets. It can be enabled with a symmetric key specified by the key +option, or Network Time Security (NTS) by the nts option (supported since +chrony version 4.0). The server needs to support the selected authentication +mechanism. Symmetric keys have to be configured on both client and server, and +each client must have its own key (one per server). + +The maximum offset that the attacker can insert in an NTP measurement by +delaying packets can be limited by the maxdelay option. The default value is 3 +seconds. The measured delay is reported as the peer delay in the ntpdata report +and measurements log. Set the maxdelay option to a value larger than the +maximum value that is normally observed. Note that the delay can increase +significantly even when not under an attack, e.g. when the network is congested +or the routing has changed. + +The maximum accepted change in time offset between clock updates can be limited +by the maxchange directive. Larger changes in the offset will be ignored or +cause chronyd to exit. Note that the attacker can get around this limit by +splitting the offset into multiple smaller offsets and/or creating a large +frequency offset. When this directive is used, chronyd will have to be +restarted after a successful attack. It will not be able to recover on its own. +It must not be restarted automatically (e.g. by the service manager). + +The impact of a large accepted time offset can be reduced by disabling clock +steps, i.e. by not using the makestep and initstepslew directives. The offset +will be slowly corrected by speeding up or slowing down the clock at a rate +which can be limited by the maxslewrate directive. Disabling clock steps +completely is practical only if the clock cannot gain a larger error on its +own, e.g. when the computer is shut down or suspended, and the maxslewrate +limit is large enough to correct an expected error in an acceptable time. The +rtcfile directive with the -s option can be used to compensate for the RTC +drift. + +A more practical approach is to enable makestep for a limited number of clock +updates (the 2nd argument of the directive) and limit the offset change in all +updates by the maxchange directive. The attacker will be able to make only a +limited step and only if the attack starts in a short window after booting the +computer, or when chronyd is restarted without the -R option. + +The frequency offset can be limited by the maxdrift directive. The measured +frequency offset is reported in the drift file, tracking report, and tracking +log. Set maxdrift to a value larger than the maximum absolute value that is +normally observed. Note that the frequency of the clock can change due to aging +of the crystal, differences in calibration of the clock source between reboots, +migrated virtual machine, etc. A typical computer clock has a drift smaller +than 100 parts per million (ppm), but much larger drifts are possible (e.g. in +some virtual machines). + +Use only trusted servers, which you expect to be well configured and managed, +using authentication for their own servers, etc. Use multiple servers, ideally +in different locations. The attacker will have to deal with a majority of the +servers in order to pass the source selection and update the clock with a large +offset. Use the minsources directive to increase the required number of +selectable sources to make the selection more robust. + +Do not specify servers as peers. The symmetric mode is less secure than the +client/server mode. If not authenticated, it is vulnerable to off-path +denial-of-service attacks, and even when it is authenticated, it is still +susceptible to replay attacks. + +Mixing of authenticated and unauthenticated servers should generally be +avoided. If mixing is necessary (e.g. for a more accurate and stable +synchronisation to a closer server which does not support authentication), the +authenticated servers should be configured as trusted and required to not allow +the unauthenticated servers to override the authenticated servers in the source +selection. Since chrony version 4.0, the selection options are enabled in such +a case automatically. This behaviour can be disabled or modified by the +authselmode directive. + +An example of a client configuration limiting the impact of the attacks could +be + +server foo.example.net iburst nts maxdelay 0.1 +server bar.example.net iburst nts maxdelay 0.2 +server baz.example.net iburst nts maxdelay 0.05 +server qux.example.net iburst nts maxdelay 0.1 +server quux.example.net iburst nts maxdelay 0.1 +minsources 3 +maxchange 100 0 0 +makestep 0.001 1 +maxdrift 100 +maxslewrate 100 +driftfile /var/lib/chrony/drift +ntsdumpdir /var/lib/chrony +rtcsync -2.6. How can I improve the accuracy of the system clock with NTP sources? +2.7. How can I improve the accuracy of the system clock with NTP sources? Select NTP servers that are well synchronised, stable and close to your -network. It's better to use more than one server, three or four is usually +network. It is better to use more than one server. Three or four is usually recommended as the minimum, so chronyd can detect servers that serve false time and combine measurements from multiple sources. If you have a network card with hardware timestamping supported on Linux, it -can be enabled by the hwtimestamp directive in the chrony.conf file. It should -make local receive and transmit timestamps of NTP packets much more accurate. +can be enabled by the hwtimestamp directive. It should make local receive and +transmit timestamps of NTP packets much more stable and accurate. -There are also useful options which can be set in the server directive, they -are minpoll, maxpoll, polltarget, maxdelay, maxdelayratio, maxdelaydevratio, -and xleave. +The server directive has some useful options: minpoll, maxpoll, polltarget, +maxdelay, maxdelayratio, maxdelaydevratio, xleave, filter. The first three options set the minimum and maximum allowed polling interval, and how should be the actual interval adjusted in the specified range. Their @@ -184,7 +308,7 @@ and 8 (samples) for polltarget. The default values should be used for general servers on the Internet. With your own NTP servers, or if you have permission to poll some servers more frequently, setting these options for shorter polling -intervals may significantly improve the accuracy of the system clock. +intervals might significantly improve the accuracy of the system clock. The optimal polling interval depends mainly on two factors, stability of the network latency and stability of the system clock (which mainly depends on the @@ -194,7 +318,7 @@ Generally, if the sourcestats command usually reports a small number of samples retained for a source (e.g. fewer than 16), a shorter polling interval should be considered. If the number of samples is usually at the maximum of 64, a -longer polling interval may work better. +longer polling interval might work better. An example of the directive for an NTP server on the Internet that you are allowed to poll frequently could be @@ -206,7 +330,7 @@ server ntp.local minpoll 2 maxpoll 4 polltarget 30 -The maxdelay options are useful to ignore measurements with an unusally large +The maxdelay options are useful to ignore measurements with an unusually large delay (e.g. due to congestion in the network) and improve the stability of the synchronisation. The maxdelaydevratio option could be added to the example with local NTP server @@ -214,15 +338,15 @@ server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2 If your server supports the interleaved mode (e.g. it is running chronyd), the -xleave option should be added to the server directive in order to allow the -server to send the client more accurate transmit timestamps (kernel or -preferably hardware). For example: +xleave option should be added to the server directive to enable the server to +provide the client with more accurate transmit timestamps (kernel or preferably +hardware). For example: server ntp.local minpoll 2 maxpoll 4 xleave When combined with local hardware timestamping, good network switches, and even shorter polling intervals, a sub-microsecond accuracy and stability of a few -tens of nanoseconds may be possible. For example: +tens of nanoseconds might be possible. For example: server ntp.local minpoll 0 maxpoll 0 xleave hwtimestamp eth0 @@ -231,29 +355,51 @@ disabled power saving and performance boosting). Energy-Efficient Ethernet (EEE) should be disabled in the network. The switches should be configured to prioritize NTP packets, especially if the network is expected to be heavily -loaded. +loaded. The dscp directive can be used to set the Differentiated Services Code +Point in transmitted NTP packets if needed. -If it is acceptable for NTP clients in the network to send requests at an -excessive rate, a sub-second polling interval may be specified. A median filter -can be enabled in order to update the clock at a reduced rate with more stable +If it is acceptable for NTP clients in the network to send requests at a high +rate, a sub-second polling interval can be specified. A median filter can be +enabled in order to update the clock at a reduced rate with more stable measurements. For example: server ntp.local minpoll -6 maxpoll -6 filter 15 xleave hwtimestamp eth0 minpoll -6 -2.7. Does chronyd have an ntpdate mode? +2.8. Does chronyd have an ntpdate mode? Yes. With the -q option chronyd will set the system clock once and exit. With the -Q option it will print the measured offset without setting the clock. If -you don't want to use a configuration file, NTP servers can be specified on the -command line. For example: +you do not want to use a configuration file, NTP servers can be specified on +the command line. For example: # chronyd -q 'pool pool.ntp.org iburst' -2.8. Can chronyd be configured to control the clock like ntpd? +The command above would normally take about 5 seconds if the servers were well +synchronised and responding to all requests. If not synchronised or responding, +it would take about 10 seconds for chronyd to give up and exit with a non-zero +status. A faster configuration is possible. A single server can be used instead +of four servers, the number of measurements can be reduced with the maxsamples +option to one (supported since chrony version 4.0), and a timeout can be +specified with the -t option. The following command would take only up to about +one second. + +# chronyd -q -t 1 'server pool.ntp.org iburst maxsamples 1' + +It is not recommended to run chronyd with the -q option periodically (e.g. from +a cron job) as a replacement for the daemon mode, because it performs +significantly worse (e.g. the clock is stepped and its frequency is not +corrected). If you must run it this way and you are using a public NTP server, +make sure chronyd does not always start around the first second of a minute, +e.g. by adding a random sleep before the chronyd command. Public servers +typically receive large bursts of requests around the first second as there is +a large number of NTP clients started from cron with no delay. + +2.9. Can chronyd be configured to control the clock like ntpd? It is not possible to perfectly emulate ntpd, but there are some options that -can configure chronyd to behave more like ntpd. +can configure chronyd to behave more like ntpd if there is a reason to prefer +that. In the following example the minsamples directive slows down the response to changes in the frequency and offset of the clock. The maxslewrate and @@ -271,11 +417,84 @@ maxchange 1000 1 1 maxclockerror 15 -Note that increasing minsamples may cause the offsets in the tracking and +Note that increasing minsamples might cause the offsets in the tracking and sourcestats reports/logs to be significantly smaller than the actual offsets and be unsuitable for monitoring. -2.9. What happened to the commandkey and generatecommandkey directives? +2.10. Can NTP server be separated from NTP client? + +Yes, it is possible to run multiple instances of chronyd on a computer at the +same time. One can operate primarily as an NTP client to synchronise the system +clock and another as a server for other computers. If they use the same +filesystem, they need to be configured with different pidfiles, Unix domain +command sockets, and any other file or directory specified in the configuration +file. If they run in the same network namespace, they need to use different NTP +and command ports, or bind the ports to different addresses or interfaces. + +The server instance should be started with the -x option to prevent it from +adjusting the system clock and interfering with the client instance. It can be +configured as a client to synchronise its NTP clock to other servers, or the +client instance running on the same computer. In the latter case, the copy +option (added in chrony version 4.1) can be used to assume the reference ID and +stratum of the client instance, which enables detection of synchronisation +loops with its own clients. + +On Linux, starting with chrony version 4.0, it is possible to run multiple +server instances sharing a port to better utilise multiple cores of the CPU. +Note that for rate limiting and client/server interleaved mode to work well it +is necessary that all packets received from the same address are handled by the +same server instance. + +An example configuration of the client instance could be + +pool pool.ntp.org iburst +allow 127.0.0.1 +port 11123 +driftfile /var/lib/chrony/drift +makestep 1 3 +rtcsync + +and configuration of the first server instance could be + +server 127.0.0.1 port 11123 minpoll 0 maxpoll 0 copy +allow +cmdport 11323 +bindcmdaddress /var/run/chrony/chronyd-server1.sock +pidfile /var/run/chronyd-server1.pid +driftfile /var/lib/chrony/drift-server1 + +2.11. Should be a leap smear enabled on NTP server? + +With the smoothtime and leapsecmode directives it is possible to enable a +server leap smear in order to hide leap seconds from clients and force them to +follow a slow server's adjustment instead. + +This feature should be used only in local networks and only when necessary, +e.g. when the clients cannot be configured to handle the leap seconds as +needed, or their number is so large that configuring them all would be +impractical. The clients should use only one leap-smearing server, or multiple +identically configured leap-smearing servers. Note that some clients can get +leap seconds from other sources (e.g. with the leapsectz directive in chrony) +and they will not work correctly with a leap smearing server. + +2.12. Does chrony support PTP? + +No, the Precision Time Protocol (PTP) is not supported and there are no plans +to support it. It is a complex protocol, which shares some issues with the NTP +broadcast mode. One of the main differences between NTP and PTP is that PTP was +designed to be easily supported in hardware (e.g. network switches and routers) +in order to make more stable and accurate measurements. PTP relies on the +hardware support. NTP does not rely on any support in the hardware, but if it +had the same support as PTP, it could perform equally well. + +On Linux, chrony supports hardware clocks that some NICs have for PTP. They are +called PTP hardware clocks (PHC). They can be used as reference clocks +(specified by the refclock directive) and for hardware timestamping of NTP +packets (enabled by the hwtimestamp directive) if the NIC can timestamp other +packets than PTP, which is usually the case at least for transmitted packets. +The ethtool -T command can be used to verify the timestamping support. + +2.13. What happened to the commandkey and generatecommandkey directives? They were removed in version 2.2. Authentication is no longer supported in the command protocol. Commands that required authentication are now allowed only @@ -291,16 +510,15 @@ 3.1. Behind a firewall? -Check the Reach value printed by the chronyc's sources command. If it's zero, +Check the Reach value printed by the chronyc's sources command. If it is zero, it means chronyd did not get any valid responses from the NTP server you are trying to use. If there is a firewall between you and the server, the packets -may be blocked. Try using a tool like wireshark or tcpdump to see if you're +might be blocked. Try using a tool like wireshark or tcpdump to see if you are getting any responses from the server. When chronyd is receiving responses from the servers, the output of the sources command issued few minutes after chronyd start might look like this: -210 Number of sources = 3 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== ^* foo.example.net 2 6 377 34 +484us[ -157us] +/- 30ms @@ -309,9 +527,9 @@ 3.2. Are NTP servers specified with the offline option? -Check that you're using chronyc's online and offline commands appropriately. -The activity command prints the number of sources that are currently online and -offline. For example: +Check that the chronyc's online and offline commands are used appropriately +(e.g. in the system networking scripts). The activity command prints the number +of sources that are currently online and offline. For example: 200 OK 3 sources online @@ -320,7 +538,20 @@ 0 sources doing burst (return to offline) 0 sources with unknown address -3.3. Is chronyd allowed to step the system clock? +3.3. Is name resolution working correctly? + +NTP servers specified by their hostname (instead of an IP address) have to have +their names resolved before chronyd can send any requests to them. If the +activity command prints a non-zero number of sources with unknown address, +there is an issue with the resolution. Typically, a DNS server is specified in +/etc/resolv.conf. Make sure it is working correctly. + +Since chrony version 4.0, you can run chronyc -N sources -a command to print +all sources, even those that do not have a known address yet, with their names +as they were specified in the configuration. This can be useful to verify that +the names specified in the configuration are used as expected. + +3.4. Is chronyd allowed to step the system clock? By default, chronyd adjusts the clock gradually by slowing it down or speeding it up. If the clock is too far from the true time, it will take a long time to @@ -334,19 +565,60 @@ makestep 1 3 the clock would be stepped in the first three updates if its offset was larger -than one second. Normally, it's recommended to allow the step only in the first -few updates, but in some cases (e.g. a computer without an RTC or virtual -machine which can be suspended and resumed with an incorrect time) it may be +than one second. Normally, it is recommended to allow the step only in the +first few updates, but in some cases (e.g. a computer without an RTC or virtual +machine which can be suspended and resumed with an incorrect time) it might be necessary to allow the step on any clock update. The example above would change to makestep 1 -1 -3.4. Using a Windows NTP server? +3.5. Using NTS? + +The Network Time Security (NTS) mechanism uses Transport Layer Security (TLS) +to establish the keys needed for authentication of NTP packets. + +Run the authdata command to check whether the key establishment was successful: + +# chronyc -N authdata +Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen +========================================================================= +foo.example.net NTS 1 15 256 33m 0 0 8 100 +bar.example.net NTS 1 15 256 33m 0 0 8 100 +baz.example.net NTS 1 15 256 33m 0 0 8 100 + +The KeyID, Type, and KLen columns should have non-zero values. If they are +zero, check the system log for error messages from chronyd. One possible cause +of failure is a firewall blocking the client's connection to the server's TCP +port 4460. + +Another possible cause of failure is a certificate that is failing to verify +because the client's clock is wrong. This is a chicken-and-egg problem with +NTS. You might need to manually correct the date, or temporarily disable NTS, +in order to get NTS working. If your computer has an RTC and it is backed up by +a good battery, this operation should be needed only once, assuming the RTC +will be set periodically with the rtcsync directive, or compensated with the +rtcfile directive and the -s option. + +If the computer does not have an RTC or battery, you can use the -s option +without rtcfile directive to restore time of the last shutdown or reboot from +the drift file. The clock will start behind the true time, but if the computer +was not shut down for too long and the server's certificate was not renewed too +close to its expiration, it should be sufficient for the time checks to +succeed. + +As a last resort, you can disable the time checks by the nocerttimecheck +directive. This has some important security implications. To reduce the +security risk, you can use the nosystemcert and ntstrustedcerts directives to +disable the system's default trusted certificate authorities and trust only a +minimal set of selected authorities needed to validate the certificates of used +NTP servers. + +3.6. Using a Windows NTP server? A common issue with Windows NTP servers is that they report a very large root dispersion (e.g. three seconds or more), which causes chronyd to ignore the -server for being too inaccurate. The sources command may show a valid +server for being too inaccurate. The sources command might show a valid measurement, but the server is not selected for synchronisation. You can check the root dispersion of the server with the chronyc's ntpdata command. @@ -355,7 +627,51 @@ maxdistance 16.0 -3.5. Using a PPS reference clock? +3.7. An unreachable source is selected? + +When chronyd is configured with multiple time sources, it tries to select the +most accurate and stable sources for synchronisation of the system clock. They +are marked with the * or + symbol in the report printed by the sources command. + +When the best source (marked with the * symbol) becomes unreachable (e.g. NTP +server stops responding), chronyd will not immediately switch to the second +best source in an attempt to minimise the error of the clock. It will let the +clock run free for as long as its estimated error (in terms of root distance) +based on previous measurements is smaller than the estimated error of the +second source, and there is still an interval which contains some measurements +from both sources. + +If the first source was significantly better than the second source, it can +take many hours before the second source is selected, depending on its polling +interval. You can force a faster reselection by increasing the clock error rate +(maxclockerror directive), shortening the polling interval (maxpoll option), or +reducing the number of samples (maxsamples option). + +3.8. 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. + +3.9. Using a PPS reference clock? A pulse-per-second (PPS) reference clock requires a non-PPS time source to determine which second of UTC corresponds to each pulse. If it is another @@ -387,37 +703,39 @@ When accessing chronyd remotely, make sure that the chrony.conf file (on the computer where chronyd is running) has a cmdallow entry for the computer you -are running chronyc on and an appropriate bindcmdaddress directive. This isn't +are running chronyc on and an appropriate bindcmdaddress directive. This is not necessary for localhost. Perhaps chronyd is not running. Try using the ps command (e.g. on Linux, ps --auxw) to see if it's running. Or try netstat -a and see if the ports 123/udp -and 323/udp are listening. If chronyd is not running, you may have a problem -with the way you are trying to start it (e.g. at boot time). +-auxw) to see if it is running. Or try netstat -a and see if the UDP port 323 +is listening. If chronyd is not running, you might have a problem with the way +you are trying to start it (e.g. at boot time). -Perhaps you have a firewall set up in a way that blocks packets on port 323/ -udp. You need to amend the firewall configuration in this case. +Perhaps you have a firewall set up in a way that blocks packets on the UDP port +323. You need to amend the firewall configuration in this case. 4.2. I keep getting the error 501 Not authorised -Since version 2.2, the password command doesn't do anything and chronyc needs -to run locally under the root or chrony user, which are allowed to access the -chronyd's Unix domain command socket. - -With older versions, you need to authenticate with the password command first -or use the -a option to authenticate automatically on start. The configuration -file needs to specify a file which contains keys (keyfile directive) and which -key in the key file should be used for chronyc authentication (commandkey -directive). - -4.3. Why does chronyc tracking always print an IPv4 address as reference ID? - -The reference ID is a 32-bit value and in versions before 3.0 it was printed in -quad-dotted notation, even if the reference source did not actually have an -IPv4 address. For IPv4 addresses, the reference ID is equal to the address, but -for IPv6 addresses it is the first 32 bits of the MD5 sum of the address. For -reference clocks, the reference ID is the value specified with the refid option -in the refclock directive. +This error indicates that chronyc sent the command to chronyd using a UDP +socket instead of the Unix domain socket (e.g. /var/run/chrony/chronyd.sock), +which is required for some commands. For security reasons, only the root and +chrony users are allowed to access the socket. + +It is also possible that the socket does not exist. chronyd will not create the +socket if the directory has a wrong owner or permissions. In this case there +should be an error message from chronyd in the system log. + +4.3. What is the reference ID reported by the tracking command? + +The reference ID is a 32-bit value used in NTP to prevent synchronisation +loops. + +In chrony versions before 3.0 it was printed in the quad-dotted notation, even +if the reference source did not actually have an IPv4 address. For IPv4 +addresses, the reference ID is equal to the address, but for IPv6 addresses it +is the first 32 bits of the MD5 sum of the address. For reference clocks, the +reference ID is the value specified with the refid option in the refclock +directive. Since version 3.0, the reference ID is printed as a hexadecimal number to avoid confusion with IPv4 addresses. @@ -436,13 +754,13 @@ 5.1. What is the real-time clock (RTC)? This is the clock which keeps the time even when your computer is turned off. -It is used to initialise the system clock on boot. It normally doesn't drift +It is used to initialise the system clock on boot. It normally does not drift more than few seconds per day. There are two approaches how chronyd can work with it. One is to use the rtcsync directive, which tells chronyd to enable a kernel mode which sets the -RTC from the system clock every 11 minutes. chronyd itself won't touch the RTC. -If the computer is not turned off for a long time, the RTC should still be +RTC from the system clock every 11 minutes. chronyd itself will not touch the +RTC. If the computer is not turned off for a long time, the RTC should still be close to the true time when the system clock will be initialised from it on the next boot. @@ -450,21 +768,21 @@ monitor the rate at which the RTC gains or loses time. When chronyd is started with the -s option on the next boot, it will set the system time from the RTC and also compensate for the drift it has measured previously. The rtcautotrim -directive can be used to keep the RTC close to the true time, but it's not +directive can be used to keep the RTC close to the true time, but it is not strictly necessary if its only purpose is to set the system clock when chronyd is started on boot. See the documentation for details. -5.2. I want to use chronyd's RTC support. Must I disable hwclock? +5.2. Does hwclock have to be disabled? -The hwclock program is often set-up by default in the boot and shutdown scripts -with many Linux installations. With the kernel RTC synchronisation (rtcsync +The hwclock program is run by default in the boot and/or shutdown scripts in +some Linux installations. With the kernel RTC synchronisation (rtcsync directive), the RTC will be set also every 11 minutes as long as the system clock is synchronised. If you want to use chronyd's RTC monitoring (rtcfile -directive), it's important to disable hwclock in the shutdown procedure. If you -don't, it will over-write the RTC with a new value, unknown to chronyd. At the -next reboot, chronyd started with the -s option will compensate this (wrong) -time with its estimate of how far the RTC has drifted whilst the power was off, -giving a meaningless initial system time. +directive), it is important to disable hwclock in the shutdown procedure. If +you do not do that, it will overwrite the RTC with a new value, unknown to +chronyd. At the next reboot, chronyd started with the -s option will compensate +this (wrong) time with its estimate of how far the RTC has drifted whilst the +power was off, giving a meaningless initial system time. There is no need to remove hwclock from the boot process, as long as chronyd is started after it has run. @@ -481,9 +799,24 @@ 5.4. I get Could not open /dev/rtc, Device or resource busy in my syslog file -Some other program running on the system may be using the device. +Some other program running on the system might be using the device. -5.5. What if my computer does not have an RTC or backup battery? +5.5. When I start chronyd, the log says Could not enable RTC interrupt : +Invalid argument (or it may say disable) + +Your real-time clock hardware might not support the required ioctl requests: + + o RTC_UIE_ON + + o RTC_UIE_OFF + +A possible solution could be to build the Linux kernel with support for +software emulation instead; try enabling the following configuration option +when building the Linux kernel: + + o CONFIG_RTC_INTF_DEV_UIE_EMUL + +5.6. What if my computer does not have an RTC or backup battery? In this case you can still use the -s option to set the system clock to the last modification time of the drift file, which should correspond to the system @@ -496,9 +829,9 @@ 6.1. Can chronyd be driven from broadcast/multicast NTP servers? No, the broadcast/multicast client mode is not supported and there is currently -no plan to implement it. While the mode may be useful to simplify configuration -of clients in large networks, it is inherently less accurate and less secure -(even with authentication) than the ordinary client/server mode. +no plan to implement it. While this mode can simplify configuration of clients +in large networks, it is inherently less accurate and less secure (even with +authentication) than the ordinary client/server mode. When configuring a large number of clients in a network, it is recommended to use the pool directive with a DNS name which resolves to addresses of multiple @@ -513,7 +846,8 @@ Yes, the broadcast directive can be used to enable the broadcast server mode to serve time to clients in the network which support the broadcast client mode -(it's not supported in chronyd, see the previous question). +(it is not supported in chronyd). Note that this mode should generally be +avoided. See the previous question. 6.3. Can chronyd keep the system clock a fixed offset away from real time? @@ -530,8 +864,21 @@ online command. Unless the network connection lasts only few minutes (less than the maximum -polling interval), the delay is usually not a problem, and it may be acceptable -to keep all sources online all the time. +polling interval), the delay is usually not a problem, and it might be +acceptable to keep all sources online all the time. + +6.5. Why is an offset measured between two computers synchronised to each +another? + +When two computers are synchronised to each other using the client/server or +symmetric NTP mode, there is an expectation that NTP measurements between the +two computers made on both ends show an average offset close to zero. + +With chronyd that can be expected only when the interleaved mode is enabled by +the xleave option. Otherwise, chronyd will use different transmit timestamps +(e.g. daemon timestamp vs kernel timestamp) for serving time and +synchronisation of its own clock, which will cause the other computer to +measure a significant offset. 7. Operating systems @@ -548,4 +895,4 @@ We have no plans to do this. Anyone is welcome to pick this work up and contribute it back to the project. -Last updated 2019-05-10 12:22:57 CEST +Last updated 2021-05-12 13:06:15 +0200 diff -Nru chrony-3.5/getdate.c chrony-4.1/getdate.c --- chrony-3.5/getdate.c 2019-05-14 11:00:05.000000000 +0000 +++ chrony-4.1/getdate.c 2021-05-13 10:48:13.000000000 +0000 @@ -1,8 +1,9 @@ -/* A Bison parser, made by GNU Bison 3.0.4. */ +/* A Bison parser, made by GNU Bison 3.5. */ /* Bison implementation for Yacc-like parsers in C - Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation, + Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -40,11 +41,14 @@ define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "3.0.4" +#define YYBISON_VERSION "3.5" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -61,8 +65,8 @@ -/* Copy the first part of user declarations. */ -#line 1 "getdate.y" /* yacc.c:339 */ +/* First part of user prologue. */ +#line 1 "getdate.y" /* ** Originally written by Steven M. Bellovin while @@ -235,13 +239,26 @@ static int yyRelYear; -#line 239 "getdate.c" /* yacc.c:339 */ +#line 243 "getdate.c" +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif # ifndef YY_NULLPTR -# if defined __cplusplus && 201103L <= __cplusplus -# define YY_NULLPTR nullptr +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif # else -# define YY_NULLPTR 0 +# define YY_NULLPTR ((void*)0) # endif # endif @@ -288,17 +305,16 @@ /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED - union YYSTYPE { -#line 177 "getdate.y" /* yacc.c:355 */ +#line 177 "getdate.y" int Number; enum _MERIDIAN Meridian; -#line 300 "getdate.c" /* yacc.c:355 */ -}; +#line 316 "getdate.c" +}; typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 @@ -311,36 +327,81 @@ -/* Copy the second part of user declarations. */ -#line 317 "getdate.c" /* yacc.c:358 */ #ifdef short # undef short #endif -#ifdef YYTYPE_UINT8 -typedef YYTYPE_UINT8 yytype_uint8; -#else -typedef unsigned char yytype_uint8; +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif #endif -#ifdef YYTYPE_INT8 -typedef YYTYPE_INT8 yytype_int8; +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; #else typedef signed char yytype_int8; #endif -#ifdef YYTYPE_UINT16 -typedef YYTYPE_UINT16 yytype_uint16; +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; #else -typedef unsigned short int yytype_uint16; +typedef short yytype_uint8; #endif -#ifdef YYTYPE_INT16 -typedef YYTYPE_INT16 yytype_int16; +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; #else -typedef short int yytype_int16; +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif #endif #ifndef YYSIZE_T @@ -348,15 +409,27 @@ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t -# elif ! defined YYSIZE_T +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else -# define YYSIZE_T unsigned int +# define YYSIZE_T unsigned # endif #endif -#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS @@ -370,30 +443,19 @@ # endif #endif -#ifndef YY_ATTRIBUTE -# if (defined __GNUC__ \ - && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ - || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C -# define YY_ATTRIBUTE(Spec) __attribute__(Spec) +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) # else -# define YY_ATTRIBUTE(Spec) /* empty */ +# define YY_ATTRIBUTE_PURE # endif #endif -#ifndef YY_ATTRIBUTE_PURE -# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) -#endif - #ifndef YY_ATTRIBUTE_UNUSED -# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) -#endif - -#if !defined _Noreturn \ - && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112) -# if defined _MSC_VER && 1200 <= _MSC_VER -# define _Noreturn __declspec (noreturn) +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) # else -# define _Noreturn YY_ATTRIBUTE ((__noreturn__)) +# define YY_ATTRIBUTE_UNUSED # endif #endif @@ -404,13 +466,13 @@ # define YYUSE(E) /* empty */ #endif -#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ /* Suppress an incorrect diagnostic about yylval being uninitialized. */ -# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ - _Pragma ("GCC diagnostic push") \ - _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") -# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ _Pragma ("GCC diagnostic pop") #else # define YY_INITIAL_VALUE(Value) Value @@ -423,6 +485,20 @@ # define YY_INITIAL_VALUE(Value) /* Nothing. */ #endif +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) #if ! defined yyoverflow || YYERROR_VERBOSE @@ -499,17 +575,17 @@ /* A type that is properly aligned for any stack member. */ union yyalloc { - yytype_int16 yyss_alloc; + yy_state_t yyss_alloc; YYSTYPE yyvs_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ -# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ - ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) # define YYCOPY_NEEDED 1 @@ -522,11 +598,11 @@ # define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ - YYSIZE_T yynewbytes; \ + YYPTRDIFF_T yynewbytes; \ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ - yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / sizeof (*yyptr); \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ } \ while (0) @@ -538,12 +614,12 @@ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(Dst, Src, Count) \ - __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) # else # define YYCOPY(Dst, Src, Count) \ do \ { \ - YYSIZE_T yyi; \ + YYPTRDIFF_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (Dst)[yyi] = (Src)[yyi]; \ } \ @@ -566,17 +642,18 @@ /* YYNSTATES -- Number of states. */ #define YYNSTATES 61 -/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned - by yylex, with out-of-bounds checking. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 273 + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ #define YYTRANSLATE(YYX) \ - ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + (0 <= (YYX) && (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM - as returned by yylex, without out-of-bounds checking. */ -static const yytype_uint8 yytranslate[] = + as returned by yylex. */ +static const yytype_int8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -610,7 +687,7 @@ #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ -static const yytype_uint16 yyrline[] = +static const yytype_int16 yyrline[] = { 0, 193, 193, 194, 197, 200, 203, 206, 209, 212, 215, 221, 227, 236, 242, 254, 257, 261, 266, 270, @@ -637,7 +714,7 @@ # ifdef YYPRINT /* YYTOKNUM[NUM] -- (External) token number corresponding to the (internal) symbol number NUM (which must be that of a token). */ -static const yytype_uint16 yytoknum[] = +static const yytype_int16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 58, @@ -645,14 +722,14 @@ }; # endif -#define YYPACT_NINF -20 +#define YYPACT_NINF (-20) -#define yypact_value_is_default(Yystate) \ - (!!((Yystate) == (-20))) +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) -#define YYTABLE_NINF -1 +#define YYTABLE_NINF (-1) -#define yytable_value_is_error(Yytable_value) \ +#define yytable_value_is_error(Yyn) \ 0 /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing @@ -671,7 +748,7 @@ /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. Performed when YYTABLE does not specify something else to do. Zero means the default is an error. */ -static const yytype_uint8 yydefact[] = +static const yytype_int8 yydefact[] = { 2, 0, 1, 18, 39, 16, 42, 45, 0, 36, 48, 0, 49, 33, 15, 3, 4, 5, 7, 6, @@ -699,7 +776,7 @@ /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule whose number is the opposite. If YYTABLE_NINF, syntax error. */ -static const yytype_uint8 yytable[] = +static const yytype_int8 yytable[] = { 2, 23, 52, 24, 3, 4, 5, 59, 6, 46, 47, 7, 8, 9, 10, 11, 12, 13, 14, 31, @@ -709,7 +786,7 @@ 60 }; -static const yytype_uint8 yycheck[] = +static const yytype_int8 yycheck[] = { 0, 20, 10, 16, 4, 5, 6, 15, 8, 15, 16, 11, 12, 13, 14, 15, 16, 17, 18, 4, @@ -721,7 +798,7 @@ /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ -static const yytype_uint8 yystos[] = +static const yytype_int8 yystos[] = { 0, 23, 0, 4, 5, 6, 8, 11, 12, 13, 14, 15, 16, 17, 18, 24, 25, 26, 27, 28, @@ -733,7 +810,7 @@ }; /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ -static const yytype_uint8 yyr1[] = +static const yytype_int8 yyr1[] = { 0, 22, 23, 23, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 26, 26, 26, 27, 27, @@ -744,7 +821,7 @@ }; /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ -static const yytype_uint8 yyr2[] = +static const yytype_int8 yyr2[] = { 0, 2, 0, 2, 1, 1, 1, 1, 1, 1, 2, 4, 4, 6, 6, 1, 1, 2, 1, 2, @@ -767,22 +844,22 @@ #define YYRECOVERING() (!!yyerrstatus) -#define YYBACKUP(Token, Value) \ -do \ - if (yychar == YYEMPTY) \ - { \ - yychar = (Token); \ - yylval = (Value); \ - YYPOPSTACK (yylen); \ - yystate = *yyssp; \ - goto yybackup; \ - } \ - else \ - { \ - yyerror (YY_("syntax error: cannot back up")); \ - YYERROR; \ - } \ -while (0) +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) /* Error token number */ #define YYTERROR 1 @@ -822,37 +899,39 @@ } while (0) -/*----------------------------------------. -| Print this symbol's value on YYOUTPUT. | -`----------------------------------------*/ +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ static void -yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep) { - FILE *yyo = yyoutput; - YYUSE (yyo); + FILE *yyoutput = yyo; + YYUSE (yyoutput); if (!yyvaluep) return; # ifdef YYPRINT if (yytype < YYNTOKENS) - YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); + YYPRINT (yyo, yytoknum[yytype], *yyvaluep); # endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END } -/*--------------------------------. -| Print this symbol on YYOUTPUT. | -`--------------------------------*/ +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ static void -yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep) { - YYFPRINTF (yyoutput, "%s %s (", + YYFPRINTF (yyo, "%s %s (", yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); - yy_symbol_value_print (yyoutput, yytype, yyvaluep); - YYFPRINTF (yyoutput, ")"); + yy_symbol_value_print (yyo, yytype, yyvaluep); + YYFPRINTF (yyo, ")"); } /*------------------------------------------------------------------. @@ -861,7 +940,7 @@ `------------------------------------------------------------------*/ static void -yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) @@ -884,12 +963,12 @@ `------------------------------------------------*/ static void -yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule) +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, int yyrule) { - unsigned long int yylno = yyrline[yyrule]; + int yylno = yyrline[yyrule]; int yynrhs = yyr2[yyrule]; int yyi; - YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) @@ -897,7 +976,7 @@ YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yystos[yyssp[yyi + 1 - yynrhs]], - &(yyvsp[(yyi + 1) - (yynrhs)]) + &yyvsp[(yyi + 1) - (yynrhs)] ); YYFPRINTF (stderr, "\n"); } @@ -941,13 +1020,13 @@ # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H -# define yystrlen strlen +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) # else /* Return the length of YYSTR. */ -static YYSIZE_T +static YYPTRDIFF_T yystrlen (const char *yystr) { - YYSIZE_T yylen; + YYPTRDIFF_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; @@ -983,12 +1062,12 @@ backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ -static YYSIZE_T +static YYPTRDIFF_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { - YYSIZE_T yyn = 0; + YYPTRDIFF_T yyn = 0; char const *yyp = yystr; for (;;) @@ -1001,7 +1080,10 @@ case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; - /* Fall through. */ + else + goto append; + + append: default: if (yyres) yyres[yyn] = *yyp; @@ -1016,10 +1098,10 @@ do_not_strip_quotes: ; } - if (! yyres) + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else return yystrlen (yystr); - - return yystpcpy (yyres, yystr) - yyres; } # endif @@ -1032,19 +1114,19 @@ *YYMSG_ALLOC to the required number of bytes. Return 2 if the required number of bytes is too large to store. */ static int -yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, - yytype_int16 *yyssp, int yytoken) +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + yy_state_t *yyssp, int yytoken) { - YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); - YYSIZE_T yysize = yysize0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; /* Internationalized format string. */ const char *yyformat = YY_NULLPTR; - /* Arguments of yyformat. */ + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - /* Number of reported tokens (one for the "unexpected", one per - "expected"). */ + /* Actual size of YYARG. */ int yycount = 0; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; /* There are many possibilities here to consider: - If this state is a consistent state with a default action, then @@ -1072,6 +1154,8 @@ if (yytoken != YYEMPTY) { int yyn = yypact[*yyssp]; + YYPTRDIFF_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + yysize = yysize0; yyarg[yycount++] = yytname[yytoken]; if (!yypact_value_is_default (yyn)) { @@ -1096,11 +1180,12 @@ } yyarg[yycount++] = yytname[yyx]; { - YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); - if (! (yysize <= yysize1 - && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else return 2; - yysize = yysize1; } } } @@ -1112,6 +1197,7 @@ case N: \ yyformat = S; \ break + default: /* Avoid compiler warnings. */ YYCASE_(0, YY_("syntax error")); YYCASE_(1, YY_("syntax error, unexpected %s")); YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); @@ -1122,10 +1208,13 @@ } { - YYSIZE_T yysize1 = yysize + yystrlen (yyformat); - if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + /* Don't count the "%s"s in the final size, but reserve room for + the terminator. */ + YYPTRDIFF_T yysize1 = yysize + (yystrlen (yyformat) - 2 * yycount) + 1; + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else return 2; - yysize = yysize1; } if (*yymsg_alloc < yysize) @@ -1151,8 +1240,8 @@ } else { - yyp++; - yyformat++; + ++yyp; + ++yyformat; } } return 0; @@ -1195,7 +1284,7 @@ int yyparse (void) { - int yystate; + yy_state_fast_t yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; @@ -1207,16 +1296,16 @@ to reallocate them elsewhere. */ /* The state stack. */ - yytype_int16 yyssa[YYINITDEPTH]; - yytype_int16 *yyss; - yytype_int16 *yyssp; + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss; + yy_state_t *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs; YYSTYPE *yyvsp; - YYSIZE_T yystacksize; + YYPTRDIFF_T yystacksize; int yyn; int yyresult; @@ -1230,7 +1319,7 @@ /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; - YYSIZE_T yymsg_alloc = sizeof yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; #endif #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) @@ -1251,46 +1340,54 @@ yychar = YYEMPTY; /* Cause a token to be read. */ goto yysetstate; + /*------------------------------------------------------------. -| yynewstate -- Push a new state, which is found in yystate. | +| yynewstate -- push a new state, which is found in yystate. | `------------------------------------------------------------*/ - yynewstate: +yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; - yysetstate: - *yyssp = yystate; + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else { /* Get the current used size of the three stacks, in elements. */ - YYSIZE_T yysize = yyssp - yyss + 1; + YYPTRDIFF_T yysize = yyssp - yyss + 1; -#ifdef yyoverflow +# if defined yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ + yy_state_t *yyss1 = yyss; YYSTYPE *yyvs1 = yyvs; - yytype_int16 *yyss1 = yyss; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), - &yyss1, yysize * sizeof (*yyssp), - &yyvs1, yysize * sizeof (*yyvsp), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), &yystacksize); - yyss = yyss1; yyvs = yyvs1; } -#else /* no yyoverflow */ -# ifndef YYSTACK_RELOCATE - goto yyexhaustedlab; -# else +# else /* defined YYSTACK_RELOCATE */ /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; @@ -1299,42 +1396,43 @@ yystacksize = YYMAXDEPTH; { - yytype_int16 *yyss1 = yyss; + yy_state_t *yyss1 = yyss; union yyalloc *yyptr = - (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); -# undef YYSTACK_RELOCATE +# undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif -#endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; - YYDPRINTF ((stderr, "Stack size increased to %lu\n", - (unsigned long int) yystacksize)); + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END if (yyss + yystacksize - 1 <= yyssp) YYABORT; } - - YYDPRINTF ((stderr, "Entering state %d\n", yystate)); +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ if (yystate == YYFINAL) YYACCEPT; goto yybackup; + /*-----------. | yybackup. | `-----------*/ yybackup: - /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ @@ -1384,15 +1482,13 @@ /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); - - /* Discard the shifted token. */ - yychar = YYEMPTY; - yystate = yyn; YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END + /* Discard the shifted token. */ + yychar = YYEMPTY; goto yynewstate; @@ -1407,7 +1503,7 @@ /*-----------------------------. -| yyreduce -- Do a reduction. | +| yyreduce -- do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ @@ -1427,71 +1523,71 @@ YY_REDUCE_PRINT (yyn); switch (yyn) { - case 4: -#line 197 "getdate.y" /* yacc.c:1646 */ - { + case 4: +#line 197 "getdate.y" + { yyHaveTime++; } -#line 1436 "getdate.c" /* yacc.c:1646 */ +#line 1532 "getdate.c" break; case 5: -#line 200 "getdate.y" /* yacc.c:1646 */ - { +#line 200 "getdate.y" + { yyHaveZone++; } -#line 1444 "getdate.c" /* yacc.c:1646 */ +#line 1540 "getdate.c" break; case 6: -#line 203 "getdate.y" /* yacc.c:1646 */ - { +#line 203 "getdate.y" + { yyHaveDate++; } -#line 1452 "getdate.c" /* yacc.c:1646 */ +#line 1548 "getdate.c" break; case 7: -#line 206 "getdate.y" /* yacc.c:1646 */ - { +#line 206 "getdate.y" + { yyHaveDay++; } -#line 1460 "getdate.c" /* yacc.c:1646 */ +#line 1556 "getdate.c" break; case 8: -#line 209 "getdate.y" /* yacc.c:1646 */ - { +#line 209 "getdate.y" + { yyHaveRel++; } -#line 1468 "getdate.c" /* yacc.c:1646 */ +#line 1564 "getdate.c" break; case 10: -#line 215 "getdate.y" /* yacc.c:1646 */ - { +#line 215 "getdate.y" + { yyHour = (yyvsp[-1].Number); yyMinutes = 0; yySeconds = 0; yyMeridian = (yyvsp[0].Meridian); } -#line 1479 "getdate.c" /* yacc.c:1646 */ +#line 1575 "getdate.c" break; case 11: -#line 221 "getdate.y" /* yacc.c:1646 */ - { +#line 221 "getdate.y" + { yyHour = (yyvsp[-3].Number); yyMinutes = (yyvsp[-1].Number); yySeconds = 0; yyMeridian = (yyvsp[0].Meridian); } -#line 1490 "getdate.c" /* yacc.c:1646 */ +#line 1586 "getdate.c" break; case 12: -#line 227 "getdate.y" /* yacc.c:1646 */ - { +#line 227 "getdate.y" + { yyHour = (yyvsp[-3].Number); yyMinutes = (yyvsp[-1].Number); yyMeridian = MER24; @@ -1500,23 +1596,23 @@ ? -(yyvsp[0].Number) % 100 + (-(yyvsp[0].Number) / 100) * 60 : - ((yyvsp[0].Number) % 100 + ((yyvsp[0].Number) / 100) * 60)); } -#line 1504 "getdate.c" /* yacc.c:1646 */ +#line 1600 "getdate.c" break; case 13: -#line 236 "getdate.y" /* yacc.c:1646 */ - { +#line 236 "getdate.y" + { yyHour = (yyvsp[-5].Number); yyMinutes = (yyvsp[-3].Number); yySeconds = (yyvsp[-1].Number); yyMeridian = (yyvsp[0].Meridian); } -#line 1515 "getdate.c" /* yacc.c:1646 */ +#line 1611 "getdate.c" break; case 14: -#line 242 "getdate.y" /* yacc.c:1646 */ - { +#line 242 "getdate.y" + { yyHour = (yyvsp[-5].Number); yyMinutes = (yyvsp[-3].Number); yySeconds = (yyvsp[-1].Number); @@ -1526,72 +1622,72 @@ ? -(yyvsp[0].Number) % 100 + (-(yyvsp[0].Number) / 100) * 60 : - ((yyvsp[0].Number) % 100 + ((yyvsp[0].Number) / 100) * 60)); } -#line 1530 "getdate.c" /* yacc.c:1646 */ +#line 1626 "getdate.c" break; case 15: -#line 254 "getdate.y" /* yacc.c:1646 */ - { +#line 254 "getdate.y" + { yyTimezone = (yyvsp[0].Number); } -#line 1538 "getdate.c" /* yacc.c:1646 */ +#line 1634 "getdate.c" break; case 16: -#line 257 "getdate.y" /* yacc.c:1646 */ - { +#line 257 "getdate.y" + { yyTimezone = (yyvsp[0].Number) - 60; } -#line 1546 "getdate.c" /* yacc.c:1646 */ +#line 1642 "getdate.c" break; case 17: -#line 261 "getdate.y" /* yacc.c:1646 */ - { +#line 261 "getdate.y" + { yyTimezone = (yyvsp[-1].Number) - 60; } -#line 1554 "getdate.c" /* yacc.c:1646 */ +#line 1650 "getdate.c" break; case 18: -#line 266 "getdate.y" /* yacc.c:1646 */ - { +#line 266 "getdate.y" + { yyDayOrdinal = 1; yyDayNumber = (yyvsp[0].Number); } -#line 1563 "getdate.c" /* yacc.c:1646 */ +#line 1659 "getdate.c" break; case 19: -#line 270 "getdate.y" /* yacc.c:1646 */ - { +#line 270 "getdate.y" + { yyDayOrdinal = 1; yyDayNumber = (yyvsp[-1].Number); } -#line 1572 "getdate.c" /* yacc.c:1646 */ +#line 1668 "getdate.c" break; case 20: -#line 274 "getdate.y" /* yacc.c:1646 */ - { +#line 274 "getdate.y" + { yyDayOrdinal = (yyvsp[-1].Number); yyDayNumber = (yyvsp[0].Number); } -#line 1581 "getdate.c" /* yacc.c:1646 */ +#line 1677 "getdate.c" break; case 21: -#line 280 "getdate.y" /* yacc.c:1646 */ - { +#line 280 "getdate.y" + { yyMonth = (yyvsp[-2].Number); yyDay = (yyvsp[0].Number); } -#line 1590 "getdate.c" /* yacc.c:1646 */ +#line 1686 "getdate.c" break; case 22: -#line 284 "getdate.y" /* yacc.c:1646 */ - { +#line 284 "getdate.y" + { /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY. The goal in recognizing YYYY/MM/DD is solely to support legacy machine-generated dates like those in an RCS log listing. If @@ -1609,72 +1705,72 @@ yyYear = (yyvsp[0].Number); } } -#line 1613 "getdate.c" /* yacc.c:1646 */ +#line 1709 "getdate.c" break; case 23: -#line 302 "getdate.y" /* yacc.c:1646 */ - { +#line 302 "getdate.y" + { /* ISO 8601 format. yyyy-mm-dd. */ yyYear = (yyvsp[-2].Number); yyMonth = -(yyvsp[-1].Number); yyDay = -(yyvsp[0].Number); } -#line 1624 "getdate.c" /* yacc.c:1646 */ +#line 1720 "getdate.c" break; case 24: -#line 308 "getdate.y" /* yacc.c:1646 */ - { +#line 308 "getdate.y" + { /* e.g. 17-JUN-1992. */ yyDay = (yyvsp[-2].Number); yyMonth = (yyvsp[-1].Number); yyYear = -(yyvsp[0].Number); } -#line 1635 "getdate.c" /* yacc.c:1646 */ +#line 1731 "getdate.c" break; case 25: -#line 314 "getdate.y" /* yacc.c:1646 */ - { +#line 314 "getdate.y" + { yyMonth = (yyvsp[-1].Number); yyDay = (yyvsp[0].Number); } -#line 1644 "getdate.c" /* yacc.c:1646 */ +#line 1740 "getdate.c" break; case 26: -#line 318 "getdate.y" /* yacc.c:1646 */ - { +#line 318 "getdate.y" + { yyMonth = (yyvsp[-3].Number); yyDay = (yyvsp[-2].Number); yyYear = (yyvsp[0].Number); } -#line 1654 "getdate.c" /* yacc.c:1646 */ +#line 1750 "getdate.c" break; case 27: -#line 323 "getdate.y" /* yacc.c:1646 */ - { +#line 323 "getdate.y" + { yyMonth = (yyvsp[0].Number); yyDay = (yyvsp[-1].Number); } -#line 1663 "getdate.c" /* yacc.c:1646 */ +#line 1759 "getdate.c" break; case 28: -#line 327 "getdate.y" /* yacc.c:1646 */ - { +#line 327 "getdate.y" + { yyMonth = (yyvsp[-1].Number); yyDay = (yyvsp[-2].Number); yyYear = (yyvsp[0].Number); } -#line 1673 "getdate.c" /* yacc.c:1646 */ +#line 1769 "getdate.c" break; case 29: -#line 334 "getdate.y" /* yacc.c:1646 */ - { +#line 334 "getdate.y" + { yyRelSeconds = -yyRelSeconds; yyRelMinutes = -yyRelMinutes; yyRelHour = -yyRelHour; @@ -1682,156 +1778,156 @@ yyRelMonth = -yyRelMonth; yyRelYear = -yyRelYear; } -#line 1686 "getdate.c" /* yacc.c:1646 */ +#line 1782 "getdate.c" break; case 31: -#line 345 "getdate.y" /* yacc.c:1646 */ - { +#line 345 "getdate.y" + { yyRelYear += (yyvsp[-1].Number) * (yyvsp[0].Number); } -#line 1694 "getdate.c" /* yacc.c:1646 */ +#line 1790 "getdate.c" break; case 32: -#line 348 "getdate.y" /* yacc.c:1646 */ - { +#line 348 "getdate.y" + { yyRelYear += (yyvsp[-1].Number) * (yyvsp[0].Number); } -#line 1702 "getdate.c" /* yacc.c:1646 */ +#line 1798 "getdate.c" break; case 33: -#line 351 "getdate.y" /* yacc.c:1646 */ - { +#line 351 "getdate.y" + { yyRelYear += (yyvsp[0].Number); } -#line 1710 "getdate.c" /* yacc.c:1646 */ +#line 1806 "getdate.c" break; case 34: -#line 354 "getdate.y" /* yacc.c:1646 */ - { +#line 354 "getdate.y" + { yyRelMonth += (yyvsp[-1].Number) * (yyvsp[0].Number); } -#line 1718 "getdate.c" /* yacc.c:1646 */ +#line 1814 "getdate.c" break; case 35: -#line 357 "getdate.y" /* yacc.c:1646 */ - { +#line 357 "getdate.y" + { yyRelMonth += (yyvsp[-1].Number) * (yyvsp[0].Number); } -#line 1726 "getdate.c" /* yacc.c:1646 */ +#line 1822 "getdate.c" break; case 36: -#line 360 "getdate.y" /* yacc.c:1646 */ - { +#line 360 "getdate.y" + { yyRelMonth += (yyvsp[0].Number); } -#line 1734 "getdate.c" /* yacc.c:1646 */ +#line 1830 "getdate.c" break; case 37: -#line 363 "getdate.y" /* yacc.c:1646 */ - { +#line 363 "getdate.y" + { yyRelDay += (yyvsp[-1].Number) * (yyvsp[0].Number); } -#line 1742 "getdate.c" /* yacc.c:1646 */ +#line 1838 "getdate.c" break; case 38: -#line 366 "getdate.y" /* yacc.c:1646 */ - { +#line 366 "getdate.y" + { yyRelDay += (yyvsp[-1].Number) * (yyvsp[0].Number); } -#line 1750 "getdate.c" /* yacc.c:1646 */ +#line 1846 "getdate.c" break; case 39: -#line 369 "getdate.y" /* yacc.c:1646 */ - { +#line 369 "getdate.y" + { yyRelDay += (yyvsp[0].Number); } -#line 1758 "getdate.c" /* yacc.c:1646 */ +#line 1854 "getdate.c" break; case 40: -#line 372 "getdate.y" /* yacc.c:1646 */ - { +#line 372 "getdate.y" + { yyRelHour += (yyvsp[-1].Number) * (yyvsp[0].Number); } -#line 1766 "getdate.c" /* yacc.c:1646 */ +#line 1862 "getdate.c" break; case 41: -#line 375 "getdate.y" /* yacc.c:1646 */ - { +#line 375 "getdate.y" + { yyRelHour += (yyvsp[-1].Number) * (yyvsp[0].Number); } -#line 1774 "getdate.c" /* yacc.c:1646 */ +#line 1870 "getdate.c" break; case 42: -#line 378 "getdate.y" /* yacc.c:1646 */ - { +#line 378 "getdate.y" + { yyRelHour += (yyvsp[0].Number); } -#line 1782 "getdate.c" /* yacc.c:1646 */ +#line 1878 "getdate.c" break; case 43: -#line 381 "getdate.y" /* yacc.c:1646 */ - { +#line 381 "getdate.y" + { yyRelMinutes += (yyvsp[-1].Number) * (yyvsp[0].Number); } -#line 1790 "getdate.c" /* yacc.c:1646 */ +#line 1886 "getdate.c" break; case 44: -#line 384 "getdate.y" /* yacc.c:1646 */ - { +#line 384 "getdate.y" + { yyRelMinutes += (yyvsp[-1].Number) * (yyvsp[0].Number); } -#line 1798 "getdate.c" /* yacc.c:1646 */ +#line 1894 "getdate.c" break; case 45: -#line 387 "getdate.y" /* yacc.c:1646 */ - { +#line 387 "getdate.y" + { yyRelMinutes += (yyvsp[0].Number); } -#line 1806 "getdate.c" /* yacc.c:1646 */ +#line 1902 "getdate.c" break; case 46: -#line 390 "getdate.y" /* yacc.c:1646 */ - { +#line 390 "getdate.y" + { yyRelSeconds += (yyvsp[-1].Number) * (yyvsp[0].Number); } -#line 1814 "getdate.c" /* yacc.c:1646 */ +#line 1910 "getdate.c" break; case 47: -#line 393 "getdate.y" /* yacc.c:1646 */ - { +#line 393 "getdate.y" + { yyRelSeconds += (yyvsp[-1].Number) * (yyvsp[0].Number); } -#line 1822 "getdate.c" /* yacc.c:1646 */ +#line 1918 "getdate.c" break; case 48: -#line 396 "getdate.y" /* yacc.c:1646 */ - { +#line 396 "getdate.y" + { yyRelSeconds += (yyvsp[0].Number); } -#line 1830 "getdate.c" /* yacc.c:1646 */ +#line 1926 "getdate.c" break; case 49: -#line 402 "getdate.y" /* yacc.c:1646 */ - { +#line 402 "getdate.y" + { if (yyHaveTime && yyHaveDate && !yyHaveRel) yyYear = (yyvsp[0].Number); else @@ -1861,27 +1957,28 @@ } } } -#line 1865 "getdate.c" /* yacc.c:1646 */ +#line 1961 "getdate.c" break; case 50: -#line 435 "getdate.y" /* yacc.c:1646 */ - { +#line 435 "getdate.y" + { (yyval.Meridian) = MER24; } -#line 1873 "getdate.c" /* yacc.c:1646 */ +#line 1969 "getdate.c" break; case 51: -#line 439 "getdate.y" /* yacc.c:1646 */ - { +#line 439 "getdate.y" + { (yyval.Meridian) = (yyvsp[0].Meridian); } -#line 1881 "getdate.c" /* yacc.c:1646 */ +#line 1977 "getdate.c" break; -#line 1885 "getdate.c" /* yacc.c:1646 */ +#line 1981 "getdate.c" + default: break; } /* User semantic actions sometimes alter yychar, and that requires @@ -1906,14 +2003,13 @@ /* Now 'shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ - - yyn = yyr1[yyn]; - - yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; - if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) - yystate = yytable[yystate]; - else - yystate = yydefgoto[yyn - YYNTOKENS]; + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } goto yynewstate; @@ -1945,7 +2041,7 @@ { if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); - yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); if (!yymsg) { yymsg = yymsgbuf; @@ -1996,12 +2092,10 @@ | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: - - /* Pacify compilers like GCC when the user code never invokes - YYERROR and the label yyerrorlab therefore never appears in user - code. */ - if (/*CONSTCOND*/ 0) - goto yyerrorlab; + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ @@ -2063,6 +2157,7 @@ yyresult = 0; goto yyreturn; + /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ @@ -2070,6 +2165,7 @@ yyresult = 1; goto yyreturn; + #if !defined yyoverflow || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | @@ -2080,6 +2176,10 @@ /* Fall through. */ #endif + +/*-----------------------------------------------------. +| yyreturn -- parsing is finished, return the result. | +`-----------------------------------------------------*/ yyreturn: if (yychar != YYEMPTY) { @@ -2109,7 +2209,7 @@ #endif return yyresult; } -#line 444 "getdate.y" /* yacc.c:1906 */ +#line 444 "getdate.y" /* Include this file down here because bison inserts code above which diff -Nru chrony-3.5/hash.h chrony-4.1/hash.h --- chrony-3.5/hash.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/hash.h 2021-05-12 11:06:15.000000000 +0000 @@ -31,12 +31,25 @@ /* length of hash values produced by SHA512 */ #define MAX_HASH_LENGTH 64 -extern int HSH_GetHashId(const char *name); +typedef enum { + HSH_INVALID = 0, + HSH_MD5 = 1, + HSH_SHA1 = 2, + HSH_SHA256 = 3, + HSH_SHA384 = 4, + HSH_SHA512 = 5, + HSH_SHA3_224 = 6, + HSH_SHA3_256 = 7, + HSH_SHA3_384 = 8, + HSH_SHA3_512 = 9, + HSH_TIGER = 10, + HSH_WHIRLPOOL = 11, +} HSH_Algorithm; -extern unsigned int HSH_Hash(int id, - const unsigned char *in1, unsigned int in1_len, - const unsigned char *in2, unsigned int in2_len, - unsigned char *out, unsigned int out_len); +extern int HSH_GetHashId(HSH_Algorithm algorithm); + +extern int HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len, + unsigned char *out, int out_len); extern void HSH_Finalise(void); diff -Nru chrony-3.5/hash_intmd5.c chrony-4.1/hash_intmd5.c --- chrony-3.5/hash_intmd5.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/hash_intmd5.c 2021-05-12 11:06:15.000000000 +0000 @@ -36,20 +36,22 @@ static MD5_CTX ctx; int -HSH_GetHashId(const char *name) +HSH_GetHashId(HSH_Algorithm algorithm) { /* only MD5 is supported */ - if (strcmp(name, "MD5")) + if (algorithm != HSH_MD5) return -1; return 0; } -unsigned int -HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len, - const unsigned char *in2, unsigned int in2_len, - unsigned char *out, unsigned int out_len) +int +HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len, + unsigned char *out, int out_len) { + if (in1_len < 0 || in2_len < 0 || out_len < 0) + return 0; + MD5Init(&ctx); MD5Update(&ctx, in1, in1_len); if (in2) diff -Nru chrony-3.5/hash_nettle.c chrony-4.1/hash_nettle.c --- chrony-3.5/hash_nettle.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/hash_nettle.c 2021-05-12 11:06:15.000000000 +0000 @@ -35,37 +35,36 @@ #include "memory.h" struct hash { - const char *name; + const HSH_Algorithm algorithm; const char *int_name; const struct nettle_hash *nettle_hash; void *context; }; static struct hash hashes[] = { - { "MD5", "md5", NULL, NULL }, - { "RMD160", "ripemd160", NULL, NULL }, - { "SHA1", "sha1", NULL, NULL }, - { "SHA256", "sha256", NULL, NULL }, - { "SHA384", "sha384", NULL, NULL }, - { "SHA512", "sha512", NULL, NULL }, - { "SHA3-224", "sha3_224", NULL, NULL }, - { "SHA3-256", "sha3_256", NULL, NULL }, - { "SHA3-384", "sha3_384", NULL, NULL }, - { "SHA3-512", "sha3_512", NULL, NULL }, - { NULL, NULL, NULL, NULL } + { HSH_MD5, "md5", NULL, NULL }, + { HSH_SHA1, "sha1", NULL, NULL }, + { HSH_SHA256, "sha256", NULL, NULL }, + { HSH_SHA384, "sha384", NULL, NULL }, + { HSH_SHA512, "sha512", NULL, NULL }, + { HSH_SHA3_224, "sha3_224", NULL, NULL }, + { HSH_SHA3_256, "sha3_256", NULL, NULL }, + { HSH_SHA3_384, "sha3_384", NULL, NULL }, + { HSH_SHA3_512, "sha3_512", NULL, NULL }, + { 0, NULL, NULL, NULL } }; int -HSH_GetHashId(const char *name) +HSH_GetHashId(HSH_Algorithm algorithm) { int id, nid; - for (id = 0; hashes[id].name; id++) { - if (!strcmp(name, hashes[id].name)) + for (id = 0; hashes[id].algorithm != 0; id++) { + if (hashes[id].algorithm == algorithm) break; } - if (!hashes[id].name) + if (hashes[id].algorithm == 0) return -1; if (hashes[id].context) @@ -85,14 +84,16 @@ return id; } -unsigned int -HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len, - const unsigned char *in2, unsigned int in2_len, - unsigned char *out, unsigned int out_len) +int +HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len, + unsigned char *out, int out_len) { const struct nettle_hash *hash; void *context; + if (in1_len < 0 || in2_len < 0 || out_len < 0) + return 0; + hash = hashes[id].nettle_hash; context = hashes[id].context; @@ -113,7 +114,7 @@ { int i; - for (i = 0; hashes[i].name; i++) { + for (i = 0; hashes[i].algorithm != 0; i++) { if (hashes[i].context) Free(hashes[i].context); } diff -Nru chrony-3.5/hash_nss.c chrony-4.1/hash_nss.c --- chrony-3.5/hash_nss.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/hash_nss.c 2021-05-12 11:06:15.000000000 +0000 @@ -38,30 +38,30 @@ struct hash { HASH_HashType type; - const char *name; + HSH_Algorithm algorithm; NSSLOWHASHContext *context; }; static struct hash hashes[] = { - { HASH_AlgMD5, "MD5", NULL }, - { HASH_AlgSHA1, "SHA1", NULL }, - { HASH_AlgSHA256, "SHA256", NULL }, - { HASH_AlgSHA384, "SHA384", NULL }, - { HASH_AlgSHA512, "SHA512", NULL }, - { 0, NULL, NULL } + { HASH_AlgMD5, HSH_MD5, NULL }, + { HASH_AlgSHA1, HSH_SHA1, NULL }, + { HASH_AlgSHA256, HSH_SHA256, NULL }, + { HASH_AlgSHA384, HSH_SHA384, NULL }, + { HASH_AlgSHA512, HSH_SHA512, NULL }, + { 0, 0, NULL } }; int -HSH_GetHashId(const char *name) +HSH_GetHashId(HSH_Algorithm algorithm) { int i; - for (i = 0; hashes[i].name; i++) { - if (!strcmp(name, hashes[i].name)) + for (i = 0; hashes[i].algorithm != 0; i++) { + if (hashes[i].algorithm == algorithm) break; } - if (!hashes[i].name) + if (hashes[i].algorithm == 0) return -1; /* not found */ if (!ictx && !(ictx = NSSLOW_Init())) @@ -74,14 +74,16 @@ return i; } -unsigned int -HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len, - const unsigned char *in2, unsigned int in2_len, - unsigned char *out, unsigned int out_len) +int +HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len, + unsigned char *out, int out_len) { unsigned char buf[MAX_HASH_LENGTH]; unsigned int ret = 0; + if (in1_len < 0 || in2_len < 0 || out_len < 0) + return 0; + NSSLOWHASH_Begin(hashes[id].context); NSSLOWHASH_Update(hashes[id].context, in1, in1_len); if (in2) @@ -99,7 +101,7 @@ { int i; - for (i = 0; hashes[i].name; i++) { + for (i = 0; hashes[i].algorithm != 0; i++) { if (hashes[i].context) NSSLOWHASH_Destroy(hashes[i].context); } diff -Nru chrony-3.5/hash_tomcrypt.c chrony-4.1/hash_tomcrypt.c --- chrony-3.5/hash_tomcrypt.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/hash_tomcrypt.c 2021-05-12 11:06:15.000000000 +0000 @@ -32,63 +32,51 @@ #include "util.h" struct hash { - const char *name; + HSH_Algorithm algorithm; const char *int_name; const struct ltc_hash_descriptor *desc; }; static const struct hash hashes[] = { - { "MD5", "md5", &md5_desc }, -#ifdef LTC_RIPEMD128 - { "RMD128", "rmd128", &rmd128_desc }, -#endif -#ifdef LTC_RIPEMD160 - { "RMD160", "rmd160", &rmd160_desc }, -#endif -#ifdef LTC_RIPEMD256 - { "RMD256", "rmd256", &rmd256_desc }, -#endif -#ifdef LTC_RIPEMD320 - { "RMD320", "rmd320", &rmd320_desc }, -#endif + { HSH_MD5, "md5", &md5_desc }, #ifdef LTC_SHA1 - { "SHA1", "sha1", &sha1_desc }, + { HSH_SHA1, "sha1", &sha1_desc }, #endif #ifdef LTC_SHA256 - { "SHA256", "sha256", &sha256_desc }, + { HSH_SHA256, "sha256", &sha256_desc }, #endif #ifdef LTC_SHA384 - { "SHA384", "sha384", &sha384_desc }, + { HSH_SHA384, "sha384", &sha384_desc }, #endif #ifdef LTC_SHA512 - { "SHA512", "sha512", &sha512_desc }, + { HSH_SHA512, "sha512", &sha512_desc }, #endif #ifdef LTC_SHA3 - { "SHA3-224", "sha3-224", &sha3_224_desc }, - { "SHA3-256", "sha3-256", &sha3_256_desc }, - { "SHA3-384", "sha3-384", &sha3_384_desc }, - { "SHA3-512", "sha3-512", &sha3_512_desc }, + { HSH_SHA3_224, "sha3-224", &sha3_224_desc }, + { HSH_SHA3_256, "sha3-256", &sha3_256_desc }, + { HSH_SHA3_384, "sha3-384", &sha3_384_desc }, + { HSH_SHA3_512, "sha3-512", &sha3_512_desc }, #endif #ifdef LTC_TIGER - { "TIGER", "tiger", &tiger_desc }, + { HSH_TIGER, "tiger", &tiger_desc }, #endif #ifdef LTC_WHIRLPOOL - { "WHIRLPOOL", "whirlpool", &whirlpool_desc }, + { HSH_WHIRLPOOL, "whirlpool", &whirlpool_desc }, #endif - { NULL, NULL, NULL } + { 0, NULL, NULL } }; int -HSH_GetHashId(const char *name) +HSH_GetHashId(HSH_Algorithm algorithm) { int i, h; - for (i = 0; hashes[i].name; i++) { - if (!strcmp(name, hashes[i].name)) + for (i = 0; hashes[i].algorithm != 0; i++) { + if (hashes[i].algorithm == algorithm) break; } - if (!hashes[i].name) + if (hashes[i].algorithm == 0) return -1; /* not found */ h = find_hash(hashes[i].int_name); @@ -101,15 +89,17 @@ return find_hash(hashes[i].int_name); } -unsigned int -HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len, - const unsigned char *in2, unsigned int in2_len, - unsigned char *out, unsigned int out_len) +int +HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len, + unsigned char *out, int out_len) { unsigned char buf[MAX_HASH_LENGTH]; unsigned long len; int r; + if (in1_len < 0 || in2_len < 0 || out_len < 0) + return 0; + len = sizeof (buf); if (in2) r = hash_memory_multi(id, buf, &len, diff -Nru chrony-3.5/INSTALL chrony-4.1/INSTALL --- chrony-3.5/INSTALL 2019-05-14 11:00:05.000000000 +0000 +++ chrony-4.1/INSTALL 2021-05-13 10:48:12.000000000 +0000 @@ -4,26 +4,34 @@ code is supplied in the form of a gzipped tar file, which unpacks to a subdirectory identifying the name and version of the program. -The following programs and libraries with their development files are needed to -build chrony: +A C compiler (e.g. gcc or clang) and GNU Make are needed to build chrony. The +following libraries with their development files, and programs, are needed to +enable optional features: - o C compiler (gcc or clang recommended) + o pkg-config: detection of development libraries - o GNU Make + o Nettle, NSS, or LibTomCrypt: secure hash functions (SECHASH) - o Nettle, NSS, or LibTomCrypt (optional) + o libcap: dropping root privileges on Linux (DROPROOT) - o Editline (optional) + o libseccomp: system call filter on Linux (SCFILTER) - o libcap (Linux only, optional) + o GnuTLS and Nettle: Network Time Security (NTS) - o libseccomp (Linux only, optional) + o Editline: line editing in chronyc (READLINE) - o timepps.h header (optional) + o timepps.h header: PPS reference clock - o Asciidoctor (for HTML documentation) + o Asciidoctor: documentation in HTML format - o Bash (for testing) + o Bash: test suite + +The following programs are needed when building chrony from the git repository +instead of a released tar file: + + o Asciidoctor: manual pages + + o Bison: parser for chronyc settime command After unpacking the source code, change directory into it, and type @@ -71,9 +79,9 @@ don't want to enable the support, specify the --disable-sechash flag to configure. -If development files for the editline or readline library are available, -chronyc will be built with line editing support. If you don't want this, -specify the --disable-readline flag to configure. +If development files for the editline library are available, chronyc will be +built with line editing support. If you don't want this, specify the +--disable-readline flag to configure. If a timepps.h header is available (e.g. from the LinuxPPS project), chronyd will be built with PPS API reference clock driver. If the header is installed @@ -129,44 +137,6 @@ attack surface and possibly prevent kernel exploits from chronyd if it is compromised. -Support for line editing libraries - -chronyc can be built with support for line editing, this allows you to use the -cursor keys to replay and edit old commands. Two libraries are supported which -provide such functionality, editline and GNU readline. - -Please note that readline since version 6.0 is licensed under GPLv3+ which is -incompatible with chrony's license GPLv2. You should use editline instead if -you don't want to use older readline versions. - -The configure script will automatically enable the line editing support if one -of the supported libraries is available. If they are both available, the -editline library will be used. - -If you don't want to use it (in which case chronyc will use a minimal command -line interface), invoke configure like this: - -./configure --disable-readline other-options... - -If you have editline, readline or ncurses installed in locations that aren't -normally searched by the compiler and linker, you need to use extra options: - ---with-readline-includes=directory_name - - This defines the name of the directory above the one where readline.h is. - readline.h is assumed to be in editline or readline subdirectory of the - named directory. - ---with-readline-library=directory_name - - This defines the directory containing the libedit.a or libedit.so file, or - libreadline.a or libreadline.so file. - ---with-ncurses-library=directory_name - - This defines the directory containing the libncurses.a or libncurses.so - file. - Extra options for package builders The configure and make procedures have some extra options that may be useful if @@ -192,4 +162,4 @@ to build a package. When untarred within the root directory, this will install the files to the intended final locations. -Last updated 2019-05-10 12:22:57 CEST +Last updated 2021-05-12 13:06:15 +0200 diff -Nru chrony-3.5/keys.c chrony-4.1/keys.c --- chrony-3.5/keys.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/keys.c 2021-05-12 11:06:15.000000000 +0000 @@ -3,7 +3,7 @@ ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 - * Copyright (C) Miroslav Lichvar 2012-2016 + * Copyright (C) Miroslav Lichvar 2012-2016, 2019-2020 * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -32,6 +32,7 @@ #include "array.h" #include "keys.h" +#include "cmac.h" #include "cmdparse.h" #include "conf.h" #include "memory.h" @@ -42,12 +43,23 @@ /* Consider 80 bits as the absolute minimum for a secure key */ #define MIN_SECURE_KEY_LENGTH 10 +typedef enum { + NTP_MAC, + CMAC, +} KeyClass; + typedef struct { uint32_t id; - char *val; - int len; - int hash_id; - int auth_delay; + int type; + int length; + KeyClass class; + union { + struct { + unsigned char *value; + int hash_id; + } ntp_mac; + CMC_Instance cmac; + } data; } Key; static ARR_Instance keys; @@ -62,9 +74,21 @@ free_keys(void) { unsigned int i; + Key *key; - for (i = 0; i < ARR_GetSize(keys); i++) - Free(((Key *)ARR_GetElement(keys, i))->val); + for (i = 0; i < ARR_GetSize(keys); i++) { + key = ARR_GetElement(keys, i); + switch (key->class) { + case NTP_MAC: + Free(key->data.ntp_mac.value); + break; + case CMAC: + CMC_DestroyInstance(key->data.cmac); + break; + default: + assert(0); + } + } ARR_SetSize(keys, 0); cache_valid = 0; @@ -98,62 +122,18 @@ } /* ================================================== */ +/* Decode key encoded in ASCII or HEX */ static int -determine_hash_delay(uint32_t key_id) -{ - NTP_Packet pkt; - struct timespec before, after; - double diff, min_diff; - int i, nsecs; - - memset(&pkt, 0, sizeof (pkt)); - - for (i = 0; i < 10; i++) { - LCL_ReadRawTime(&before); - KEY_GenerateAuth(key_id, (unsigned char *)&pkt, NTP_NORMAL_PACKET_LENGTH, - (unsigned char *)&pkt.auth_data, sizeof (pkt.auth_data)); - LCL_ReadRawTime(&after); - - diff = UTI_DiffTimespecsToDouble(&after, &before); - - if (i == 0 || min_diff > diff) - min_diff = diff; - } - - /* Add on a bit extra to allow for copying, conversions etc */ - nsecs = 1.0625e9 * min_diff; - - DEBUG_LOG("authentication delay for key %"PRIu32": %d nsecs", key_id, nsecs); - - return nsecs; -} - -/* ================================================== */ -/* Decode password encoded in ASCII or HEX */ - -static int -decode_password(char *key) +decode_key(char *key) { - int i, j, len = strlen(key); - char buf[3], *p; + int len = strlen(key); if (!strncmp(key, "ASCII:", 6)) { memmove(key, key + 6, len - 6); return len - 6; } else if (!strncmp(key, "HEX:", 4)) { - if ((len - 4) % 2) - return 0; - - for (i = 0, j = 4; j + 1 < len; i++, j += 2) { - buf[0] = key[j], buf[1] = key[j + 1], buf[2] = '\0'; - key[i] = strtol(buf, &p, 16); - - if (p != buf + 2) - return 0; - } - - return i; + return UTI_HexToBytes(key + 4, key, len); } else { /* assume ASCII */ return len; @@ -185,11 +165,13 @@ void KEY_Reload(void) { - unsigned int i, line_number; + unsigned int i, line_number, key_length, cmac_key_length; FILE *in; - uint32_t key_id; - char line[2048], *keyval, *key_file; - const char *hashname; + char line[2048], *key_file, *key_value; + const char *key_type; + HSH_Algorithm hash_algorithm; + CMC_Algorithm cmac_algorithm; + int hash_id; Key key; free_keys(); @@ -200,7 +182,7 @@ if (!key_file) return; - in = fopen(key_file, "r"); + in = UTI_OpenFile(NULL, key_file, NULL, 'r', 0); if (!in) { LOG(LOGS_WARN, "Could not open keyfile %s", key_file); return; @@ -213,26 +195,56 @@ if (!*line) continue; - if (!CPS_ParseKey(line, &key_id, &hashname, &keyval)) { + memset(&key, 0, sizeof (key)); + + if (!CPS_ParseKey(line, &key.id, &key_type, &key_value)) { LOG(LOGS_WARN, "Could not parse key at line %u in file %s", line_number, key_file); continue; } - key.hash_id = HSH_GetHashId(hashname); - if (key.hash_id < 0) { - LOG(LOGS_WARN, "Unknown hash function in key %"PRIu32, key_id); + key_length = decode_key(key_value); + if (key_length == 0) { + LOG(LOGS_WARN, "Could not decode key %"PRIu32, key.id); continue; } - key.len = decode_password(keyval); - if (!key.len) { - LOG(LOGS_WARN, "Could not decode password in key %"PRIu32, key_id); + hash_algorithm = UTI_HashNameToAlgorithm(key_type); + cmac_algorithm = UTI_CmacNameToAlgorithm(key_type); + + if (hash_algorithm != 0) { + hash_id = HSH_GetHashId(hash_algorithm); + if (hash_id < 0) { + LOG(LOGS_WARN, "Unsupported %s in key %"PRIu32, "hash function", key.id); + continue; + } + key.class = NTP_MAC; + key.type = hash_algorithm; + key.length = key_length; + key.data.ntp_mac.value = MallocArray(unsigned char, key_length); + memcpy(key.data.ntp_mac.value, key_value, key_length); + key.data.ntp_mac.hash_id = hash_id; + } else if (cmac_algorithm != 0) { + cmac_key_length = CMC_GetKeyLength(cmac_algorithm); + if (cmac_key_length == 0) { + LOG(LOGS_WARN, "Unsupported %s in key %"PRIu32, "cipher", key.id); + continue; + } else if (cmac_key_length != key_length) { + LOG(LOGS_WARN, "Invalid length of %s key %"PRIu32" (expected %u bits)", + key_type, key.id, 8 * cmac_key_length); + continue; + } + + key.class = CMAC; + key.type = cmac_algorithm; + key.length = key_length; + key.data.cmac = CMC_CreateInstance(cmac_algorithm, (unsigned char *)key_value, + key_length); + assert(key.data.cmac); + } else { + LOG(LOGS_WARN, "Invalid type in key %"PRIu32, key.id); continue; } - key.id = key_id; - key.val = MallocArray(char, key.len); - memcpy(key.val, keyval, key.len); ARR_AppendElement(keys, &key); } @@ -251,9 +263,6 @@ /* Erase any passwords from stack */ memset(line, 0, sizeof (line)); - - for (i = 0; i < ARR_GetSize(keys); i++) - get_key(i)->auth_delay = determine_hash_delay(get_key(i)->id); } /* ================================================== */ @@ -310,8 +319,9 @@ /* ================================================== */ int -KEY_GetAuthDelay(uint32_t key_id) +KEY_GetAuthLength(uint32_t key_id) { + unsigned char buf[MAX_HASH_LENGTH]; Key *key; key = get_key_by_id(key_id); @@ -319,15 +329,22 @@ if (!key) return 0; - return key->auth_delay; + switch (key->class) { + case NTP_MAC: + return HSH_Hash(key->data.ntp_mac.hash_id, buf, 0, buf, 0, buf, sizeof (buf)); + case CMAC: + return CMC_Hash(key->data.cmac, buf, 0, buf, sizeof (buf)); + default: + assert(0); + return 0; + } } /* ================================================== */ int -KEY_GetAuthLength(uint32_t key_id) +KEY_CheckKeyLength(uint32_t key_id) { - unsigned char buf[MAX_HASH_LENGTH]; Key *key; key = get_key_by_id(key_id); @@ -335,13 +352,13 @@ if (!key) return 0; - return HSH_Hash(key->hash_id, buf, 0, buf, 0, buf, sizeof (buf)); + return key->length >= MIN_SECURE_KEY_LENGTH; } /* ================================================== */ int -KEY_CheckKeyLength(uint32_t key_id) +KEY_GetKeyInfo(uint32_t key_id, int *type, int *bits) { Key *key; @@ -350,30 +367,38 @@ if (!key) return 0; - return key->len >= MIN_SECURE_KEY_LENGTH; + *type = key->type; + *bits = 8 * key->length; + + return 1; } /* ================================================== */ static int -generate_ntp_auth(int hash_id, const unsigned char *key, int key_len, - const unsigned char *data, int data_len, - unsigned char *auth, int auth_len) +generate_auth(Key *key, const void *data, int data_len, unsigned char *auth, int auth_len) { - return HSH_Hash(hash_id, key, key_len, data, data_len, auth, auth_len); + switch (key->class) { + case NTP_MAC: + return HSH_Hash(key->data.ntp_mac.hash_id, key->data.ntp_mac.value, + key->length, data, data_len, auth, auth_len); + case CMAC: + return CMC_Hash(key->data.cmac, data, data_len, auth, auth_len); + default: + return 0; + } } /* ================================================== */ static int -check_ntp_auth(int hash_id, const unsigned char *key, int key_len, - const unsigned char *data, int data_len, - const unsigned char *auth, int auth_len, int trunc_len) +check_auth(Key *key, const void *data, int data_len, + const unsigned char *auth, int auth_len, int trunc_len) { unsigned char buf[MAX_HASH_LENGTH]; int hash_len; - hash_len = generate_ntp_auth(hash_id, key, key_len, data, data_len, buf, sizeof (buf)); + hash_len = generate_auth(key, data, data_len, buf, sizeof (buf)); return MIN(hash_len, trunc_len) == auth_len && !memcmp(buf, auth, auth_len); } @@ -381,8 +406,8 @@ /* ================================================== */ int -KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len, - unsigned char *auth, int auth_len) +KEY_GenerateAuth(uint32_t key_id, const void *data, int data_len, + unsigned char *auth, int auth_len) { Key *key; @@ -391,14 +416,13 @@ if (!key) return 0; - return generate_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len, - data, data_len, auth, auth_len); + return generate_auth(key, data, data_len, auth, auth_len); } /* ================================================== */ int -KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len, +KEY_CheckAuth(uint32_t key_id, const void *data, int data_len, const unsigned char *auth, int auth_len, int trunc_len) { Key *key; @@ -408,6 +432,5 @@ if (!key) return 0; - return check_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len, - data, data_len, auth, auth_len, trunc_len); + return check_auth(key, data, data_len, auth, auth_len, trunc_len); } diff -Nru chrony-3.5/keys.h chrony-4.1/keys.h --- chrony-3.5/keys.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/keys.h 2021-05-12 11:06:15.000000000 +0000 @@ -34,15 +34,14 @@ extern void KEY_Reload(void); -extern int KEY_GetKey(uint32_t key_id, char **key, int *len); extern int KEY_KeyKnown(uint32_t key_id); -extern int KEY_GetAuthDelay(uint32_t key_id); extern int KEY_GetAuthLength(uint32_t key_id); extern int KEY_CheckKeyLength(uint32_t key_id); +extern int KEY_GetKeyInfo(uint32_t key_id, int *type, int *bits); -extern int KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, - int data_len, unsigned char *auth, int auth_len); -extern int KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len, +extern int KEY_GenerateAuth(uint32_t key_id, const void *data, int data_len, + unsigned char *auth, int auth_len); +extern int KEY_CheckAuth(uint32_t key_id, const void *data, int data_len, const unsigned char *auth, int auth_len, int trunc_len); #endif /* GOT_KEYS_H */ diff -Nru chrony-3.5/local.c chrony-4.1/local.c --- chrony-3.5/local.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/local.c 2021-05-12 11:06:15.000000000 +0000 @@ -108,8 +108,8 @@ #define NSEC_PER_SEC 1000000000 -static void -calculate_sys_precision(void) +static double +measure_clock_precision(void) { struct timespec ts, old_ts; int iters, diff, best; @@ -135,18 +135,7 @@ assert(best > 0); - precision_quantum = 1.0e-9 * best; - - /* Get rounded log2 value of the measured precision */ - precision_log = 0; - while (best < 707106781) { - precision_log--; - best *= 2; - } - - assert(precision_log >= -30); - - DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log); + return 1.0e-9 * best; } /* ================================================== */ @@ -170,7 +159,16 @@ current_freq_ppm = 0.0; temp_comp_ppm = 0.0; - calculate_sys_precision(); + precision_quantum = CNF_GetClockPrecision(); + if (precision_quantum <= 0.0) + precision_quantum = measure_clock_precision(); + + precision_quantum = CLAMP(1.0e-9, precision_quantum, 1.0); + precision_log = round(log(precision_quantum) / log(2.0)); + /* NTP code doesn't support smaller log than -30 */ + assert(precision_log >= -30); + + DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log); /* This is the maximum allowed frequency offset in ppm, the time must never stop or run backwards */ @@ -185,13 +183,11 @@ void LCL_Finalise(void) { - while (change_list.next != &change_list) - LCL_RemoveParameterChangeHandler(change_list.next->handler, - change_list.next->anything); - - while (dispersion_notify_list.next != &dispersion_notify_list) - LCL_RemoveDispersionNotifyHandler(dispersion_notify_list.next->handler, - dispersion_notify_list.next->anything); + /* Make sure all handlers have been removed */ + if (change_list.next != &change_list) + assert(0); + if (dispersion_notify_list.next != &dispersion_notify_list) + assert(0); } /* ================================================== */ @@ -509,7 +505,7 @@ /* ================================================== */ -void +int LCL_AccumulateOffset(double offset, double corr_rate) { struct timespec raw, cooked; @@ -521,12 +517,14 @@ LCL_CookTime(&raw, &cooked, NULL); if (!check_offset(&cooked, offset)) - return; + return 0; (*drv_accrue_offset)(offset, corr_rate); /* Dispatch to all handlers */ invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeAdjust); + + return 1; } /* ================================================== */ @@ -590,7 +588,7 @@ /* ================================================== */ -void +int LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate) { struct timespec raw, cooked; @@ -602,7 +600,7 @@ LCL_CookTime(&raw, &cooked, NULL); if (!check_offset(&cooked, doffset)) - return; + return 0; old_freq_ppm = current_freq_ppm; @@ -624,6 +622,8 @@ /* Dispatch to all handlers */ invoke_parameter_change_handlers(&raw, &cooked, dfreq, doffset, LCL_ChangeAdjust); + + return 1; } /* ================================================== */ @@ -690,6 +690,19 @@ } /* ================================================== */ + +void +LCL_CancelOffsetCorrection(void) +{ + struct timespec raw; + double correction; + + LCL_ReadRawTime(&raw); + LCL_GetOffsetCorrection(&raw, &correction, NULL); + LCL_AccumulateOffset(correction, 0.0); +} + +/* ================================================== */ int LCL_CanSystemLeap(void) diff -Nru chrony-3.5/local.h chrony-4.1/local.h --- chrony-3.5/local.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/local.h 2021-05-12 11:06:15.000000000 +0000 @@ -149,7 +149,7 @@ forwards (i.e. it is currently slow of true time). Provided is also a suggested correction rate (correction time * offset). */ -extern void LCL_AccumulateOffset(double offset, double corr_rate); +extern int LCL_AccumulateOffset(double offset, double corr_rate); /* Routine to apply an immediate offset by doing a sudden step if possible. (Intended for use after an initial estimate of offset has @@ -171,7 +171,7 @@ /* Perform the combination of modifying the frequency and applying a slew, in one easy step */ -extern void LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate); +extern int LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate); /* Routine to read the system precision as a log to base 2 value. */ extern int LCL_GetSysPrecisionAsLog(void); @@ -197,6 +197,9 @@ to a timezone problem. */ extern int LCL_MakeStep(void); +/* Routine to cancel the outstanding system clock correction */ +extern void LCL_CancelOffsetCorrection(void); + /* Check if the system driver supports leap seconds, i.e. LCL_SetSystemLeap does something */ extern int LCL_CanSystemLeap(void); diff -Nru chrony-3.5/logging.c chrony-4.1/logging.c --- chrony-3.5/logging.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/logging.c 2021-05-12 11:06:15.000000000 +0000 @@ -3,7 +3,7 @@ ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 - * Copyright (C) Miroslav Lichvar 2011-2014, 2018 + * Copyright (C) Miroslav Lichvar 2011-2014, 2018-2020 * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -29,26 +29,25 @@ #include "sysincl.h" +#include + #include "conf.h" #include "logging.h" +#include "memory.h" #include "util.h" /* This is used by DEBUG_LOG macro */ -int log_debug_enabled = 0; +LOG_Severity log_min_severity = LOGS_INFO; /* ================================================== */ /* Flag indicating we have initialised */ static int initialised = 0; -static FILE *file_log; +static FILE *file_log = NULL; static int system_log = 0; static int parent_fd = 0; -#define DEBUG_LEVEL_PRINT_FUNCTION 2 -#define DEBUG_LEVEL_PRINT_DEBUG 2 -static int debug_level = 0; - struct LogFile { const char *name; const char *banner; @@ -63,14 +62,18 @@ static struct LogFile logfiles[MAX_FILELOGS]; +/* Global prefix for debug messages */ +static char *debug_prefix; + /* ================================================== */ /* Init function */ void LOG_Initialise(void) { + debug_prefix = Strdup(""); initialised = 1; - file_log = stderr; + LOG_OpenFileLog(NULL); } /* ================================================== */ @@ -87,6 +90,8 @@ LOG_CycleLogFiles(); + Free(debug_prefix); + initialised = 0; } @@ -134,7 +139,9 @@ time_t t; struct tm *tm; - if (!system_log && file_log) { + assert(initialised); + + if (!system_log && file_log && severity >= log_min_severity) { /* Don't clutter up syslog with timestamps and internal debugging info */ time(&t); tm = gmtime(&t); @@ -143,8 +150,8 @@ fprintf(file_log, "%s ", buf); } #if DEBUG > 0 - if (debug_level >= DEBUG_LEVEL_PRINT_FUNCTION) - fprintf(file_log, "%s:%d:(%s) ", filename, line_number, function_name); + if (log_min_severity <= LOGS_DEBUG) + fprintf(file_log, "%s%s:%d:(%s) ", debug_prefix, filename, line_number, function_name); #endif } @@ -157,10 +164,12 @@ case LOGS_INFO: case LOGS_WARN: case LOGS_ERR: - log_message(0, severity, buf); + if (severity >= log_min_severity) + log_message(0, severity, buf); break; case LOGS_FATAL: - log_message(1, severity, buf); + if (severity >= log_min_severity) + log_message(1, severity, buf); /* Send the message also to the foreground process if it is still running, or stderr if it is still open */ @@ -171,6 +180,7 @@ system_log = 0; log_message(1, severity, buf); } + exit(1); break; default: assert(0); @@ -185,9 +195,7 @@ FILE *f; if (log_file) { - f = fopen(log_file, "a"); - if (!f) - LOG_FATAL("Could not open log file %s", log_file); + f = UTI_OpenFile(NULL, log_file, NULL, 'A', 0640); } else { f = stderr; } @@ -213,12 +221,27 @@ /* ================================================== */ -void LOG_SetDebugLevel(int level) +void LOG_SetMinSeverity(LOG_Severity severity) { - debug_level = level; - if (level >= DEBUG_LEVEL_PRINT_DEBUG) { - log_debug_enabled = 1; - } + /* Don't print any debug messages in a non-debug build */ + log_min_severity = CLAMP(DEBUG > 0 ? LOGS_DEBUG : LOGS_INFO, severity, LOGS_FATAL); +} + +/* ================================================== */ + +LOG_Severity +LOG_GetMinSeverity(void) +{ + return log_min_severity; +} + +/* ================================================== */ + +void +LOG_SetDebugPrefix(const char *prefix) +{ + Free(debug_prefix); + debug_prefix = Strdup(prefix); } /* ================================================== */ @@ -246,7 +269,10 @@ LOG_FileID LOG_FileOpen(const char *name, const char *banner) { - assert(n_filelogs < MAX_FILELOGS); + if (n_filelogs >= MAX_FILELOGS) { + assert(0); + return -1; + } logfiles[n_filelogs].name = name; logfiles[n_filelogs].banner = banner; @@ -268,24 +294,20 @@ return; if (!logfiles[id].file) { - char filename[512], *logdir = CNF_GetLogDir(); + char *logdir = CNF_GetLogDir(); - if (logdir[0] == '\0') { + if (!logdir) { LOG(LOGS_WARN, "logdir not specified"); logfiles[id].name = NULL; return; } - if (snprintf(filename, sizeof(filename), "%s/%s.log", - logdir, logfiles[id].name) >= sizeof (filename) || - !(logfiles[id].file = fopen(filename, "a"))) { - LOG(LOGS_WARN, "Could not open log file %s", filename); + logfiles[id].file = UTI_OpenFile(logdir, logfiles[id].name, ".log", 'a', 0644); + if (!logfiles[id].file) { + /* Disable the log */ logfiles[id].name = NULL; return; } - - /* Close on exec */ - UTI_FdSetCloexec(fileno(logfiles[id].file)); } banner = CNF_GetLogBanner(); @@ -293,7 +315,7 @@ char bannerline[256]; int i, bannerlen; - bannerlen = strlen(logfiles[id].banner); + bannerlen = MIN(strlen(logfiles[id].banner), sizeof (bannerline) - 1); for (i = 0; i < bannerlen; i++) bannerline[i] = '='; diff -Nru chrony-3.5/logging.h chrony-4.1/logging.h --- chrony-3.5/logging.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/logging.h 2021-05-12 11:06:15.000000000 +0000 @@ -31,9 +31,6 @@ #include "sysincl.h" -/* Flag indicating whether debug messages are logged */ -extern int log_debug_enabled; - /* Line logging macros. If the compiler is GNU C, we take advantage of being able to get the function name also. */ @@ -55,7 +52,7 @@ #define DEBUG_LOG(...) \ do { \ - if (DEBUG && log_debug_enabled) \ + if (DEBUG && log_min_severity == LOGS_DEBUG) \ LOG_MESSAGE(LOGS_DEBUG, __VA_ARGS__); \ } while (0) @@ -69,13 +66,16 @@ /* Definition of severity */ typedef enum { - LOGS_INFO, + LOGS_DEBUG = -1, + LOGS_INFO = 0, LOGS_WARN, LOGS_ERR, LOGS_FATAL, - LOGS_DEBUG } LOG_Severity; +/* Minimum severity of messages to be logged */ +extern LOG_Severity log_min_severity; + /* Init function */ extern void LOG_Initialise(void); @@ -92,12 +92,16 @@ extern void LOG_Message(LOG_Severity severity, const char *format, ...); #endif -/* Set debug level: - 0, 1 - only non-debug messages are logged - 2 - debug messages are logged too, all messages are prefixed with - filename, line, and function name - */ -extern void LOG_SetDebugLevel(int level); +/* Set the minimum severity of a message to be logged or printed to terminal. + If the severity is LOGS_DEBUG and DEBUG is enabled, all messages will be + prefixed with the filename, line number, and function name. */ +extern void LOG_SetMinSeverity(LOG_Severity severity); + +/* Get the minimum severity */ +extern LOG_Severity LOG_GetMinSeverity(void); + +/* Set a prefix for debug messages */ +extern void LOG_SetDebugPrefix(const char *prefix); /* Log messages to a file instead of stderr, or stderr again if NULL */ extern void LOG_OpenFileLog(const char *log_file); diff -Nru chrony-3.5/main.c chrony-4.1/main.c --- chrony-3.5/main.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/main.c 2021-05-12 11:06:15.000000000 +0000 @@ -4,7 +4,7 @@ ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) John G. Hasler 2009 - * Copyright (C) Miroslav Lichvar 2012-2018 + * Copyright (C) Miroslav Lichvar 2012-2020 * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -38,6 +38,9 @@ #include "ntp_signd.h" #include "ntp_sources.h" #include "ntp_core.h" +#include "nts_ke_server.h" +#include "nts_ntp_server.h" +#include "socket.h" #include "sources.h" #include "sourcestats.h" #include "reference.h" @@ -87,11 +90,11 @@ { const char *pidfile = CNF_GetPidFile(); - if (!pidfile[0]) + if (!pidfile) return; - /* Don't care if this fails, there's not a lot we can do */ - unlink(pidfile); + if (!UTI_RemoveFile(NULL, pidfile, NULL)) + ; } /* ================================================== */ @@ -101,9 +104,8 @@ { if (!initialised) exit(exit_status); - if (CNF_GetDumpDir()[0] != '\0') { - SRC_DumpSources(); - } + LCL_CancelOffsetCorrection(); + SRC_DumpSources(); /* Don't update clock when removing sources */ REF_SetMode(REF_ModeIgnore); @@ -112,18 +114,23 @@ TMC_Finalise(); MNL_Finalise(); CLG_Finalise(); + NKS_Finalise(); + NNS_Finalise(); NSD_Finalise(); NSR_Finalise(); SST_Finalise(); NCR_Finalise(); NIO_Finalise(); CAM_Finalise(); + KEY_Finalise(); RCL_Finalise(); SRC_Finalise(); REF_Finalise(); RTC_Finalise(); SYS_Finalise(); + + SCK_Finalise(); SCH_Finalise(); LCL_Finalise(); PRV_Finalise(); @@ -142,7 +149,6 @@ static void signal_cleanup(int x) { - if (!initialised) exit(0); SCH_QuitProgram(); } @@ -178,7 +184,7 @@ NSR_AutoStartSources(); /* Special modes can end only when sources update their reachability. - Give up immediatelly if there are no active sources. */ + Give up immediately if there are no active sources. */ if (ref_mode != REF_ModeNormal && !SRC_ActiveSources()) { REF_SetUnsynchronised(); } @@ -253,7 +259,10 @@ FILE *in; int pid, count; - in = fopen(pidfile, "r"); + if (!pidfile) + return; + + in = UTI_OpenFile(NULL, pidfile, NULL, 'r', 0); if (!in) return; @@ -278,16 +287,12 @@ const char *pidfile = CNF_GetPidFile(); FILE *out; - if (!pidfile[0]) + if (!pidfile) return; - out = fopen(pidfile, "w"); - if (!out) { - LOG_FATAL("Could not open %s : %s", pidfile, strerror(errno)); - } else { - fprintf(out, "%d\n", (int)getpid()); - fclose(out); - } + out = UTI_OpenFile(NULL, pidfile, NULL, 'W', 0644); + fprintf(out, "%d\n", (int)getpid()); + fclose(out); } /* ================================================== */ @@ -370,8 +375,34 @@ static void print_help(const char *progname) { - printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n", - progname); + printf("Usage: %s [OPTION]... [DIRECTIVE]...\n\n" + "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); } /* ================================================== */ @@ -404,16 +435,16 @@ char *user = NULL, *log_file = NULL; struct passwd *pw; int opt, debug = 0, nofork = 0, address_family = IPADDR_UNSPEC; - int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = 0; + int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = -1; int scfilter_level = 0, lock_memory = 0, sched_priority = 0; - int clock_control = 1, system_log = 1; - int config_args = 0; + int clock_control = 1, system_log = 1, log_severity = LOGS_INFO; + int user_check = 1, config_args = 0, print_config = 0; do_platform_checks(); LOG_Initialise(); - /* Parse (undocumented) long command-line options */ + /* Parse long command-line options */ for (optind = 1; optind < argc; optind++) { if (!strcmp("--help", argv[optind])) { print_help(progname); @@ -427,7 +458,7 @@ optind = 1; /* Parse short command-line options */ - while ((opt = getopt(argc, argv, "46df:F:hl:mnP:qQrRst:u:vx")) != -1) { + while ((opt = getopt(argc, argv, "46df:F:hl:L:mnpP:qQrRst:u:Uvx")) != -1) { switch (opt) { case '4': case '6': @@ -447,12 +478,22 @@ case 'l': log_file = optarg; break; + case 'L': + log_severity = parse_int_arg(optarg); + break; case 'm': lock_memory = 1; break; case 'n': nofork = 1; break; + case 'p': + print_config = 1; + user_check = 0; + nofork = 1; + system_log = 0; + log_severity = LOGS_WARN; + break; case 'P': sched_priority = parse_int_arg(optarg); break; @@ -466,6 +507,7 @@ ref_mode = REF_ModePrintOnce; nofork = 1; client_only = 1; + user_check = 0; clock_control = 0; system_log = 0; break; @@ -484,6 +526,9 @@ case 'u': user = optarg; break; + case 'U': + user_check = 0; + break; case 'v': print_version(); return 0; @@ -496,7 +541,7 @@ } } - if (getuid() && !client_only) + if (user_check && getuid() != 0) LOG_FATAL("Not superuser"); /* Turn into a daemon */ @@ -510,13 +555,15 @@ LOG_OpenSystemLog(); } - LOG_SetDebugLevel(debug); + LOG_SetMinSeverity(debug >= 2 ? LOGS_DEBUG : log_severity); LOG(LOGS_INFO, "chronyd version %s starting (%s)", CHRONY_VERSION, CHRONYD_FEATURES); DNS_SetAddressFamily(address_family); CNF_Initialise(restarted, client_only); + if (print_config) + CNF_EnablePrint(); /* Parse the config file or the remaining command line arguments */ config_args = argc - optind; @@ -527,6 +574,9 @@ CNF_ParseLine(NULL, config_args + optind - argc + 1, argv[optind]); } + if (print_config) + return 0; + /* Check whether another chronyd may already be running */ check_pidfile(); @@ -546,6 +596,11 @@ PRV_Initialise(); LCL_Initialise(); SCH_Initialise(); + SCK_Initialise(address_family); + + /* Start helper processes if needed */ + NKS_PreInitialise(pw->pw_uid, pw->pw_gid, scfilter_level); + SYS_Initialise(clock_control); RTC_Initialise(do_init_rtc); SRC_Initialise(); @@ -553,8 +608,8 @@ KEY_Initialise(); /* Open privileged ports before dropping root */ - CAM_Initialise(address_family); - NIO_Initialise(address_family); + CAM_Initialise(); + NIO_Initialise(); NCR_Initialise(); CNF_SetupAccessRestrictions(); @@ -572,12 +627,17 @@ /* Drop root privileges if the specified user has a non-zero UID */ if (!geteuid() && (pw->pw_uid || pw->pw_gid)) - SYS_DropRoot(pw->pw_uid, pw->pw_gid); + SYS_DropRoot(pw->pw_uid, pw->pw_gid, SYS_MAIN_PROCESS); + + if (!geteuid()) + LOG(LOGS_WARN, "Running with root privileges"); REF_Initialise(); SST_Initialise(); NSR_Initialise(); NSD_Initialise(); + NNS_Initialise(); + NKS_Initialise(); CLG_Initialise(); MNL_Initialise(); TMC_Initialise(); @@ -591,7 +651,7 @@ CAM_OpenUnixSocket(); if (scfilter_level) - SYS_EnableSystemCallFilter(scfilter_level); + SYS_EnableSystemCallFilter(scfilter_level, SYS_MAIN_PROCESS); if (ref_mode == REF_ModeNormal && CNF_GetInitSources() > 0) { ref_mode = REF_ModeInitStepSlew; @@ -600,7 +660,7 @@ REF_SetModeEndHandler(reference_mode_end); REF_SetMode(ref_mode); - if (timeout > 0) + if (timeout >= 0) SCH_AddTimeoutByDelay(timeout, quit_timeout, NULL); if (do_init_rtc) { diff -Nru chrony-3.5/Makefile.in chrony-4.1/Makefile.in --- chrony-3.5/Makefile.in 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/Makefile.in 2021-05-12 11:06:15.000000000 +0000 @@ -21,44 +21,42 @@ # # Makefile template -SYSCONFDIR=@SYSCONFDIR@ -BINDIR=@BINDIR@ -SBINDIR=@SBINDIR@ -LOCALSTATEDIR=@LOCALSTATEDIR@ -CHRONYVARDIR=@CHRONYVARDIR@ +SYSCONFDIR = @SYSCONFDIR@ +BINDIR = @BINDIR@ +SBINDIR = @SBINDIR@ +LOCALSTATEDIR = @LOCALSTATEDIR@ +CHRONYVARDIR = @CHRONYVARDIR@ +DESTDIR = CC = @CC@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ -DESTDIR= - -HASH_OBJ = @HASH_OBJ@ +EXTRA_OBJS = @EXTRA_OBJS@ OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \ - reference.o regress.o rtc.o samplefilt.o sched.o sources.o sourcestats.o stubs.o \ - smooth.o sys.o sys_null.o tempcomp.o util.o $(HASH_OBJ) + reference.o regress.o rtc.o samplefilt.o sched.o socket.o sources.o sourcestats.o \ + stubs.o smooth.o sys.o sys_null.o tempcomp.o util.o $(EXTRA_OBJS) -EXTRA_OBJS=@EXTRA_OBJECTS@ +EXTRA_CLI_OBJS = @EXTRA_CLI_OBJS@ CLI_OBJS = array.o client.o cmdparse.o getdate.o memory.o nameserv.o \ - pktlength.o util.o $(HASH_OBJ) + pktlength.o socket.o util.o $(EXTRA_CLI_OBJS) -ALL_OBJS = $(OBJS) $(EXTRA_OBJS) $(CLI_OBJS) +ALL_OBJS = $(OBJS) $(CLI_OBJS) -LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ - -EXTRA_LIBS=@EXTRA_LIBS@ -EXTRA_CLI_LIBS=@EXTRA_CLI_LIBS@ +EXTRA_LIBS = @EXTRA_LIBS@ +EXTRA_CLI_LIBS = @EXTRA_CLI_LIBS@ # Until we have a main procedure we can link, just build object files # to test compilation all : chronyd chronyc -chronyd : $(OBJS) $(EXTRA_OBJS) - $(CC) $(CFLAGS) -o chronyd $(OBJS) $(EXTRA_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS) +chronyd : $(OBJS) + $(CC) $(CFLAGS) -o chronyd $(OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS) chronyc : $(CLI_OBJS) $(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS) @@ -70,6 +68,7 @@ -rm -f Makefile config.h config.log clean : + $(MAKE) -C test/unit clean -rm -f *.o *.s chronyc chronyd core.* *~ -rm -f *.gcda *.gcno -rm -rf .deps @@ -121,7 +120,7 @@ cd test/system && ./run print-chronyd-objects : - @echo $(OBJS) $(EXTRA_OBJS) + @echo $(OBJS) Makefile : Makefile.in configure @echo @@ -135,4 +134,6 @@ .deps/%.d: %.c | .deps @$(CC) -MM $(CPPFLAGS) -MT '$(<:%.c=%.o) $@' $< -o $@ +ifndef NODEPS -include $(ALL_OBJS:%.o=.deps/%.d) +endif diff -Nru chrony-3.5/manual.c chrony-4.1/manual.c --- chrony-3.5/manual.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/manual.c 2021-05-12 11:06:15.000000000 +0000 @@ -92,6 +92,7 @@ void MNL_Finalise(void) { + LCL_RemoveParameterChangeHandler(slew_samples, NULL); } /* ================================================== */ diff -Nru chrony-3.5/nameserv_async.c chrony-4.1/nameserv_async.c --- chrony-3.5/nameserv_async.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/nameserv_async.c 2021-05-12 11:06:15.000000000 +0000 @@ -51,7 +51,7 @@ int pipe[2]; }; -static int resolving_threads = 0; +static pthread_mutex_t privops_lock = PTHREAD_MUTEX_INITIALIZER; /* ================================================== */ @@ -60,7 +60,9 @@ { struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything; + pthread_mutex_lock(&privops_lock); inst->status = PRV_Name2IPAddress(inst->name, inst->addresses, DNS_MAX_ADDRESSES); + pthread_mutex_unlock(&privops_lock); /* Notify the main thread that the result is ready */ if (write(inst->pipe[1], "", 1) < 0) @@ -81,8 +83,6 @@ LOG_FATAL("pthread_join() failed"); } - resolving_threads--; - SCH_RemoveFileHandler(inst->pipe[0]); close(inst->pipe[0]); close(inst->pipe[1]); @@ -116,9 +116,6 @@ UTI_FdSetCloexec(inst->pipe[0]); UTI_FdSetCloexec(inst->pipe[1]); - resolving_threads++; - assert(resolving_threads <= 1); - if (pthread_create(&inst->thread, NULL, start_resolving, inst)) { LOG_FATAL("pthread_create() failed"); } diff -Nru chrony-3.5/nameserv.c chrony-4.1/nameserv.c --- chrony-3.5/nameserv.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/nameserv.c 2021-05-12 11:06:15.000000000 +0000 @@ -34,6 +34,7 @@ #include #include "nameserv.h" +#include "socket.h" #include "util.h" /* ================================================== */ @@ -49,12 +50,24 @@ DNS_Status DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs) { -#ifdef HAVE_GETADDRINFO struct addrinfo hints, *res, *ai; int i, result; + IPAddr ip; max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES); + for (i = 0; i < max_addrs; i++) + ip_addrs[i].family = IPADDR_UNSPEC; + + /* Avoid calling getaddrinfo() if the name is an IP address */ + if (UTI_StringToIP(name, &ip)) { + if (address_family != IPADDR_UNSPEC && ip.family != address_family) + return DNS_Failure; + if (max_addrs >= 1) + ip_addrs[0] = ip; + return DNS_Success; + } + memset(&hints, 0, sizeof (hints)); switch (address_family) { @@ -69,7 +82,7 @@ default: hints.ai_family = AF_UNSPEC; } - hints.ai_socktype = SOCK_STREAM; + hints.ai_socktype = SOCK_DGRAM; result = getaddrinfo(name, NULL, &hints, &res); @@ -94,6 +107,9 @@ case AF_INET6: if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET6) continue; + /* Don't return an address that would lose a scope ID */ + if (((struct sockaddr_in6 *)ai->ai_addr)->sin6_scope_id != 0) + continue; ip_addrs[i].family = IPADDR_INET6; memcpy(&ip_addrs[i].addr.in6, &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr.s6_addr, sizeof (ip_addrs->addr.in6)); @@ -103,48 +119,9 @@ } } - for (; i < max_addrs; i++) - ip_addrs[i].family = IPADDR_UNSPEC; - freeaddrinfo(res); return !max_addrs || ip_addrs[0].family != IPADDR_UNSPEC ? DNS_Success : DNS_Failure; -#else - struct hostent *host; - int i; - - if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET4) - return DNS_Failure; - - max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES); - - host = gethostbyname(name); - - if (host == NULL) { - if (h_errno == TRY_AGAIN) - return DNS_TryAgain; - } else { - if (host->h_addrtype != AF_INET || !host->h_addr_list[0]) - return DNS_Failure; - - for (i = 0; host->h_addr_list[i] && i < max_addrs; i++) { - ip_addrs[i].family = IPADDR_INET4; - ip_addrs[i].addr.in4 = ntohl(*(uint32_t *)host->h_addr_list[i]); - } - - for (; i < max_addrs; i++) - ip_addrs[i].family = IPADDR_UNSPEC; - - return DNS_Success; - } - -#ifdef FORCE_DNSRETRY - return DNS_TryAgain; -#else - return DNS_Failure; -#endif - -#endif } /* ================================================== */ @@ -153,35 +130,21 @@ DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len) { char *result = NULL; - #ifdef FEAT_IPV6 - struct sockaddr_in6 in6; + struct sockaddr_in6 saddr; +#else + struct sockaddr_in saddr; +#endif + IPSockAddr ip_saddr; socklen_t slen; char hbuf[NI_MAXHOST]; - slen = UTI_IPAndPortToSockaddr(ip_addr, 0, (struct sockaddr *)&in6); - if (!getnameinfo((struct sockaddr *)&in6, slen, hbuf, sizeof (hbuf), NULL, 0, 0)) - result = hbuf; -#else - struct hostent *host; - uint32_t addr; + ip_saddr.ip_addr = *ip_addr; + ip_saddr.port = 0; - switch (ip_addr->family) { - case IPADDR_INET4: - addr = htonl(ip_addr->addr.in4); - host = gethostbyaddr((const char *) &addr, sizeof (ip_addr), AF_INET); - break; -#ifdef FEAT_IPV6 - case IPADDR_INET6: - host = gethostbyaddr((const void *) ip_addr->addr.in6, sizeof (ip_addr->addr.in6), AF_INET6); - break; -#endif - default: - host = NULL; - } - if (host) - result = host->h_name; -#endif + slen = SCK_IPSockAddrToSockaddr(&ip_saddr, (struct sockaddr *)&saddr, sizeof (saddr)); + if (!getnameinfo((struct sockaddr *)&saddr, slen, hbuf, sizeof (hbuf), NULL, 0, 0)) + result = hbuf; if (result == NULL) result = UTI_IPToString(ip_addr); diff -Nru chrony-3.5/NEWS chrony-4.1/NEWS --- chrony-3.5/NEWS 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/NEWS 2021-05-12 11:06:15.000000000 +0000 @@ -1,3 +1,80 @@ +New in version 4.1 +================== + +Enhancements +------------ +* Add support for NTS servers specified by IP address (matching + Subject Alternative Name in server certificate) +* Add source-specific configuration of trusted certificates +* Allow multiple files and directories with trusted certificates +* Allow multiple pairs of server keys and certificates +* Add copy option to server/pool directive +* Increase PPS lock limit to 40% of pulse interval +* Perform source selection immediately after loading dump files +* Reload dump files for addresses negotiated by NTS-KE server +* Update seccomp filter and add less restrictive level +* Restart ongoing name resolution on online command + +Bug fixes +--------- +* Fix responding to IPv4 command requests on FreeBSD +* Fix dump files to not include uncorrected offset +* Fix initstepslew to accept time from own NTP clients +* Reset NTP address and port when no longer negotiated by NTS-KE server + +New in version 4.0 +================== + +Enhancements +------------ +* Add support for Network Time Security (NTS) authentication +* Add support for AES-CMAC keys (AES128, AES256) with Nettle +* Add authselectmode directive to control selection of unauthenticated sources +* Add binddevice, bindacqdevice, bindcmddevice directives +* Add confdir directive to better support fragmented configuration +* Add sourcedir directive and "reload sources" command to support dynamic + NTP sources specified in files +* Add clockprecision directive +* Add dscp directive to set Differentiated Services Code Point (DSCP) +* Add -L option to limit log messages by severity +* Add -p option to print whole configuration with included files +* Add -U option to allow start under non-root user +* Allow maxsamples to be set to 1 for faster update with -q/-Q option +* Avoid replacing NTP sources with sources that have unreachable address +* Improve pools to repeat name resolution to get "maxsources" sources +* Improve source selection with trusted sources +* Improve NTP loop test to prevent synchronisation to itself +* Repeat iburst when NTP source is switched from offline state to online +* Update clock synchronisation status and leap status more frequently +* Update seccomp filter +* Add "add pool" command +* Add "reset sources" command to drop all measurements +* Add authdata command to print details about NTP authentication +* Add selectdata command to print details about source selection +* Add -N option and sourcename command to print original names of sources +* Add -a option to some commands to print also unresolved sources +* Add -k, -p, -r options to clients command to select, limit, reset data + +Bug fixes +--------- +* Don't set interface for NTP responses to allow asymmetric routing +* Handle RTCs that don't support interrupts +* Respond to command requests with correct address on multihomed hosts + +Removed features +---------------- +* Drop support for RIPEMD keys (RMD128, RMD160, RMD256, RMD320) +* Drop support for long (non-standard) MACs in NTPv4 packets (chrony 2.x + clients using non-MD5/SHA1 keys need to use option "version 3") +* Drop support for line editing with GNU Readline + +New in version 3.5.1 +==================== + +Security fixes +-------------- +* Create new file when writing pidfile (CVE-2020-14367) + New in version 3.5 ================== diff -Nru chrony-3.5/ntp_auth.c chrony-4.1/ntp_auth.c --- chrony-3.5/ntp_auth.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/ntp_auth.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,495 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2019-2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + NTP authentication + */ + +#include "config.h" + +#include "sysincl.h" + +#include "keys.h" +#include "logging.h" +#include "memory.h" +#include "ntp_auth.h" +#include "ntp_ext.h" +#include "ntp_signd.h" +#include "nts_ntp.h" +#include "nts_ntp_client.h" +#include "nts_ntp_server.h" +#include "srcparams.h" +#include "util.h" + +/* Structure to hold authentication configuration and state */ + +struct NAU_Instance_Record { + NTP_AuthMode mode; /* Authentication mode of NTP packets */ + uint32_t key_id; /* Identifier of a symmetric key */ + NNC_Instance nts; /* Client NTS state */ +}; + +/* ================================================== */ + +static int +generate_symmetric_auth(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info) +{ + int auth_len, max_auth_len; + + if (info->length + NTP_MIN_MAC_LENGTH > sizeof (*packet)) { + DEBUG_LOG("Packet too long"); + return 0; + } + + /* Truncate long MACs in NTPv4 packets to allow deterministic parsing + of extension fields (RFC 7822) */ + max_auth_len = (info->version == 4 ? NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH) - 4; + max_auth_len = MIN(max_auth_len, sizeof (*packet) - info->length - 4); + + auth_len = KEY_GenerateAuth(key_id, packet, info->length, + (unsigned char *)packet + info->length + 4, max_auth_len); + if (auth_len < NTP_MIN_MAC_LENGTH - 4) { + DEBUG_LOG("Could not generate auth data with key %"PRIu32, key_id); + return 0; + } + + *(uint32_t *)((unsigned char *)packet + info->length) = htonl(key_id); + + info->auth.mac.start = info->length; + info->auth.mac.length = 4 + auth_len; + info->auth.mac.key_id = key_id; + info->length += info->auth.mac.length; + + return 1; +} + +/* ================================================== */ + +static int +check_symmetric_auth(NTP_Packet *packet, NTP_PacketInfo *info) +{ + int trunc_len; + + if (info->auth.mac.length < NTP_MIN_MAC_LENGTH) + return 0; + + trunc_len = info->version == 4 && info->auth.mac.length <= NTP_MAX_V4_MAC_LENGTH ? + NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH; + + if (!KEY_CheckAuth(info->auth.mac.key_id, packet, info->auth.mac.start, + (unsigned char *)packet + info->auth.mac.start + 4, + info->auth.mac.length - 4, trunc_len - 4)) + return 0; + + return 1; +} + +/* ================================================== */ + +static int +is_zero_data(unsigned char *data, int length) +{ + int i; + + for (i = 0; i < length; i++) + if (data[i] != 0) + return 0; + return 1; +} + +/* ================================================== */ + +static NAU_Instance +create_instance(NTP_AuthMode mode) +{ + NAU_Instance instance; + + instance = MallocNew(struct NAU_Instance_Record); + instance->mode = mode; + instance->key_id = INACTIVE_AUTHKEY; + instance->nts = NULL; + + assert(sizeof (instance->key_id) == 4); + + return instance; +} + +/* ================================================== */ + +NAU_Instance +NAU_CreateNoneInstance(void) +{ + return create_instance(NTP_AUTH_NONE); +} + +/* ================================================== */ + +NAU_Instance +NAU_CreateSymmetricInstance(uint32_t key_id) +{ + NAU_Instance instance = create_instance(NTP_AUTH_SYMMETRIC); + + instance->key_id = key_id; + + if (!KEY_KeyKnown(key_id)) + LOG(LOGS_WARN, "Key %"PRIu32" is %s", key_id, "missing"); + else if (!KEY_CheckKeyLength(key_id)) + LOG(LOGS_WARN, "Key %"PRIu32" is %s", key_id, "too short"); + + return instance; +} + +/* ================================================== */ + +NAU_Instance +NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name, uint32_t cert_set, + uint16_t ntp_port) +{ + NAU_Instance instance = create_instance(NTP_AUTH_NTS); + + instance->nts = NNC_CreateInstance(nts_address, name, cert_set, ntp_port); + + return instance; +} + +/* ================================================== */ + +void +NAU_DestroyInstance(NAU_Instance instance) +{ + if (instance->mode == NTP_AUTH_NTS) + NNC_DestroyInstance(instance->nts); + Free(instance); +} + +/* ================================================== */ + +int +NAU_IsAuthEnabled(NAU_Instance instance) +{ + return instance->mode != NTP_AUTH_NONE; +} + +/* ================================================== */ + +int +NAU_GetSuggestedNtpVersion(NAU_Instance instance) +{ + /* If the MAC in NTPv4 packets would be truncated, prefer NTPv3 for + compatibility with older chronyd servers */ + if (instance->mode == NTP_AUTH_SYMMETRIC && + KEY_GetAuthLength(instance->key_id) + sizeof (instance->key_id) > NTP_MAX_V4_MAC_LENGTH) + return 3; + + return NTP_VERSION; +} + +/* ================================================== */ + +int +NAU_PrepareRequestAuth(NAU_Instance instance) +{ + switch (instance->mode) { + case NTP_AUTH_NTS: + if (!NNC_PrepareForAuth(instance->nts)) + return 0; + break; + default: + break; + } + + return 1; +} + +/* ================================================== */ + +int +NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, NTP_PacketInfo *info) +{ + switch (instance->mode) { + case NTP_AUTH_NONE: + break; + case NTP_AUTH_SYMMETRIC: + if (!generate_symmetric_auth(instance->key_id, request, info)) + return 0; + break; + case NTP_AUTH_NTS: + if (!NNC_GenerateRequestAuth(instance->nts, request, info)) + return 0; + break; + default: + assert(0); + } + + info->auth.mode = instance->mode; + + return 1; +} + +/* ================================================== */ + +int +NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info) +{ + int parsed, remainder, ef_length, ef_type; + unsigned char *data; + + data = (void *)packet; + parsed = NTP_HEADER_LENGTH; + remainder = info->length - parsed; + + info->ext_fields = 0; + + /* Check if this is a plain NTP packet with no extension fields or MAC */ + if (remainder <= 0) + return 1; + + assert(remainder % 4 == 0); + + /* In NTPv3 and older packets don't have extension fields. Anything after + the header is assumed to be a MAC. */ + if (info->version <= 3) { + info->auth.mode = NTP_AUTH_SYMMETRIC; + info->auth.mac.start = parsed; + info->auth.mac.length = remainder; + info->auth.mac.key_id = ntohl(*(uint32_t *)(data + parsed)); + + /* Check if it is an MS-SNTP authenticator field or extended authenticator + field with zeroes as digest */ + if (info->version == 3 && info->auth.mac.key_id != 0) { + if (remainder == 20 && is_zero_data(data + parsed + 4, remainder - 4)) + info->auth.mode = NTP_AUTH_MSSNTP; + else if (remainder == 72 && is_zero_data(data + parsed + 8, remainder - 8)) + info->auth.mode = NTP_AUTH_MSSNTP_EXT; + } + + return 1; + } + + /* Check for a crypto NAK */ + if (remainder == 4 && ntohl(*(uint32_t *)(data + parsed)) == 0) { + info->auth.mode = NTP_AUTH_SYMMETRIC; + info->auth.mac.start = parsed; + info->auth.mac.length = remainder; + info->auth.mac.key_id = 0; + return 1; + } + + /* Parse the rest of the NTPv4 packet */ + + while (remainder > 0) { + /* Check if the remaining data is a MAC */ + if (remainder >= NTP_MIN_MAC_LENGTH && remainder <= NTP_MAX_V4_MAC_LENGTH) + break; + + /* Check if this is a valid NTPv4 extension field and skip it */ + if (!NEF_ParseField(packet, info->length, parsed, &ef_length, &ef_type, NULL, NULL)) { + DEBUG_LOG("Invalid format"); + return 0; + } + + assert(ef_length > 0 && ef_length % 4 == 0); + + switch (ef_type) { + case NTP_EF_NTS_UNIQUE_IDENTIFIER: + case NTP_EF_NTS_COOKIE: + case NTP_EF_NTS_COOKIE_PLACEHOLDER: + case NTP_EF_NTS_AUTH_AND_EEF: + info->auth.mode = NTP_AUTH_NTS; + break; + default: + DEBUG_LOG("Unknown extension field type=%x", (unsigned int)ef_type); + } + + info->ext_fields++; + parsed += ef_length; + remainder = info->length - parsed; + } + + if (remainder == 0) { + /* No MAC */ + return 1; + } else if (remainder >= NTP_MIN_MAC_LENGTH) { + info->auth.mode = NTP_AUTH_SYMMETRIC; + info->auth.mac.start = parsed; + info->auth.mac.length = remainder; + info->auth.mac.key_id = ntohl(*(uint32_t *)(data + parsed)); + return 1; + } + + DEBUG_LOG("Invalid format"); + return 0; +} + +/* ================================================== */ + +int +NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod) +{ + *kod = 0; + + switch (info->auth.mode) { + case NTP_AUTH_NONE: + break; + case NTP_AUTH_SYMMETRIC: + if (!check_symmetric_auth(request, info)) + return 0; + break; + case NTP_AUTH_MSSNTP: + /* MS-SNTP requests are not authenticated */ + break; + case NTP_AUTH_MSSNTP_EXT: + /* Not supported yet */ + return 0; + case NTP_AUTH_NTS: + if (!NNS_CheckRequestAuth(request, info, kod)) + return 0; + break; + default: + return 0; + } + + return 1; +} + +/* ================================================== */ + +int +NAU_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *request_info, + NTP_Packet *response, NTP_PacketInfo *response_info, + NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, + uint32_t kod) +{ + switch (request_info->auth.mode) { + case NTP_AUTH_NONE: + break; + case NTP_AUTH_SYMMETRIC: + if (!generate_symmetric_auth(request_info->auth.mac.key_id, response, response_info)) + return 0; + break; + case NTP_AUTH_MSSNTP: + /* Sign the packet asynchronously by ntp_signd */ + if (!NSD_SignAndSendPacket(request_info->auth.mac.key_id, response, response_info, + remote_addr, local_addr)) + return 0; + /* Don't send the original packet */ + return 0; + case NTP_AUTH_NTS: + if (!NNS_GenerateResponseAuth(request, request_info, response, response_info, kod)) + return 0; + break; + default: + DEBUG_LOG("Could not authenticate response auth_mode=%d", (int)request_info->auth.mode); + return 0; + } + + response_info->auth.mode = request_info->auth.mode; + + return 1; +} + +/* ================================================== */ + +int +NAU_CheckResponseAuth(NAU_Instance instance, NTP_Packet *response, NTP_PacketInfo *info) +{ + /* The authentication must match the expected mode */ + if (info->auth.mode != instance->mode) + return 0; + + switch (info->auth.mode) { + case NTP_AUTH_NONE: + break; + case NTP_AUTH_SYMMETRIC: + /* Check if it is authenticated with the specified key */ + if (info->auth.mac.key_id != instance->key_id) + return 0; + /* and that the MAC is valid */ + if (!check_symmetric_auth(response, info)) + return 0; + break; + case NTP_AUTH_NTS: + if (!NNC_CheckResponseAuth(instance->nts, response, info)) + return 0; + break; + default: + return 0; + } + + return 1; +} + +/* ================================================== */ + +void +NAU_ChangeAddress(NAU_Instance instance, IPAddr *address) +{ + switch (instance->mode) { + case NTP_AUTH_NONE: + case NTP_AUTH_SYMMETRIC: + break; + case NTP_AUTH_NTS: + NNC_ChangeAddress(instance->nts, address); + break; + default: + assert(0); + } +} + +/* ================================================== */ + +void +NAU_DumpData(NAU_Instance instance) +{ + switch (instance->mode) { + case NTP_AUTH_NTS: + NNC_DumpData(instance->nts); + break; + default: + break; + } +} + +/* ================================================== */ + +void +NAU_GetReport(NAU_Instance instance, RPT_AuthReport *report) +{ + memset(report, 0, sizeof (*report)); + + report->mode = instance->mode; + report->last_ke_ago = -1; + + switch (instance->mode) { + case NTP_AUTH_NONE: + break; + case NTP_AUTH_SYMMETRIC: + report->key_id = instance->key_id; + KEY_GetKeyInfo(instance->key_id, &report->key_type, &report->key_length); + break; + case NTP_AUTH_NTS: + NNC_GetReport(instance->nts, report); + break; + default: + assert(0); + } +} diff -Nru chrony-3.5/ntp_auth.h chrony-4.1/ntp_auth.h --- chrony-3.5/ntp_auth.h 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/ntp_auth.h 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,87 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + Header file for NTP authentication + */ + +#ifndef GOT_NTP_AUTH_H +#define GOT_NTP_AUTH_H + +#include "addressing.h" +#include "ntp.h" +#include "reports.h" + +typedef struct NAU_Instance_Record *NAU_Instance; + +/* Create an authenticator instance in a specific mode */ +extern NAU_Instance NAU_CreateNoneInstance(void); +extern NAU_Instance NAU_CreateSymmetricInstance(uint32_t key_id); +extern NAU_Instance NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name, + uint32_t cert_set, uint16_t ntp_port); + +/* Destroy an instance */ +extern void NAU_DestroyInstance(NAU_Instance instance); + +/* Check if an instance is not in the None mode */ +extern int NAU_IsAuthEnabled(NAU_Instance instance); + +/* Get NTP version recommended for better compatibility */ +extern int NAU_GetSuggestedNtpVersion(NAU_Instance instance); + +/* Perform operations necessary for NAU_GenerateRequestAuth() */ +extern int NAU_PrepareRequestAuth(NAU_Instance instance); + +/* Extend a request with data required by the authentication mode */ +extern int NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, + NTP_PacketInfo *info); + +/* Parse a request or response to detect the authentication mode */ +extern int NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info); + +/* Verify that a request is authentic. If it is not authentic and a non-zero + kod code is returned, a KoD response should be sent back. */ +extern int NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod); + +/* Extend a response with data required by the authentication mode. This + function can be called only if the previous call of NAU_CheckRequestAuth() + was on the same request. */ +extern int NAU_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *request_info, + NTP_Packet *response, NTP_PacketInfo *response_info, + NTP_Remote_Address *remote_addr, + NTP_Local_Address *local_addr, + uint32_t kod); + +/* Verify that a response is authentic */ +extern int NAU_CheckResponseAuth(NAU_Instance instance, NTP_Packet *response, + NTP_PacketInfo *info); + +/* Change an authentication-specific address (e.g. after replacing a source) */ +extern void NAU_ChangeAddress(NAU_Instance instance, IPAddr *address); + +/* Save authentication-specific data to speed up the next start */ +extern void NAU_DumpData(NAU_Instance instance); + +/* Provide a report about the current authentication state */ +extern void NAU_GetReport(NAU_Instance instance, RPT_AuthReport *report); + +#endif diff -Nru chrony-3.5/ntp_core.c chrony-4.1/ntp_core.c --- chrony-3.5/ntp_core.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/ntp_core.c 2021-05-12 11:06:15.000000000 +0000 @@ -3,7 +3,7 @@ ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 - * Copyright (C) Miroslav Lichvar 2009-2018 + * Copyright (C) Miroslav Lichvar 2009-2020 * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,9 +30,9 @@ #include "sysincl.h" #include "array.h" +#include "ntp_auth.h" #include "ntp_core.h" #include "ntp_io.h" -#include "ntp_signd.h" #include "memory.h" #include "sched.h" #include "reference.h" @@ -43,7 +43,6 @@ #include "util.h" #include "conf.h" #include "logging.h" -#include "keys.h" #include "addrfilt.h" #include "clientlog.h" @@ -64,16 +63,6 @@ } OperatingMode; /* ================================================== */ -/* Enumeration for authentication modes of NTP packets */ - -typedef enum { - AUTH_NONE = 0, /* No authentication */ - AUTH_SYMMETRIC, /* MAC using symmetric key (RFC 1305, RFC 5905) */ - AUTH_MSSNTP, /* MS-SNTP authenticator field */ - AUTH_MSSNTP_EXT, /* MS-SNTP extended authenticator field */ -} AuthenticationMode; - -/* ================================================== */ /* Structure used for holding a single peer/server's protocol machine */ @@ -89,6 +78,7 @@ SCH_TimeoutID tx_timeout_id; /* Timeout ID for next transmission */ int tx_suspended; /* Boolean indicating we can't transmit yet */ + int auto_iburst; /* If 1, initiate a burst when going online */ int auto_burst; /* If 1, initiate a burst on each poll */ int auto_offline; /* If 1, automatically go offline when requests cannot be sent */ @@ -117,6 +107,8 @@ int min_stratum; /* Increase stratum in received packets to the minimum */ + int copy; /* Boolean suppressing own refid and stratum */ + int poll_target; /* Target number of sourcestats samples */ int version; /* Version set in packets for server/peer */ @@ -137,9 +129,7 @@ double offset_correction; /* Correction applied to measured offset (e.g. for asymmetry in network delay) */ - AuthenticationMode auth_mode; /* Authentication mode of our requests */ - uint32_t auth_key_id; /* The ID of the authentication key to - use. */ + NAU_Instance auth; /* Authentication */ /* Count of transmitted packets since last valid response */ unsigned int tx_count; @@ -209,6 +199,7 @@ typedef struct { NTP_Remote_Address addr; NTP_Local_Address local_addr; + NAU_Instance auth; int interval; } BroadcastDestination; @@ -253,9 +244,6 @@ /* Maximum allowed dispersion - as defined in RFC 5905 (16 seconds) */ #define NTP_MAX_DISPERSION 16.0 -/* Invalid stratum number */ -#define NTP_INVALID_STRATUM 0 - /* Maximum allowed time for server to process client packet */ #define MAX_SERVER_INTERVAL 4.0 @@ -311,6 +299,8 @@ static void transmit_timeout(void *arg); static double get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx); static double get_separation(int poll); +static int parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info); +static void set_connectivity(NCR_Instance inst, SRC_Connectivity connectivity); /* ================================================== */ @@ -415,8 +405,10 @@ if (server_sock_fd6 != INVALID_SOCK_FD) NIO_CloseServerSocket(server_sock_fd6); - for (i = 0; i < ARR_GetSize(broadcasts); i++) + for (i = 0; i < ARR_GetSize(broadcasts); i++) { NIO_CloseServerSocket(((BroadcastDestination *)ARR_GetElement(broadcasts, i))->local_addr.sock_fd); + NAU_DestroyInstance(((BroadcastDestination *)ARR_GetElement(broadcasts, i))->auth); + } ARR_DestroyInstance(broadcasts); ADF_DestroyTable(access_auth_table); @@ -513,7 +505,8 @@ /* ================================================== */ NCR_Instance -NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params) +NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, + SourceParameters *params, const char *name) { NCR_Instance result; @@ -566,41 +559,38 @@ result->max_delay_ratio = CLAMP(0.0, params->max_delay_ratio, MAX_MAXDELAYRATIO); result->max_delay_dev_ratio = CLAMP(0.0, params->max_delay_dev_ratio, MAX_MAXDELAYDEVRATIO); result->offset_correction = params->offset; + result->auto_iburst = params->iburst; result->auto_burst = params->burst; result->auto_offline = params->auto_offline; + result->copy = params->copy && result->mode == MODE_CLIENT; result->poll_target = params->poll_target; - result->version = NTP_VERSION; + if (params->nts) { + IPSockAddr nts_address; + + if (result->mode == MODE_ACTIVE) + LOG(LOGS_WARN, "NTS not supported with peers"); + + nts_address.ip_addr = remote_addr->ip_addr; + nts_address.port = params->nts_port; - if (params->authkey == INACTIVE_AUTHKEY) { - result->auth_mode = AUTH_NONE; - result->auth_key_id = 0; + result->auth = NAU_CreateNtsInstance(&nts_address, name, params->cert_set, + result->remote_addr.port); + } else if (params->authkey != INACTIVE_AUTHKEY) { + result->auth = NAU_CreateSymmetricInstance(params->authkey); } else { - result->auth_mode = AUTH_SYMMETRIC; - result->auth_key_id = params->authkey; - if (!KEY_KeyKnown(result->auth_key_id)) { - LOG(LOGS_WARN, "Key %"PRIu32" used by source %s is %s", - result->auth_key_id, UTI_IPToString(&result->remote_addr.ip_addr), - "missing"); - } else if (!KEY_CheckKeyLength(result->auth_key_id)) { - LOG(LOGS_WARN, "Key %"PRIu32" used by source %s is %s", - result->auth_key_id, UTI_IPToString(&result->remote_addr.ip_addr), - "too short"); - } - - /* If the MAC in NTPv4 packets would be truncated, use version 3 by - default for compatibility with older chronyd servers */ - if (KEY_GetAuthLength(result->auth_key_id) + 4 > NTP_MAX_V4_MAC_LENGTH) - result->version = 3; + result->auth = NAU_CreateNoneInstance(); } + result->version = NAU_GetSuggestedNtpVersion(result->auth); + if (params->version) result->version = CLAMP(NTP_MIN_COMPAT_VERSION, params->version, NTP_VERSION); /* Create a source instance for this NTP source */ result->source = SRC_CreateNewInstance(UTI_IPToRefid(&remote_addr->ip_addr), - SRC_NTP, params->sel_options, - &result->remote_addr.ip_addr, + SRC_NTP, NAU_IsAuthEnabled(result->auth), + params->sel_options, &result->remote_addr.ip_addr, params->min_samples, params->max_samples, params->min_delay, params->asymmetry); @@ -613,9 +603,7 @@ result->rx_timeout_id = 0; result->tx_timeout_id = 0; result->tx_suspended = 1; - result->opmode = params->connectivity == SRC_ONLINE || - (params->connectivity == SRC_MAYBE_ONLINE && - NIO_IsServerConnectable(remote_addr)) ? MD_ONLINE : MD_OFFLINE; + result->opmode = MD_OFFLINE; result->local_poll = result->minpoll; result->poll_score = 0.0; zero_local_timestamp(&result->local_tx); @@ -625,9 +613,7 @@ NCR_ResetInstance(result); - if (params->iburst) { - NCR_InitiateSampleBurst(result, IBURST_GOOD_SAMPLES, IBURST_TOTAL_SAMPLES); - } + set_connectivity(result, params->connectivity); return result; } @@ -647,6 +633,8 @@ if (instance->filter) SPF_DestroyInstance(instance->filter); + NAU_DestroyInstance(instance->auth); + /* This will destroy the source instance inside the structure, which will cause reselection if this was the synchronising source etc. */ @@ -714,10 +702,14 @@ /* ================================================== */ void -NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr) +NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int ntp_only) { memset(&inst->report, 0, sizeof (inst->report)); NCR_ResetInstance(inst); + + if (!ntp_only) + NAU_ChangeAddress(inst->auth, &remote_addr->ip_addr); + inst->remote_addr = *remote_addr; if (inst->mode == MODE_CLIENT) @@ -914,8 +906,7 @@ { NCR_Instance inst = (NCR_Instance)arg; - DEBUG_LOG("Receive timeout for [%s:%d]", - UTI_IPToString(&inst->remote_addr.ip_addr), inst->remote_addr.port); + DEBUG_LOG("Receive timeout for %s", UTI_IPSockAddrToString(&inst->remote_addr)); inst->rx_timeout_id = 0; close_client_socket(inst); @@ -928,8 +919,8 @@ int interleaved, /* Flag enabling interleaved mode */ int my_poll, /* The log2 of the local poll interval */ int version, /* The NTP version to be set in the packet */ - int auth_mode, /* The authentication mode */ - uint32_t key_id, /* The authentication key ID */ + uint32_t kod, /* KoD code - 0 disabled */ + NAU_Instance auth, /* The authentication to be used for the packet */ NTP_int64 *remote_ntp_rx, /* The receive timestamp from received packet */ NTP_int64 *remote_ntp_tx, /* The transmit timestamp from received packet */ NTP_Local_Timestamp *local_rx, /* The RX time of the received packet */ @@ -940,13 +931,16 @@ NTP_int64 *local_ntp_tx, /* The transmit timestamp from the previous packet RESULT : transmit timestamp from this packet */ NTP_Remote_Address *where_to, /* Where to address the reponse to */ - NTP_Local_Address *from /* From what address to send it */ + NTP_Local_Address *from, /* From what address to send it */ + NTP_Packet *request, /* The received packet if responding */ + NTP_PacketInfo *request_info /* and its info */ ) { + NTP_PacketInfo info; NTP_Packet message; - int auth_len, max_auth_len, length, ret, precision; struct timespec local_receive, local_transmit; double smooth_offset, local_transmit_err; + int ret, precision; NTP_int64 ts_fuzz; /* Parameters read from reference module */ @@ -956,6 +950,8 @@ struct timespec our_ref_time; double our_root_delay, our_root_dispersion; + assert(auth || (request && request_info)); + /* Don't reply with version higher than ours */ if (version > NTP_VERSION) { version = NTP_VERSION; @@ -968,6 +964,10 @@ smooth_time = 0; smooth_offset = 0.0; + /* Get an initial transmit timestamp. A more accurate timestamp will be + taken later in this function. */ + SCH_GetLastEventTime(&local_transmit, NULL, NULL); + if (my_mode == MODE_CLIENT) { /* Don't reveal local time or state of the clock in client packets */ precision = 32; @@ -975,10 +975,6 @@ our_root_delay = our_root_dispersion = 0.0; UTI_ZeroTimespec(&our_ref_time); } else { - /* This is accurate enough and cheaper than calling LCL_ReadCookedTime. - A more accurate timestamp will be taken later in this function. */ - SCH_GetLastEventTime(&local_transmit, NULL, NULL); - REF_GetReferenceParams(&local_transmit, &are_we_synchronised, &leap_status, &our_stratum, @@ -1007,6 +1003,12 @@ local_receive = local_rx->ts; } + if (kod != 0) { + leap_status = LEAP_Unsynchronised; + our_stratum = NTP_INVALID_STRATUM; + our_ref_id = kod; + } + /* Generate transmit packet */ message.lvm = NTP_LVM(leap_status, version, my_mode); /* Stratum 16 and larger are invalid */ @@ -1018,12 +1020,8 @@ message.poll = my_poll; message.precision = precision; - - /* If we're sending a client mode packet and we aren't synchronized yet, - we might have to set up artificial values for some of these parameters */ message.root_delay = UTI_DoubleToNtp32(our_root_delay); message.root_dispersion = UTI_DoubleToNtp32(our_root_dispersion); - message.reference_id = htonl(our_ref_id); /* Now fill in timestamps */ @@ -1057,53 +1055,35 @@ } do { + if (!parse_packet(&message, NTP_HEADER_LENGTH, &info)) + return 0; + /* Prepare random bits which will be added to the transmit timestamp */ UTI_GetNtp64Fuzz(&ts_fuzz, precision); - /* Transmit - this our local time right now! Also, we might need to - store this for our own use later, next time we receive a message - from the source we're sending to now. */ - LCL_ReadCookedTime(&local_transmit, &local_transmit_err); - - if (smooth_time) - UTI_AddDoubleToTimespec(&local_transmit, smooth_offset, &local_transmit); - - length = NTP_NORMAL_PACKET_LENGTH; - - /* Authenticate the packet */ - - if (auth_mode == AUTH_SYMMETRIC || auth_mode == AUTH_MSSNTP) { - /* Pre-compensate the transmit time by approximately how long it will - take to generate the authentication data */ - local_transmit.tv_nsec += auth_mode == AUTH_SYMMETRIC ? - KEY_GetAuthDelay(key_id) : NSD_GetAuthDelay(key_id); - UTI_NormaliseTimespec(&local_transmit); - UTI_TimespecToNtp64(interleaved ? &local_tx->ts : &local_transmit, - &message.transmit_ts, &ts_fuzz); - - if (auth_mode == AUTH_SYMMETRIC) { - /* Truncate long MACs in NTPv4 packets to allow deterministic parsing - of extension fields (RFC 7822) */ - max_auth_len = version == 4 ? - NTP_MAX_V4_MAC_LENGTH - 4 : sizeof (message.auth_data); - - auth_len = KEY_GenerateAuth(key_id, (unsigned char *) &message, - offsetof(NTP_Packet, auth_keyid), - (unsigned char *)&message.auth_data, max_auth_len); - if (!auth_len) { - DEBUG_LOG("Could not generate auth data with key %"PRIu32, key_id); - return 0; - } - - message.auth_keyid = htonl(key_id); - length += sizeof (message.auth_keyid) + auth_len; - } else if (auth_mode == AUTH_MSSNTP) { - /* MS-SNTP packets are signed (asynchronously) by ntp_signd */ - return NSD_SignAndSendPacket(key_id, &message, where_to, from, length); + /* Get a more accurate transmit timestamp if it needs to be saved in the + packet (i.e. in the server, symmetric, and broadcast basic modes) */ + if (!interleaved && precision < 32) { + LCL_ReadCookedTime(&local_transmit, &local_transmit_err); + if (smooth_time) + UTI_AddDoubleToTimespec(&local_transmit, smooth_offset, &local_transmit); + } + + UTI_TimespecToNtp64(interleaved ? &local_tx->ts : &local_transmit, + &message.transmit_ts, &ts_fuzz); + + /* Generate the authentication data */ + if (auth) { + if (!NAU_GenerateRequestAuth(auth, &message, &info)) { + DEBUG_LOG("Could not generate request auth"); + return 0; } } else { - UTI_TimespecToNtp64(interleaved ? &local_tx->ts : &local_transmit, - &message.transmit_ts, &ts_fuzz); + if (!NAU_GenerateResponseAuth(request, request_info, &message, &info, + where_to, from, kod)) { + DEBUG_LOG("Could not generate response auth"); + return 0; + } } /* Do not send a packet with a non-zero transmit timestamp which is @@ -1117,9 +1097,22 @@ UTI_IsEqualAnyNtp64(&message.transmit_ts, &message.receive_ts, &message.originate_ts, local_ntp_tx)); - ret = NIO_SendPacket(&message, where_to, from, length, local_tx != NULL); + if (request_info && request_info->length < info.length) { + DEBUG_LOG("Response longer than request req_len=%d res_len=%d", + request_info->length, info.length); + return 0; + } + + /* If the transmit timestamp will be saved, get an even more + accurate daemon timestamp closer to the transmission */ + if (local_tx) + LCL_ReadCookedTime(&local_transmit, &local_transmit_err); + + ret = NIO_SendPacket(&message, where_to, from, info.length, local_tx != NULL); if (local_tx) { + if (smooth_time) + UTI_AddDoubleToTimespec(&local_transmit, smooth_offset, &local_transmit); local_tx->ts = local_transmit; local_tx->err = local_transmit_err; local_tx->source = NTP_TS_DAEMON; @@ -1172,8 +1165,17 @@ return; } - DEBUG_LOG("Transmit timeout for [%s:%d]", - UTI_IPToString(&inst->remote_addr.ip_addr), inst->remote_addr.port); + DEBUG_LOG("Transmit timeout for %s", UTI_IPSockAddrToString(&inst->remote_addr)); + + /* Prepare authentication */ + if (!NAU_PrepareRequestAuth(inst->auth)) { + if (inst->burst_total_samples_to_go > 0) + inst->burst_total_samples_to_go--; + adjust_poll(inst, 0.25); + SRC_UpdateReachability(inst->source, 0); + restart_timeout(inst, get_transmit_delay(inst, 1, 0.0)); + return; + } /* Open new client socket */ if (inst->mode == MODE_CLIENT) { @@ -1225,13 +1227,13 @@ } /* Send the request (which may also be a response in the symmetric mode) */ - sent = transmit_packet(inst->mode, interleaved, inst->local_poll, inst->version, - inst->auth_mode, inst->auth_key_id, + sent = transmit_packet(inst->mode, interleaved, inst->local_poll, inst->version, 0, + inst->auth, initial ? NULL : &inst->remote_ntp_rx, initial ? &inst->init_remote_ntp_tx : &inst->remote_ntp_tx, initial ? &inst->init_local_rx : &inst->local_rx, &inst->local_tx, &inst->local_ntp_rx, &inst->local_ntp_tx, - &inst->remote_addr, &local_addr); + &inst->remote_addr, &local_addr, NULL, NULL); ++inst->tx_count; if (sent) @@ -1284,123 +1286,29 @@ /* ================================================== */ static int -check_packet_format(NTP_Packet *message, int length) +parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info) { - int version; - - /* Check version and length */ - - version = NTP_LVM_TO_VERSION(message->lvm); - if (version < NTP_MIN_COMPAT_VERSION || version > NTP_MAX_COMPAT_VERSION) { - DEBUG_LOG("NTP packet has invalid version %d", version); - return 0; - } - - if (length < NTP_NORMAL_PACKET_LENGTH || (unsigned int)length % 4) { + if (length < NTP_HEADER_LENGTH || length % 4U != 0) { DEBUG_LOG("NTP packet has invalid length %d", length); return 0; } - /* We can't reliably check the packet for invalid extension fields as we - support MACs longer than the shortest valid extension field */ - - return 1; -} - -/* ================================================== */ - -static int -is_zero_data(unsigned char *data, int length) -{ - int i; - - for (i = 0; i < length; i++) - if (data[i]) - return 0; - return 1; -} - -/* ================================================== */ - -static int -check_packet_auth(NTP_Packet *pkt, int length, - AuthenticationMode *auth_mode, uint32_t *key_id) -{ - int i, version, remainder, ext_length, max_mac_length; - unsigned char *data; - uint32_t id; - - /* Go through extension fields and see if there is a valid MAC */ - - version = NTP_LVM_TO_VERSION(pkt->lvm); - i = NTP_NORMAL_PACKET_LENGTH; - data = (void *)pkt; - - while (1) { - remainder = length - i; - - /* Check if the remaining data is a valid MAC. There is a limit on MAC - length in NTPv4 packets to allow deterministic parsing of extension - fields (RFC 7822), but we need to support longer MACs to not break - compatibility with older chrony clients. This needs to be done before - trying to parse the data as an extension field. */ - - max_mac_length = version == 4 && remainder <= NTP_MAX_V4_MAC_LENGTH ? - NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH; - - if (remainder >= NTP_MIN_MAC_LENGTH && remainder <= max_mac_length) { - id = ntohl(*(uint32_t *)(data + i)); - if (KEY_CheckAuth(id, (void *)pkt, i, (void *)(data + i + 4), - remainder - 4, max_mac_length - 4)) { - *auth_mode = AUTH_SYMMETRIC; - *key_id = id; - - /* If it's an NTPv4 packet with long MAC and no extension fields, - rewrite the version in the packet to respond with long MAC too */ - if (version == 4 && NTP_NORMAL_PACKET_LENGTH + remainder == length && - remainder > NTP_MAX_V4_MAC_LENGTH) - pkt->lvm = NTP_LVM(NTP_LVM_TO_LEAP(pkt->lvm), 3, NTP_LVM_TO_MODE(pkt->lvm)); - - return 1; - } - } + info->length = length; + info->version = NTP_LVM_TO_VERSION(packet->lvm); + info->mode = NTP_LVM_TO_MODE(packet->lvm); + info->ext_fields = 0; + info->auth.mode = NTP_AUTH_NONE; - /* Check if this is a valid NTPv4 extension field and skip it. It should - have a 16-bit type, 16-bit length, and data padded to 32 bits. */ - if (version == 4 && remainder >= NTP_MIN_EXTENSION_LENGTH) { - ext_length = ntohs(*(uint16_t *)(data + i + 2)); - if (ext_length >= NTP_MIN_EXTENSION_LENGTH && - ext_length <= remainder && ext_length % 4 == 0) { - i += ext_length; - continue; - } - } - - /* Invalid or missing MAC, or format error */ - break; + if (info->version < NTP_MIN_COMPAT_VERSION || info->version > NTP_MAX_COMPAT_VERSION) { + DEBUG_LOG("NTP packet has invalid version %d", info->version); + return 0; } - /* This is not 100% reliable as a MAC could fail to authenticate and could - pass as an extension field, leaving reminder smaller than the minimum MAC - length */ - if (remainder >= NTP_MIN_MAC_LENGTH) { - *auth_mode = AUTH_SYMMETRIC; - *key_id = ntohl(*(uint32_t *)(data + i)); - - /* Check if it is an MS-SNTP authenticator field or extended authenticator - field with zeroes as digest */ - if (version == 3 && *key_id) { - if (remainder == 20 && is_zero_data(data + i + 4, remainder - 4)) - *auth_mode = AUTH_MSSNTP; - else if (remainder == 72 && is_zero_data(data + i + 8, remainder - 8)) - *auth_mode = AUTH_MSSNTP_EXT; - } - } else { - *auth_mode = AUTH_NONE; - *key_id = 0; - } + /* Parse authentication extension fields or MAC */ + if (!NAU_ParsePacket(packet, info)) + return 0; - return 0; + return 1; } /* ================================================== */ @@ -1466,6 +1374,51 @@ /* ================================================== */ +static int +check_sync_loop(NCR_Instance inst, NTP_Packet *message, NTP_Local_Address *local_addr, + struct timespec *local_ts) +{ + double our_root_delay, our_root_dispersion; + int are_we_synchronised, our_stratum; + struct timespec our_ref_time; + NTP_Leap leap_status; + uint32_t our_ref_id; + + /* Check if a client or peer can be synchronised to us */ + if (!NIO_IsServerSocketOpen() || REF_GetMode() != REF_ModeNormal) + return 1; + + /* Check if the source indicates that it is synchronised to our address + (assuming it uses the same address as the one from which we send requests + to the source) */ + if (message->stratum > 1 && + message->reference_id == htonl(UTI_IPToRefid(&local_addr->ip_addr))) + return 0; + + /* Compare our reference data with the source to make sure it is not us + (e.g. due to a misconfiguration) */ + + REF_GetReferenceParams(local_ts, &are_we_synchronised, &leap_status, &our_stratum, + &our_ref_id, &our_ref_time, &our_root_delay, &our_root_dispersion); + + if (message->stratum == our_stratum && + message->reference_id == htonl(our_ref_id) && + message->root_delay == UTI_DoubleToNtp32(our_root_delay) && + !UTI_IsZeroNtp64(&message->reference_ts)) { + NTP_int64 ntp_ref_time; + + UTI_TimespecToNtp64(&our_ref_time, &ntp_ref_time, NULL); + if (UTI_CompareNtp64(&message->reference_ts, &ntp_ref_time) == 0) { + DEBUG_LOG("Source %s is me", UTI_IPToString(&inst->remote_addr.ip_addr)); + return 0; + } + } + + return 1; +} + +/* ================================================== */ + static void process_sample(NCR_Instance inst, NTP_Sample *sample) { @@ -1513,17 +1466,16 @@ /* ================================================== */ static int -receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr, - NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length) +process_response(NCR_Instance inst, NTP_Local_Address *local_addr, + NTP_Local_Timestamp *rx_ts, NTP_Packet *message, NTP_PacketInfo *info) { NTP_Sample sample; SST_Stats stats; int pkt_leap, pkt_version; - uint32_t pkt_refid, pkt_key_id; + uint32_t pkt_refid; double pkt_root_delay; double pkt_root_dispersion; - AuthenticationMode pkt_auth_mode; /* The skew and estimated frequency offset relative to the remote source */ double skew, source_freq_lo, source_freq_hi; @@ -1548,8 +1500,6 @@ stats = SRC_GetSourcestats(inst->source); - inst->report.total_rx_count++; - pkt_leap = NTP_LVM_TO_LEAP(message->lvm); pkt_version = NTP_LVM_TO_VERSION(message->lvm); pkt_refid = ntohl(message->reference_id); @@ -1580,14 +1530,8 @@ /* Test 4 would check for denied access. It would always pass as this function is called only for known sources. */ - /* Test 5 checks for authentication failure. If we expect authenticated info - from this peer/server and the packet doesn't have it, the authentication - is bad, or it's authenticated with a different key than expected, it's got - to fail. If we don't expect the packet to be authenticated, just ignore - the test. */ - test5 = inst->auth_mode == AUTH_NONE || - (check_packet_auth(message, length, &pkt_auth_mode, &pkt_key_id) && - pkt_auth_mode == inst->auth_mode && pkt_key_id == inst->auth_key_id); + /* Test 5 checks for authentication failure */ + test5 = NAU_CheckResponseAuth(inst->auth, message, info); /* Test 6 checks for unsynchronised server */ test6 = pkt_leap != LEAP_Unsynchronised && @@ -1718,10 +1662,9 @@ the increase in delay */ testC = check_delay_dev_ratio(inst, stats, &sample.time, sample.offset, sample.peer_delay); - /* Test D requires that the remote peer is not synchronised to us to - prevent a synchronisation loop */ - testD = message->stratum <= 1 || REF_GetMode() != REF_ModeNormal || - pkt_refid != UTI_IPToRefid(&local_addr->ip_addr); + /* Test D requires that the source is not synchronised to us and is not us + to prevent a synchronisation loop */ + testD = check_sync_loop(inst, message, local_addr, &rx_ts->ts); } else { remote_interval = local_interval = response_time = 0.0; sample.offset = sample.peer_delay = sample.peer_dispersion = 0.0; @@ -1737,8 +1680,6 @@ sample.root_delay = pkt_root_delay + sample.peer_delay; sample.root_dispersion = pkt_root_dispersion + sample.peer_dispersion; - sample.stratum = MAX(message->stratum, inst->min_stratum); - sample.leap = (NTP_Leap)pkt_leap; /* Update the NTP timestamps. If it's a valid packet from a synchronised source, the timestamps may be used later when processing a packet in the @@ -1821,7 +1762,7 @@ if (valid_packet) { inst->remote_poll = message->poll; inst->remote_stratum = message->stratum != NTP_INVALID_STRATUM ? - message->stratum : NTP_MAX_STRATUM; + MIN(message->stratum, NTP_MAX_STRATUM) : NTP_MAX_STRATUM; inst->prev_local_poll = inst->local_poll; inst->prev_tx_count = inst->tx_count; @@ -1829,6 +1770,16 @@ SRC_UpdateReachability(inst->source, synced_packet); + if (synced_packet) { + if (inst->copy && inst->remote_stratum > 0) { + /* Assume the reference ID and stratum of the server */ + inst->remote_stratum--; + SRC_SetRefid(inst->source, ntohl(message->reference_id), &inst->remote_addr.ip_addr); + } + + SRC_UpdateStatus(inst->source, MAX(inst->remote_stratum, inst->min_stratum), pkt_leap); + } + if (good_packet) { /* Adjust the polling interval, accumulate the sample, etc. */ process_sample(inst, &sample); @@ -1850,8 +1801,8 @@ break; } } else { - /* Slowly increase the polling interval if we can't get good packet */ - adjust_poll(inst, 0.1); + /* Slowly increase the polling interval if we can't get a good response */ + adjust_poll(inst, testD ? 0.02 : 0.1); } /* If in client mode, no more packets are expected to be coming from the @@ -1907,7 +1858,7 @@ test5) << 1 | test6) << 1 | test7) << 1 | testA) << 1 | testB) << 1 | testC) << 1 | testD; inst->report.interleaved = interleaved_packet; - inst->report.authenticated = inst->auth_mode != AUTH_NONE; + inst->report.authenticated = NAU_IsAuthEnabled(inst->auth); inst->report.tx_tss_char = tss_chars[local_transmit.source]; inst->report.rx_tss_char = tss_chars[local_receive.source]; @@ -1967,17 +1918,19 @@ NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr, NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length) { - int pkt_mode, proc_packet, proc_as_unknown; + int proc_packet, proc_as_unknown; + NTP_PacketInfo info; + + inst->report.total_rx_count++; - if (!check_packet_format(message, length)) + if (!parse_packet(message, length, &info)) return 0; - pkt_mode = NTP_LVM_TO_MODE(message->lvm); proc_packet = 0; proc_as_unknown = 0; /* Now, depending on the mode we decide what to do */ - switch (pkt_mode) { + switch (info.mode) { case MODE_ACTIVE: switch (inst->mode) { case MODE_ACTIVE: @@ -2067,13 +2020,13 @@ return 0; } - return receive_packet(inst, local_addr, rx_ts, message, length); + return process_response(inst, local_addr, rx_ts, message, &info); } else if (proc_as_unknown) { NCR_ProcessRxUnknown(&inst->remote_addr, local_addr, rx_ts, message, length); /* It's not a reply to our request, don't return success */ return 0; } else { - DEBUG_LOG("NTP packet discarded pkt_mode=%d our_mode=%u", pkt_mode, inst->mode); + DEBUG_LOG("NTP packet discarded mode=%d our_mode=%u", (int)info.mode, inst->mode); return 0; } } @@ -2086,12 +2039,12 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length) { - NTP_Mode pkt_mode, my_mode; + NTP_PacketInfo info; + NTP_Mode my_mode; NTP_int64 *local_ntp_rx, *local_ntp_tx; NTP_Local_Timestamp local_tx, *tx_ts; - int pkt_version, valid_auth, log_index, interleaved, poll; - AuthenticationMode auth_mode; - uint32_t key_id; + int log_index, interleaved, poll, version; + uint32_t kod; /* Ignore the packet if it wasn't received by server socket */ if (!NIO_IsServerSocket(local_addr->sock_fd)) { @@ -2099,20 +2052,16 @@ return; } - if (!check_packet_format(message, length)) + if (!parse_packet(message, length, &info)) return; if (!ADF_IsAllowed(access_auth_table, &remote_addr->ip_addr)) { - DEBUG_LOG("NTP packet received from unauthorised host %s port %d", - UTI_IPToString(&remote_addr->ip_addr), - remote_addr->port); + DEBUG_LOG("NTP packet received from unauthorised host %s", + UTI_IPToString(&remote_addr->ip_addr)); return; } - pkt_mode = NTP_LVM_TO_MODE(message->lvm); - pkt_version = NTP_LVM_TO_VERSION(message->lvm); - - switch (pkt_mode) { + switch (info.mode) { case MODE_ACTIVE: /* We are symmetric passive, even though we don't ever lock to him */ my_mode = MODE_PASSIVE; @@ -2125,42 +2074,35 @@ /* Check if it is an NTPv1 client request (NTPv1 packets have a reserved field instead of the mode field and the actual mode is determined from the port numbers). Don't ever respond with a mode 0 packet! */ - if (pkt_version == 1 && remote_addr->port != NTP_PORT) { + if (info.version == 1 && remote_addr->port != NTP_PORT) { my_mode = MODE_SERVER; break; } /* Fall through */ default: /* Discard */ - DEBUG_LOG("NTP packet discarded pkt_mode=%u", pkt_mode); + DEBUG_LOG("NTP packet discarded mode=%d", (int)info.mode); return; } - log_index = CLG_LogNTPAccess(&remote_addr->ip_addr, &rx_ts->ts); + kod = 0; + log_index = CLG_LogServiceAccess(CLG_NTP, &remote_addr->ip_addr, &rx_ts->ts); /* Don't reply to all requests if the rate is excessive */ - if (log_index >= 0 && CLG_LimitNTPResponseRate(log_index)) { + if (log_index >= 0 && CLG_LimitServiceRate(CLG_NTP, log_index)) { DEBUG_LOG("NTP packet discarded to limit response rate"); return; } - /* Check if the packet includes MAC that authenticates properly */ - valid_auth = check_packet_auth(message, length, &auth_mode, &key_id); + /* Check authentication */ + if (!NAU_CheckRequestAuth(message, &info, &kod)) { + DEBUG_LOG("NTP packet failed auth mode=%d kod=%"PRIx32, (int)info.auth.mode, kod); - /* If authentication failed, select whether and how we should respond */ - if (!valid_auth) { - switch (auth_mode) { - case AUTH_NONE: - /* Reply with no MAC */ - break; - case AUTH_MSSNTP: - /* Ignore the failure (MS-SNTP servers don't check client MAC) */ - break; - default: - /* Discard packets in other modes */ - DEBUG_LOG("NTP packet discarded auth_mode=%u", auth_mode); - return; - } + /* Don't respond unless a non-zero KoD was returned */ + if (kod == 0) + return; + } else if (info.auth.mode != NTP_AUTH_NONE && info.auth.mode != NTP_AUTH_MSSNTP) { + CLG_LogAuthNtpRequest(); } local_ntp_rx = local_ntp_tx = NULL; @@ -2172,7 +2114,7 @@ in the interleaved mode. This means the third reply to a new client is the earliest one that can be interleaved. We don't want to waste time on clients that are not using the interleaved mode. */ - if (log_index >= 0) { + if (kod == 0 && log_index >= 0) { CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx); interleaved = !UTI_IsZeroNtp64(local_ntp_rx) && !UTI_CompareNtp64(&message->originate_ts, local_ntp_rx) && @@ -2192,10 +2134,14 @@ poll = CLG_GetNtpMinPoll(); poll = MAX(poll, message->poll); + /* Respond with the same version */ + version = info.version; + /* Send a reply */ - transmit_packet(my_mode, interleaved, poll, pkt_version, - auth_mode, key_id, &message->receive_ts, &message->transmit_ts, - rx_ts, tx_ts, local_ntp_rx, NULL, remote_addr, local_addr); + transmit_packet(my_mode, interleaved, poll, version, kod, NULL, + &message->receive_ts, &message->transmit_ts, + rx_ts, tx_ts, local_ntp_rx, NULL, remote_addr, local_addr, + message, &info); /* Save the transmit timestamp */ if (tx_ts) @@ -2240,15 +2186,13 @@ NCR_ProcessTxKnown(NCR_Instance inst, NTP_Local_Address *local_addr, NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length) { - NTP_Mode pkt_mode; + NTP_PacketInfo info; - if (!check_packet_format(message, length)) + if (!parse_packet(message, length, &info)) return; - pkt_mode = NTP_LVM_TO_MODE(message->lvm); - /* Server and passive mode packets are responses to unknown sources */ - if (pkt_mode != MODE_CLIENT && pkt_mode != MODE_ACTIVE) { + if (info.mode != MODE_CLIENT && info.mode != MODE_ACTIVE) { NCR_ProcessTxUnknown(&inst->remote_addr, local_addr, tx_ts, message, length); return; } @@ -2265,19 +2209,20 @@ { NTP_int64 *local_ntp_rx, *local_ntp_tx; NTP_Local_Timestamp local_tx; + NTP_PacketInfo info; int log_index; - if (!check_packet_format(message, length)) + if (!parse_packet(message, length, &info)) return; - if (NTP_LVM_TO_MODE(message->lvm) == MODE_BROADCAST) + if (info.mode == MODE_BROADCAST) return; log_index = CLG_GetClientIndex(&remote_addr->ip_addr); if (log_index < 0) return; - if (SMT_IsEnabled() && NTP_LVM_TO_MODE(message->lvm) == MODE_SERVER) + if (SMT_IsEnabled() && info.mode == MODE_SERVER) UTI_AddDoubleToTimespec(&tx_ts->ts, SMT_GetOffset(&tx_ts->ts), &tx_ts->ts); CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx); @@ -2311,13 +2256,9 @@ /* ================================================== */ -void -NCR_SetConnectivity(NCR_Instance inst, SRC_Connectivity connectivity) +static void +set_connectivity(NCR_Instance inst, SRC_Connectivity connectivity) { - char *s; - - s = UTI_IPToString(&inst->remote_addr.ip_addr); - if (connectivity == SRC_MAYBE_ONLINE) connectivity = NIO_IsServerConnectable(&inst->remote_addr) ? SRC_ONLINE : SRC_OFFLINE; @@ -2328,17 +2269,17 @@ /* Nothing to do */ break; case MD_OFFLINE: - LOG(LOGS_INFO, "Source %s online", s); inst->opmode = MD_ONLINE; NCR_ResetInstance(inst); start_initial_timeout(inst); + if (inst->auto_iburst) + NCR_InitiateSampleBurst(inst, IBURST_GOOD_SAMPLES, IBURST_TOTAL_SAMPLES); break; case MD_BURST_WAS_ONLINE: /* Will revert */ break; case MD_BURST_WAS_OFFLINE: inst->opmode = MD_BURST_WAS_ONLINE; - LOG(LOGS_INFO, "Source %s online", s); break; default: assert(0); @@ -2347,14 +2288,12 @@ case SRC_OFFLINE: switch (inst->opmode) { case MD_ONLINE: - LOG(LOGS_INFO, "Source %s offline", s); take_offline(inst); break; case MD_OFFLINE: break; case MD_BURST_WAS_ONLINE: inst->opmode = MD_BURST_WAS_OFFLINE; - LOG(LOGS_INFO, "Source %s offline", s); break; case MD_BURST_WAS_OFFLINE: break; @@ -2370,6 +2309,26 @@ /* ================================================== */ void +NCR_SetConnectivity(NCR_Instance inst, SRC_Connectivity connectivity) +{ + OperatingMode prev_opmode; + int was_online, is_online; + + prev_opmode = inst->opmode; + + set_connectivity(inst, connectivity); + + /* Report an important change */ + was_online = prev_opmode == MD_ONLINE || prev_opmode == MD_BURST_WAS_ONLINE; + is_online = inst->opmode == MD_ONLINE || inst->opmode == MD_BURST_WAS_ONLINE; + if (was_online != is_online) + LOG(LOGS_INFO, "Source %s %s", + UTI_IPToString(&inst->remote_addr.ip_addr), is_online ? "online" : "offline"); +} + +/* ================================================== */ + +void NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll) { if (new_minpoll < MIN_POLL || new_minpoll > MAX_POLL) @@ -2501,6 +2460,14 @@ /* ================================================== */ void +NCR_GetAuthReport(NCR_Instance inst, RPT_AuthReport *report) +{ + NAU_GetReport(inst->auth, report); +} + +/* ================================================== */ + +void NCR_GetNTPReport(NCR_Instance inst, RPT_NTPReport *report) { *report = inst->report; @@ -2618,6 +2585,14 @@ /* ================================================== */ +void +NCR_DumpAuthData(NCR_Instance inst) +{ + NAU_DumpData(inst->auth); +} + +/* ================================================== */ + static void broadcast_timeout(void *arg) { @@ -2632,8 +2607,9 @@ UTI_ZeroNtp64(&orig_ts); zero_local_timestamp(&recv_ts); - transmit_packet(MODE_BROADCAST, 0, poll, NTP_VERSION, 0, 0, &orig_ts, &orig_ts, &recv_ts, - NULL, NULL, NULL, &destination->addr, &destination->local_addr); + transmit_packet(MODE_BROADCAST, 0, poll, NTP_VERSION, 0, destination->auth, + &orig_ts, &orig_ts, &recv_ts, NULL, NULL, NULL, + &destination->addr, &destination->local_addr, NULL, NULL); /* Requeue timeout. We don't care if interval drifts gradually. */ SCH_AddTimeoutInClass(destination->interval, get_separation(poll), SAMPLING_RANDOMNESS, @@ -2643,17 +2619,17 @@ /* ================================================== */ void -NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval) +NCR_AddBroadcastDestination(NTP_Remote_Address *addr, int interval) { BroadcastDestination *destination; destination = (BroadcastDestination *)ARR_GetNewElement(broadcasts); - destination->addr.ip_addr = *addr; - destination->addr.port = port; + destination->addr = *addr; destination->local_addr.ip_addr.family = IPADDR_UNSPEC; destination->local_addr.if_index = INVALID_IF_INDEX; destination->local_addr.sock_fd = NIO_OpenServerSocket(&destination->addr); + destination->auth = NAU_CreateNoneInstance(); destination->interval = CLAMP(1, interval, 1 << MAX_POLL); SCH_AddTimeoutInClass(destination->interval, MAX_SAMPLING_SEPARATION, SAMPLING_RANDOMNESS, diff -Nru chrony-3.5/ntp_core.h chrony-4.1/ntp_core.h --- chrony-3.5/ntp_core.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/ntp_core.h 2021-05-12 11:06:15.000000000 +0000 @@ -59,7 +59,8 @@ extern void NCR_Finalise(void); /* Get a new instance for a server or peer */ -extern NCR_Instance NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params); +extern NCR_Instance NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, + SourceParameters *params, const char *name); /* Destroy an instance */ extern void NCR_DestroyInstance(NCR_Instance instance); @@ -74,7 +75,8 @@ extern void NCR_ResetPoll(NCR_Instance instance); /* Change the remote address of an instance */ -extern void NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr); +extern void NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, + int ntp_only); /* This routine is called when a new packet arrives off the network, and it relates to a source we have an ongoing protocol exchange with */ @@ -120,6 +122,7 @@ extern void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples); extern void NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timespec *now); +extern void NCR_GetAuthReport(NCR_Instance inst, RPT_AuthReport *report); extern void NCR_GetNTPReport(NCR_Instance inst, RPT_NTPReport *report); extern int NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all); @@ -134,6 +137,8 @@ extern int NCR_IsSyncPeer(NCR_Instance instance); -extern void NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval); +extern void NCR_DumpAuthData(NCR_Instance inst); + +extern void NCR_AddBroadcastDestination(NTP_Remote_Address *addr, int interval); #endif /* GOT_NTP_CORE_H */ diff -Nru chrony-3.5/ntp_ext.c chrony-4.1/ntp_ext.c --- chrony-3.5/ntp_ext.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/ntp_ext.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,192 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2019-2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + Functions for adding and parsing NTPv4 extension fields + */ + +#include "config.h" + +#include "sysincl.h" + +#include "ntp_ext.h" + +struct ExtFieldHeader { + uint16_t type; + uint16_t length; +}; + +/* ================================================== */ + +static int +format_field(unsigned char *buffer, int buffer_length, int start, + int type, int body_length, int *length, void **body) +{ + struct ExtFieldHeader *header; + + if (buffer_length < 0 || start < 0 || buffer_length <= start || + buffer_length - start < sizeof (*header) || start % 4 != 0) + return 0; + + header = (struct ExtFieldHeader *)(buffer + start); + + if (body_length < 0 || sizeof (*header) + body_length > 0xffff || + start + sizeof (*header) + body_length > buffer_length || body_length % 4 != 0) + return 0; + + header->type = htons(type); + header->length = htons(sizeof (*header) + body_length); + *length = sizeof (*header) + body_length; + *body = header + 1; + + return 1; +} + +/* ================================================== */ + +int +NEF_SetField(unsigned char *buffer, int buffer_length, int start, + int type, void *body, int body_length, int *length) +{ + void *ef_body; + + if (!format_field(buffer, buffer_length, start, type, body_length, length, &ef_body)) + return 0; + + memcpy(ef_body, body, body_length); + + return 1; +} + +/* ================================================== */ + +int +NEF_AddBlankField(NTP_Packet *packet, NTP_PacketInfo *info, int type, int body_length, void **body) +{ + int ef_length, length = info->length; + + if (length < NTP_HEADER_LENGTH || length >= sizeof (*packet) || length % 4 != 0) + return 0; + + /* Only NTPv4 packets can have extension fields */ + if (info->version != 4) + return 0; + + if (!format_field((unsigned char *)packet, sizeof (*packet), length, + type, body_length, &ef_length, body)) + return 0; + + if (ef_length < NTP_MIN_EF_LENGTH) + return 0; + + info->length += ef_length; + info->ext_fields++; + + return 1; +} + +/* ================================================== */ + +int +NEF_AddField(NTP_Packet *packet, NTP_PacketInfo *info, + int type, void *body, int body_length) +{ + void *ef_body; + + if (!NEF_AddBlankField(packet, info, type, body_length, &ef_body)) + return 0; + + memcpy(ef_body, body, body_length); + + return 1; +} + +/* ================================================== */ + +int +NEF_ParseSingleField(unsigned char *buffer, int buffer_length, int start, + int *length, int *type, void **body, int *body_length) +{ + struct ExtFieldHeader *header; + int ef_length; + + if (buffer_length < 0 || start < 0 || buffer_length <= start || + buffer_length - start < sizeof (*header)) + return 0; + + header = (struct ExtFieldHeader *)(buffer + start); + + assert(sizeof (*header) == 4); + + ef_length = ntohs(header->length); + + if (ef_length < (int)(sizeof (*header)) || start + ef_length > buffer_length || + ef_length % 4 != 0) + return 0; + + if (length) + *length = ef_length; + if (type) + *type = ntohs(header->type); + if (body) + *body = header + 1; + if (body_length) + *body_length = ef_length - sizeof (*header); + + return 1; +} + +/* ================================================== */ + +int +NEF_ParseField(NTP_Packet *packet, int packet_length, int start, + int *length, int *type, void **body, int *body_length) +{ + int ef_length; + + if (packet_length <= NTP_HEADER_LENGTH || packet_length > sizeof (*packet) || + packet_length <= start || packet_length % 4 != 0 || + start < NTP_HEADER_LENGTH || start % 4 != 0) + return 0; + + /* Only NTPv4 packets have extension fields */ + if (NTP_LVM_TO_VERSION(packet->lvm) != 4) + return 0; + + /* Check if the remaining data is a MAC. RFC 7822 specifies the maximum + length of a MAC in NTPv4 packets in order to enable deterministic + parsing. */ + if (packet_length - start <= NTP_MAX_V4_MAC_LENGTH) + return 0; + + if (!NEF_ParseSingleField((unsigned char *)packet, packet_length, start, + &ef_length, type, body, body_length)) + return 0; + + if (ef_length < NTP_MIN_EF_LENGTH) + return 0; + + if (length) + *length = ef_length; + + return 1; +} diff -Nru chrony-3.5/ntp_ext.h chrony-4.1/ntp_ext.h --- chrony-3.5/ntp_ext.h 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/ntp_ext.h 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,43 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + Header file for NTP extension fields + */ + +#ifndef GOT_NTP_EXT_H +#define GOT_NTP_EXT_H + +#include "ntp.h" + +extern int NEF_SetField(unsigned char *buffer, int buffer_length, int start, + int type, void *body, int body_length, int *length); +extern int NEF_AddBlankField(NTP_Packet *packet, NTP_PacketInfo *info, int type, + int body_length, void **body); +extern int NEF_AddField(NTP_Packet *packet, NTP_PacketInfo *info, + int type, void *body, int body_length); +extern int NEF_ParseSingleField(unsigned char *buffer, int buffer_length, int start, + int *length, int *type, void **body, int *body_length); +extern int NEF_ParseField(NTP_Packet *packet, int packet_length, int start, + int *length, int *type, void **body, int *body_length); + +#endif diff -Nru chrony-3.5/ntp.h chrony-4.1/ntp.h --- chrony-3.5/ntp.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/ntp.h 2021-05-12 11:06:15.000000000 +0000 @@ -47,18 +47,21 @@ /* Maximum stratum number (infinity) */ #define NTP_MAX_STRATUM 16 -/* The minimum valid length of an extension field */ -#define NTP_MIN_EXTENSION_LENGTH 16 - -/* The maximum assumed length of all extension fields in received - packets (RFC 5905 doesn't specify a limit on length or number of - extension fields in one packet) */ -#define NTP_MAX_EXTENSIONS_LENGTH 1024 +/* Invalid stratum number */ +#define NTP_INVALID_STRATUM 0 /* The minimum and maximum supported length of MAC */ #define NTP_MIN_MAC_LENGTH (4 + 16) #define NTP_MAX_MAC_LENGTH (4 + MAX_HASH_LENGTH) +/* The minimum valid length of an extension field */ +#define NTP_MIN_EF_LENGTH 16 + +/* The maximum assumed length of all extension fields in an NTP packet, + including a MAC (RFC 5905 doesn't specify a limit on length or number of + extension fields in one packet) */ +#define NTP_MAX_EXTENSIONS_LENGTH (1024 + NTP_MAX_MAC_LENGTH) + /* The maximum length of MAC in NTPv4 packets which allows deterministic parsing of extension fields (RFC 7822) */ #define NTP_MAX_V4_MAC_LENGTH (4 + 20) @@ -93,21 +96,10 @@ NTP_int64 receive_ts; NTP_int64 transmit_ts; - /* Optional extension fields, we don't send packets with them yet */ - /* uint8_t extensions[] */ - - /* Optional message authentication code (MAC) */ - NTP_int32 auth_keyid; - uint8_t auth_data[NTP_MAX_MAC_LENGTH - 4]; + uint8_t extensions[NTP_MAX_EXTENSIONS_LENGTH]; } NTP_Packet; -#define NTP_NORMAL_PACKET_LENGTH (int)offsetof(NTP_Packet, auth_keyid) - -/* The buffer used to hold a datagram read from the network */ -typedef struct { - NTP_Packet ntp_pkt; - uint8_t extensions[NTP_MAX_EXTENSIONS_LENGTH]; -} NTP_Receive_Buffer; +#define NTP_HEADER_LENGTH (int)offsetof(NTP_Packet, extensions) /* Macros to work with the lvm field */ #define NTP_LVM_TO_LEAP(lvm) (((lvm) >> 6) & 0x3) @@ -121,6 +113,34 @@ #define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */ #define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */ +/* Enumeration for authentication modes of NTP packets */ +typedef enum { + NTP_AUTH_NONE = 0, /* No authentication */ + NTP_AUTH_SYMMETRIC, /* NTP MAC or CMAC using a symmetric key + (RFC 1305, RFC 5905, RFC 8573) */ + NTP_AUTH_MSSNTP, /* MS-SNTP authenticator field */ + NTP_AUTH_MSSNTP_EXT, /* MS-SNTP extended authenticator field */ + NTP_AUTH_NTS, /* Network Time Security (RFC 8915) */ +} NTP_AuthMode; + +/* Structure describing an NTP packet */ +typedef struct { + int length; + int version; + NTP_Mode mode; + + int ext_fields; + + struct { + NTP_AuthMode mode; + struct { + int start; + int length; + uint32_t key_id; + } mac; + } auth; +} NTP_PacketInfo; + /* Structure used to save NTP measurements. time is the local time at which the sample is to be considered to have been made and offset is the offset at the time (positive indicates that the local clock is slow relative to the @@ -132,8 +152,6 @@ double peer_dispersion; double root_delay; double root_dispersion; - int stratum; - NTP_Leap leap; } NTP_Sample; #endif /* GOT_NTP_H */ diff -Nru chrony-3.5/ntp_io.c chrony-4.1/ntp_io.c --- chrony-3.5/ntp_io.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/ntp_io.c 2021-05-12 11:06:15.000000000 +0000 @@ -4,7 +4,7 @@ ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Timo Teras 2009 - * Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018 + * Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018-2020 * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,11 +30,11 @@ #include "sysincl.h" -#include "array.h" #include "ntp_io.h" #include "ntp_core.h" #include "ntp_sources.h" #include "sched.h" +#include "socket.h" #include "local.h" #include "logging.h" #include "conf.h" @@ -46,54 +46,16 @@ #endif #define INVALID_SOCK_FD -1 -#define CMSGBUF_SIZE 256 - -union sockaddr_in46 { - struct sockaddr_in in4; -#ifdef FEAT_IPV6 - struct sockaddr_in6 in6; -#endif - struct sockaddr u; -}; - -struct Message { - union sockaddr_in46 name; - struct iovec iov; - NTP_Receive_Buffer buf; - /* Aligned buffer for control messages */ - struct cmsghdr cmsgbuf[CMSGBUF_SIZE / sizeof (struct cmsghdr)]; -}; - -#ifdef HAVE_RECVMMSG -#define MAX_RECV_MESSAGES 4 -#define MessageHeader mmsghdr -#else -/* Compatible with mmsghdr */ -struct MessageHeader { - struct msghdr msg_hdr; - unsigned int msg_len; -}; - -#define MAX_RECV_MESSAGES 1 -#endif - -/* Arrays of Message and MessageHeader */ -static ARR_Instance recv_messages; -static ARR_Instance recv_headers; /* The server/peer and client sockets for IPv4 and IPv6 */ static int server_sock_fd4; -static int client_sock_fd4; -#ifdef FEAT_IPV6 static int server_sock_fd6; +static int client_sock_fd4; static int client_sock_fd6; -#endif /* Reference counters for server sockets to keep them open only when needed */ static int server_sock_ref4; -#ifdef FEAT_IPV6 static int server_sock_ref6; -#endif /* Flag indicating we create a new connected client socket for each server instead of sharing client_sock_fd4 and client_sock_fd6 */ @@ -119,162 +81,54 @@ /* ================================================== */ static int -prepare_socket(int family, int port_number, int client_only) +open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr) { - union sockaddr_in46 my_addr; - socklen_t my_addr_len; - int sock_fd; - IPAddr bind_address; - int events = SCH_FILE_INPUT, on_off = 1; + int sock_fd, sock_flags, dscp, events = SCH_FILE_INPUT; + IPSockAddr local_addr; + char *iface; - /* Open Internet domain UDP socket for NTP message transmissions */ + if (!SCK_IsIpFamilyEnabled(family)) + return INVALID_SOCK_FD; - sock_fd = socket(family, SOCK_DGRAM, 0); + if (!client_only) { + CNF_GetBindAddress(family, &local_addr.ip_addr); + iface = CNF_GetBindNtpInterface(); + } else { + CNF_GetBindAcquisitionAddress(family, &local_addr.ip_addr); + iface = CNF_GetBindAcquisitionInterface(); + } + + local_addr.port = local_port; + sock_flags = SCK_FLAG_RX_DEST_ADDR | SCK_FLAG_PRIV_BIND; + if (!client_only) + sock_flags |= SCK_FLAG_BROADCAST; + + sock_fd = SCK_OpenUdpSocket(remote_addr, &local_addr, iface, sock_flags); if (sock_fd < 0) { - if (!client_only) { - LOG(LOGS_ERR, "Could not open %s NTP socket : %s", - UTI_SockaddrFamilyToString(family), strerror(errno)); - } else { - DEBUG_LOG("Could not open %s NTP socket : %s", - UTI_SockaddrFamilyToString(family), strerror(errno)); - } + if (!client_only) + LOG(LOGS_ERR, "Could not open NTP socket on %s", UTI_IPSockAddrToString(&local_addr)); return INVALID_SOCK_FD; } - /* Close on exec */ - UTI_FdSetCloexec(sock_fd); - - /* Enable non-blocking mode on server sockets */ - if (!client_only && fcntl(sock_fd, F_SETFL, O_NONBLOCK)) - DEBUG_LOG("Could not set O_NONBLOCK : %s", strerror(errno)); - - /* Prepare local address */ - memset(&my_addr, 0, sizeof (my_addr)); - my_addr_len = 0; - - switch (family) { - case AF_INET: - if (!client_only) - CNF_GetBindAddress(IPADDR_INET4, &bind_address); - else - CNF_GetBindAcquisitionAddress(IPADDR_INET4, &bind_address); - - if (bind_address.family == IPADDR_INET4) - my_addr.in4.sin_addr.s_addr = htonl(bind_address.addr.in4); - else if (port_number) - my_addr.in4.sin_addr.s_addr = htonl(INADDR_ANY); - else - break; - - my_addr.in4.sin_family = family; - my_addr.in4.sin_port = htons(port_number); - my_addr_len = sizeof (my_addr.in4); - - if (!client_only) - bound_server_sock_fd4 = my_addr.in4.sin_addr.s_addr != htonl(INADDR_ANY); - - break; -#ifdef FEAT_IPV6 - case AF_INET6: - if (!client_only) - CNF_GetBindAddress(IPADDR_INET6, &bind_address); - else - CNF_GetBindAcquisitionAddress(IPADDR_INET6, &bind_address); - - if (bind_address.family == IPADDR_INET6) - memcpy(my_addr.in6.sin6_addr.s6_addr, bind_address.addr.in6, - sizeof (my_addr.in6.sin6_addr.s6_addr)); - else if (port_number) - my_addr.in6.sin6_addr = in6addr_any; - else - break; - - my_addr.in6.sin6_family = family; - my_addr.in6.sin6_port = htons(port_number); - my_addr_len = sizeof (my_addr.in6); - - break; + dscp = CNF_GetNtpDscp(); + if (dscp > 0 && dscp < 64) { +#ifdef IP_TOS + if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_TOS, dscp << 2)) + ; #endif - default: - assert(0); } - /* Make the socket capable of re-using an old address if binding to a specific port */ - if (port_number && - setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on_off, sizeof(on_off)) < 0) { - LOG(LOGS_ERR, "Could not set %s socket option", "SO_REUSEADDR"); - /* Don't quit - we might survive anyway */ - } - - /* Make the socket capable of sending broadcast pkts - needed for NTP broadcast mode */ - if (!client_only && - setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&on_off, sizeof(on_off)) < 0) { - LOG(LOGS_ERR, "Could not set %s socket option", "SO_BROADCAST"); - /* Don't quit - we might survive anyway */ - } + if (!client_only && family == IPADDR_INET4 && local_addr.port > 0) + bound_server_sock_fd4 = local_addr.ip_addr.addr.in4 != INADDR_ANY; /* Enable kernel/HW timestamping of packets */ #ifdef HAVE_LINUX_TIMESTAMPING if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, client_only, &events)) #endif -#ifdef SO_TIMESTAMPNS - if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPNS, (char *)&on_off, sizeof(on_off)) < 0) -#endif -#ifdef SO_TIMESTAMP - if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on_off, sizeof(on_off)) < 0) - LOG(LOGS_ERR, "Could not set %s socket option", "SO_TIMESTAMP"); -#endif + if (!SCK_EnableKernelRxTimestamping(sock_fd)) ; -#ifdef IP_FREEBIND - /* Allow binding to address that doesn't exist yet */ - if (my_addr_len > 0 && - setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) { - LOG(LOGS_ERR, "Could not set %s socket option", "IP_FREEBIND"); - } -#endif - - if (family == AF_INET) { -#ifdef HAVE_IN_PKTINFO - if (setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) - LOG(LOGS_ERR, "Could not set %s socket option", "IP_PKTINFO"); -#elif defined(IP_RECVDSTADDR) - if (setsockopt(sock_fd, IPPROTO_IP, IP_RECVDSTADDR, (char *)&on_off, sizeof(on_off)) < 0) - LOG(LOGS_ERR, "Could not set %s socket option", "IP_RECVDSTADDR"); -#endif - } -#ifdef FEAT_IPV6 - else if (family == AF_INET6) { -#ifdef IPV6_V6ONLY - /* Receive IPv6 packets only */ - if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) { - LOG(LOGS_ERR, "Could not set %s socket option", "IPV6_V6ONLY"); - } -#endif - -#ifdef HAVE_IN6_PKTINFO -#ifdef IPV6_RECVPKTINFO - if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char *)&on_off, sizeof(on_off)) < 0) { - LOG(LOGS_ERR, "Could not set %s socket option", "IPV6_RECVPKTINFO"); - } -#else - if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) { - LOG(LOGS_ERR, "Could not set %s socket option", "IPV6_PKTINFO"); - } -#endif -#endif - } -#endif - - /* Bind the socket if a port or address was specified */ - if (my_addr_len > 0 && PRV_BindSocket(sock_fd, &my_addr.u, my_addr_len) < 0) { - LOG(LOGS_ERR, "Could not bind %s NTP socket : %s", - UTI_SockaddrFamilyToString(family), strerror(errno)); - close(sock_fd); - return INVALID_SOCK_FD; - } - /* Register handler for read and possibly exception events on the socket */ SCH_AddFileHandler(sock_fd, events, read_from_socket, NULL); @@ -284,40 +138,9 @@ /* ================================================== */ static int -prepare_separate_client_socket(int family) -{ - switch (family) { - case IPADDR_INET4: - return prepare_socket(AF_INET, 0, 1); -#ifdef FEAT_IPV6 - case IPADDR_INET6: - return prepare_socket(AF_INET6, 0, 1); -#endif - default: - return INVALID_SOCK_FD; - } -} - -/* ================================================== */ - -static int -connect_socket(int sock_fd, NTP_Remote_Address *remote_addr) +open_separate_client_socket(IPSockAddr *remote_addr) { - union sockaddr_in46 addr; - socklen_t addr_len; - - addr_len = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port, &addr.u); - - assert(addr_len); - - if (connect(sock_fd, &addr.u, addr_len) < 0) { - DEBUG_LOG("Could not connect NTP socket to %s:%d : %s", - UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, - strerror(errno)); - return 0; - } - - return 1; + return open_socket(remote_addr->ip_addr.family, 0, 1, remote_addr); } /* ================================================== */ @@ -332,45 +155,23 @@ NIO_Linux_NotifySocketClosing(sock_fd); #endif SCH_RemoveFileHandler(sock_fd); - close(sock_fd); -} - -/* ================================================== */ - -static void -prepare_buffers(unsigned int n) -{ - struct MessageHeader *hdr; - struct Message *msg; - unsigned int i; - - for (i = 0; i < n; i++) { - msg = ARR_GetElement(recv_messages, i); - hdr = ARR_GetElement(recv_headers, i); - - msg->iov.iov_base = &msg->buf; - msg->iov.iov_len = sizeof (msg->buf); - hdr->msg_hdr.msg_name = &msg->name; - hdr->msg_hdr.msg_namelen = sizeof (msg->name); - hdr->msg_hdr.msg_iov = &msg->iov; - hdr->msg_hdr.msg_iovlen = 1; - hdr->msg_hdr.msg_control = &msg->cmsgbuf; - hdr->msg_hdr.msg_controllen = sizeof (msg->cmsgbuf); - hdr->msg_hdr.msg_flags = 0; - hdr->msg_len = 0; - } + SCK_CloseSocket(sock_fd); } /* ================================================== */ void -NIO_Initialise(int family) +NIO_Initialise(void) { int server_port, client_port; assert(!initialised); initialised = 1; +#ifdef PRIVOPS_BINDSOCKET + SCK_SetPrivBind(PRV_BindSocket); +#endif + #ifdef HAVE_LINUX_TIMESTAMPING NIO_Linux_Initialise(); #else @@ -381,12 +182,6 @@ } #endif - recv_messages = ARR_CreateInstance(sizeof (struct Message)); - ARR_SetSize(recv_messages, MAX_RECV_MESSAGES); - recv_headers = ARR_CreateInstance(sizeof (struct MessageHeader)); - ARR_SetSize(recv_headers, MAX_RECV_MESSAGES); - prepare_buffers(MAX_RECV_MESSAGES); - server_port = CNF_GetNTPPort(); client_port = CNF_GetAcquisitionPort(); @@ -399,47 +194,31 @@ client_port == server_port); server_sock_fd4 = INVALID_SOCK_FD; - client_sock_fd4 = INVALID_SOCK_FD; - server_sock_ref4 = 0; -#ifdef FEAT_IPV6 server_sock_fd6 = INVALID_SOCK_FD; + client_sock_fd4 = INVALID_SOCK_FD; client_sock_fd6 = INVALID_SOCK_FD; + server_sock_ref4 = 0; server_sock_ref6 = 0; -#endif - if (family == IPADDR_UNSPEC || family == IPADDR_INET4) { - if (permanent_server_sockets && server_port) - server_sock_fd4 = prepare_socket(AF_INET, server_port, 0); - if (!separate_client_sockets) { - if (client_port != server_port || !server_port) - client_sock_fd4 = prepare_socket(AF_INET, client_port, 1); - else - client_sock_fd4 = server_sock_fd4; - } + if (permanent_server_sockets && server_port) { + server_sock_fd4 = open_socket(IPADDR_INET4, server_port, 0, NULL); + server_sock_fd6 = open_socket(IPADDR_INET6, server_port, 0, NULL); } -#ifdef FEAT_IPV6 - if (family == IPADDR_UNSPEC || family == IPADDR_INET6) { - if (permanent_server_sockets && server_port) - server_sock_fd6 = prepare_socket(AF_INET6, server_port, 0); - if (!separate_client_sockets) { - if (client_port != server_port || !server_port) - client_sock_fd6 = prepare_socket(AF_INET6, client_port, 1); - else - client_sock_fd6 = server_sock_fd6; + + if (!separate_client_sockets) { + if (client_port != server_port || !server_port) { + client_sock_fd4 = open_socket(IPADDR_INET4, client_port, 1, NULL); + client_sock_fd6 = open_socket(IPADDR_INET6, client_port, 1, NULL); + } else { + client_sock_fd4 = server_sock_fd4; + client_sock_fd6 = server_sock_fd6; } } -#endif - if ((server_port && server_sock_fd4 == INVALID_SOCK_FD && - permanent_server_sockets -#ifdef FEAT_IPV6 - && server_sock_fd6 == INVALID_SOCK_FD -#endif - ) || (!separate_client_sockets && client_sock_fd4 == INVALID_SOCK_FD -#ifdef FEAT_IPV6 - && client_sock_fd6 == INVALID_SOCK_FD -#endif - )) { + if ((server_port && permanent_server_sockets && + server_sock_fd4 == INVALID_SOCK_FD && server_sock_fd6 == INVALID_SOCK_FD) || + (!separate_client_sockets && + client_sock_fd4 == INVALID_SOCK_FD && client_sock_fd6 == INVALID_SOCK_FD)) { LOG_FATAL("Could not open NTP sockets"); } } @@ -453,14 +232,11 @@ close_socket(client_sock_fd4); close_socket(server_sock_fd4); server_sock_fd4 = client_sock_fd4 = INVALID_SOCK_FD; -#ifdef FEAT_IPV6 + if (server_sock_fd6 != client_sock_fd6) close_socket(client_sock_fd6); close_socket(server_sock_fd6); server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD; -#endif - ARR_DestroyInstance(recv_headers); - ARR_DestroyInstance(recv_messages); #ifdef HAVE_LINUX_TIMESTAMPING NIO_Linux_Finalise(); @@ -475,25 +251,13 @@ NIO_OpenClientSocket(NTP_Remote_Address *remote_addr) { if (separate_client_sockets) { - int sock_fd = prepare_separate_client_socket(remote_addr->ip_addr.family); - - if (sock_fd == INVALID_SOCK_FD) - return INVALID_SOCK_FD; - - if (!connect_socket(sock_fd, remote_addr)) { - close_socket(sock_fd); - return INVALID_SOCK_FD; - } - - return sock_fd; + return open_separate_client_socket(remote_addr); } else { switch (remote_addr->ip_addr.family) { case IPADDR_INET4: return client_sock_fd4; -#ifdef FEAT_IPV6 case IPADDR_INET6: return client_sock_fd6; -#endif default: return INVALID_SOCK_FD; } @@ -510,20 +274,18 @@ if (permanent_server_sockets) return server_sock_fd4; if (server_sock_fd4 == INVALID_SOCK_FD) - server_sock_fd4 = prepare_socket(AF_INET, CNF_GetNTPPort(), 0); + server_sock_fd4 = open_socket(IPADDR_INET4, CNF_GetNTPPort(), 0, NULL); if (server_sock_fd4 != INVALID_SOCK_FD) server_sock_ref4++; return server_sock_fd4; -#ifdef FEAT_IPV6 case IPADDR_INET6: if (permanent_server_sockets) return server_sock_fd6; if (server_sock_fd6 == INVALID_SOCK_FD) - server_sock_fd6 = prepare_socket(AF_INET6, CNF_GetNTPPort(), 0); + server_sock_fd6 = open_socket(IPADDR_INET6, CNF_GetNTPPort(), 0, NULL); if (server_sock_fd6 != INVALID_SOCK_FD) server_sock_ref6++; return server_sock_fd6; -#endif default: return INVALID_SOCK_FD; } @@ -551,16 +313,12 @@ close_socket(server_sock_fd4); server_sock_fd4 = INVALID_SOCK_FD; } - } -#ifdef FEAT_IPV6 - else if (sock_fd == server_sock_fd6) { + } else if (sock_fd == server_sock_fd6) { if (--server_sock_ref6 <= 0) { close_socket(server_sock_fd6); server_sock_fd6 = INVALID_SOCK_FD; } - } -#endif - else { + } else { assert(0); } } @@ -571,11 +329,15 @@ NIO_IsServerSocket(int sock_fd) { return sock_fd != INVALID_SOCK_FD && - (sock_fd == server_sock_fd4 -#ifdef FEAT_IPV6 - || sock_fd == server_sock_fd6 -#endif - ); + (sock_fd == server_sock_fd4 || sock_fd == server_sock_fd6); +} + +/* ================================================== */ + +int +NIO_IsServerSocketOpen(void) +{ + return server_sock_fd4 != INVALID_SOCK_FD || server_sock_fd6 != INVALID_SOCK_FD; } /* ================================================== */ @@ -583,132 +345,60 @@ int NIO_IsServerConnectable(NTP_Remote_Address *remote_addr) { - int sock_fd, r; + int sock_fd; - sock_fd = prepare_separate_client_socket(remote_addr->ip_addr.family); + sock_fd = open_separate_client_socket(remote_addr); if (sock_fd == INVALID_SOCK_FD) return 0; - r = connect_socket(sock_fd, remote_addr); close_socket(sock_fd); - return r; + return 1; } /* ================================================== */ static void -process_message(struct msghdr *hdr, int length, int sock_fd) +process_message(SCK_Message *message, int sock_fd, int event) { - NTP_Remote_Address remote_addr; NTP_Local_Address local_addr; NTP_Local_Timestamp local_ts; struct timespec sched_ts; - struct cmsghdr *cmsg; SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL); local_ts.source = NTP_TS_DAEMON; sched_ts = local_ts.ts; - if (hdr->msg_namelen > sizeof (union sockaddr_in46)) { - DEBUG_LOG("Truncated source address"); + if (message->addr_type != SCK_ADDR_IP) { + DEBUG_LOG("Unexpected address type"); return; } - if (hdr->msg_namelen >= sizeof (((struct sockaddr *)hdr->msg_name)->sa_family)) { - UTI_SockaddrToIPAndPort((struct sockaddr *)hdr->msg_name, - &remote_addr.ip_addr, &remote_addr.port); - } else { - remote_addr.ip_addr.family = IPADDR_UNSPEC; - remote_addr.port = 0; - } - - local_addr.ip_addr.family = IPADDR_UNSPEC; - local_addr.if_index = INVALID_IF_INDEX; + local_addr.ip_addr = message->local_addr.ip; + local_addr.if_index = message->if_index;; local_addr.sock_fd = sock_fd; - if (hdr->msg_flags & MSG_TRUNC) { - DEBUG_LOG("Received truncated message from %s:%d", - UTI_IPToString(&remote_addr.ip_addr), remote_addr.port); - return; - } - - if (hdr->msg_flags & MSG_CTRUNC) { - DEBUG_LOG("Truncated control message"); - /* Continue */ - } - - for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) { -#ifdef HAVE_IN_PKTINFO - if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { - struct in_pktinfo ipi; - - memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi)); - local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_addr.s_addr); - local_addr.ip_addr.family = IPADDR_INET4; - local_addr.if_index = ipi.ipi_ifindex; - } -#elif defined(IP_RECVDSTADDR) - if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { - struct in_addr addr; - - memcpy(&addr, CMSG_DATA(cmsg), sizeof (addr)); - local_addr.ip_addr.addr.in4 = ntohl(addr.s_addr); - local_addr.ip_addr.family = IPADDR_INET4; - } -#endif - -#ifdef HAVE_IN6_PKTINFO - if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { - struct in6_pktinfo ipi; - - memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi)); - memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr, - sizeof (local_addr.ip_addr.addr.in6)); - local_addr.ip_addr.family = IPADDR_INET6; - local_addr.if_index = ipi.ipi6_ifindex; - } -#endif - -#ifdef SCM_TIMESTAMP - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) { - struct timeval tv; - struct timespec ts; - - memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv)); - UTI_TimevalToTimespec(&tv, &ts); - LCL_CookTime(&ts, &local_ts.ts, &local_ts.err); - local_ts.source = NTP_TS_KERNEL; - } -#endif - -#ifdef SCM_TIMESTAMPNS - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) { - struct timespec ts; - - memcpy(&ts, CMSG_DATA(cmsg), sizeof (ts)); - LCL_CookTime(&ts, &local_ts.ts, &local_ts.err); - local_ts.source = NTP_TS_KERNEL; - } -#endif - } - #ifdef HAVE_LINUX_TIMESTAMPING - if (NIO_Linux_ProcessMessage(&remote_addr, &local_addr, &local_ts, hdr, length)) + if (NIO_Linux_ProcessMessage(message, &local_addr, &local_ts, event)) return; +#else + if (!UTI_IsZeroTimespec(&message->timestamp.kernel)) { + LCL_CookTime(&message->timestamp.kernel, &local_ts.ts, &local_ts.err); + local_ts.source = NTP_TS_KERNEL; + } #endif - DEBUG_LOG("Received %d bytes from %s:%d to %s fd=%d if=%d tss=%u delay=%.9f", - length, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port, - UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd, local_addr.if_index, - local_ts.source, UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts)); + if (local_ts.source != NTP_TS_DAEMON) + DEBUG_LOG("Updated RX timestamp delay=%.9f tss=%u", + UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts), local_ts.source); /* Just ignore the packet if it's not of a recognized length */ - if (length < NTP_NORMAL_PACKET_LENGTH || length > sizeof (NTP_Receive_Buffer)) + if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet)) { + DEBUG_LOG("Unexpected length"); return; + } - NSR_ProcessRx(&remote_addr, &local_addr, &local_ts, - (NTP_Packet *)hdr->msg_iov[0].iov_base, length); + NSR_ProcessRx(&message->remote_addr.ip, &local_addr, &local_ts, message->data, message->length); } /* ================================================== */ @@ -716,68 +406,28 @@ static void read_from_socket(int sock_fd, int event, void *anything) { - /* This should only be called when there is something - to read, otherwise it may block */ - - struct MessageHeader *hdr; - unsigned int i, n; - int status, flags = 0; + SCK_Message *messages; + int i, received, flags = 0; #ifdef HAVE_LINUX_TIMESTAMPING if (NIO_Linux_ProcessEvent(sock_fd, event)) return; #endif - hdr = ARR_GetElements(recv_headers); - n = ARR_GetSize(recv_headers); - assert(n >= 1); - if (event == SCH_FILE_EXCEPTION) { #ifdef HAVE_LINUX_TIMESTAMPING - flags |= MSG_ERRQUEUE; + flags |= SCK_FLAG_MSG_ERRQUEUE; #else assert(0); #endif } -#ifdef HAVE_RECVMMSG - status = recvmmsg(sock_fd, hdr, n, flags | MSG_DONTWAIT, NULL); - if (status >= 0) - n = status; -#else - n = 1; - status = recvmsg(sock_fd, &hdr[0].msg_hdr, flags); - if (status >= 0) - hdr[0].msg_len = status; -#endif - - if (status < 0) { -#ifdef HAVE_LINUX_TIMESTAMPING - /* If reading from the error queue failed, the exception should be - for a socket error. Clear the error to avoid a busy loop. */ - if (flags & MSG_ERRQUEUE) { - int error = 0; - socklen_t len = sizeof (error); - - if (getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, &error, &len)) - DEBUG_LOG("Could not get SO_ERROR"); - if (error) - errno = error; - } -#endif - - DEBUG_LOG("Could not receive from fd %d : %s", sock_fd, - strerror(errno)); + messages = SCK_ReceiveMessages(sock_fd, flags, &received); + if (!messages) return; - } - for (i = 0; i < n; i++) { - hdr = ARR_GetElement(recv_headers, i); - process_message(&hdr->msg_hdr, hdr->msg_len, sock_fd); - } - - /* Restore the buffers to their original state */ - prepare_buffers(n); + for (i = 0; i < received; i++) + process_message(&messages[i], sock_fd, event); } /* ================================================== */ @@ -787,123 +437,47 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length, int process_tx) { - union sockaddr_in46 remote; - struct msghdr msg; - struct iovec iov; - struct cmsghdr *cmsg, cmsgbuf[CMSGBUF_SIZE / sizeof (struct cmsghdr)]; - int cmsglen; - socklen_t addrlen = 0; + SCK_Message message; assert(initialised); if (local_addr->sock_fd == INVALID_SOCK_FD) { - DEBUG_LOG("No socket to send to %s:%d", - UTI_IPToString(&remote_addr->ip_addr), remote_addr->port); + DEBUG_LOG("No socket to send to %s", UTI_IPSockAddrToString(remote_addr)); return 0; } - /* Don't set address with connected socket */ + SCK_InitMessage(&message, SCK_ADDR_IP); + + message.data = packet; + message.length = length; + + /* Specify remote address if the socket is not connected */ if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) { - addrlen = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port, - &remote.u); - if (!addrlen) - return 0; + message.remote_addr.ip.ip_addr = remote_addr->ip_addr; + message.remote_addr.ip.port = remote_addr->port; } - if (addrlen) { - msg.msg_name = &remote.u; - msg.msg_namelen = addrlen; - } else { - msg.msg_name = NULL; - msg.msg_namelen = 0; - } + message.local_addr.ip = local_addr->ip_addr; - iov.iov_base = packet; - iov.iov_len = length; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = cmsgbuf; - msg.msg_controllen = sizeof(cmsgbuf); - msg.msg_flags = 0; - cmsglen = 0; - -#ifdef HAVE_IN_PKTINFO - if (local_addr->ip_addr.family == IPADDR_INET4) { - struct in_pktinfo *ipi; - - cmsg = CMSG_FIRSTHDR(&msg); - memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_pktinfo))); - cmsglen += CMSG_SPACE(sizeof(struct in_pktinfo)); - - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_PKTINFO; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); - - ipi = (struct in_pktinfo *) CMSG_DATA(cmsg); - ipi->ipi_spec_dst.s_addr = htonl(local_addr->ip_addr.addr.in4); - if (local_addr->if_index != INVALID_IF_INDEX) - ipi->ipi_ifindex = local_addr->if_index; - } -#elif defined(IP_SENDSRCADDR) - /* Specify the IPv4 source address only if the socket is not bound */ - if (local_addr->ip_addr.family == IPADDR_INET4 && - local_addr->sock_fd == server_sock_fd4 && !bound_server_sock_fd4) { - struct in_addr *addr; - - cmsg = CMSG_FIRSTHDR(&msg); - memset(cmsg, 0, CMSG_SPACE(sizeof (struct in_addr))); - cmsglen += CMSG_SPACE(sizeof (struct in_addr)); - - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_SENDSRCADDR; - cmsg->cmsg_len = CMSG_LEN(sizeof (struct in_addr)); - - addr = (struct in_addr *)CMSG_DATA(cmsg); - addr->s_addr = htonl(local_addr->ip_addr.addr.in4); - } -#endif - -#ifdef HAVE_IN6_PKTINFO - if (local_addr->ip_addr.family == IPADDR_INET6) { - struct in6_pktinfo *ipi; - - cmsg = CMSG_FIRSTHDR(&msg); - memset(cmsg, 0, CMSG_SPACE(sizeof(struct in6_pktinfo))); - cmsglen += CMSG_SPACE(sizeof(struct in6_pktinfo)); - - cmsg->cmsg_level = IPPROTO_IPV6; - cmsg->cmsg_type = IPV6_PKTINFO; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); - - ipi = (struct in6_pktinfo *) CMSG_DATA(cmsg); - memcpy(&ipi->ipi6_addr.s6_addr, &local_addr->ip_addr.addr.in6, - sizeof(ipi->ipi6_addr.s6_addr)); - if (local_addr->if_index != INVALID_IF_INDEX) - ipi->ipi6_ifindex = local_addr->if_index; - } + /* Don't require responses to non-link-local addresses to use the same + interface */ + message.if_index = SCK_IsLinkLocalIPAddress(&message.remote_addr.ip.ip_addr) ? + local_addr->if_index : INVALID_IF_INDEX; + +#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR) + /* On FreeBSD a local IPv4 address cannot be specified on bound socket */ + if (message.local_addr.ip.family == IPADDR_INET4 && + (local_addr->sock_fd != server_sock_fd4 || bound_server_sock_fd4)) + message.local_addr.ip.family = IPADDR_UNSPEC; #endif #ifdef HAVE_LINUX_TIMESTAMPING if (process_tx) - cmsglen = NIO_Linux_RequestTxTimestamp(&msg, cmsglen, local_addr->sock_fd); + NIO_Linux_RequestTxTimestamp(&message, local_addr->sock_fd); #endif - msg.msg_controllen = cmsglen; - /* This is apparently required on some systems */ - if (!cmsglen) - msg.msg_control = NULL; - - if (sendmsg(local_addr->sock_fd, &msg, 0) < 0) { - DEBUG_LOG("Could not send to %s:%d from %s fd %d : %s", - UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, - UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd, - strerror(errno)); + if (!SCK_SendMessage(local_addr->sock_fd, &message, 0)) return 0; - } - - DEBUG_LOG("Sent %d bytes to %s:%d from %s fd %d", length, - UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, - UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd); return 1; } diff -Nru chrony-3.5/ntp_io.h chrony-4.1/ntp_io.h --- chrony-3.5/ntp_io.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/ntp_io.h 2021-05-12 11:06:15.000000000 +0000 @@ -33,7 +33,7 @@ #include "addressing.h" /* Function to initialise the module. */ -extern void NIO_Initialise(int family); +extern void NIO_Initialise(void); /* Function to finalise the module */ extern void NIO_Finalise(void); @@ -53,6 +53,9 @@ /* Function to check if socket is a server socket */ extern int NIO_IsServerSocket(int sock_fd); +/* Function to check if a server socket is currently open */ +extern int NIO_IsServerSocketOpen(void); + /* Function to check if client packets can be sent to a server */ extern int NIO_IsServerConnectable(NTP_Remote_Address *remote_addr); diff -Nru chrony-3.5/ntp_io_linux.c chrony-4.1/ntp_io_linux.c --- chrony-3.5/ntp_io_linux.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/ntp_io_linux.c 2021-05-12 11:06:15.000000000 +0000 @@ -29,7 +29,6 @@ #include "sysincl.h" #include -#include #include #include #include @@ -45,17 +44,10 @@ #include "ntp_io_linux.h" #include "ntp_sources.h" #include "sched.h" +#include "socket.h" #include "sys_linux.h" #include "util.h" -union sockaddr_in46 { - struct sockaddr_in in4; -#ifdef FEAT_IPV6 - struct sockaddr_in6 in6; -#endif - struct sockaddr u; -}; - struct Interface { char name[IF_NAMESIZE]; int if_index; @@ -133,7 +125,7 @@ return 1; } - sock_fd = socket(AF_INET, SOCK_DGRAM, 0); + sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0); if (sock_fd < 0) return 0; @@ -142,13 +134,13 @@ if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", conf_iface->name) >= sizeof (req.ifr_name)) { - close(sock_fd); + SCK_CloseSocket(sock_fd); return 0; } if (ioctl(sock_fd, SIOCGIFINDEX, &req)) { DEBUG_LOG("ioctl(%s) failed : %s", "SIOCGIFINDEX", strerror(errno)); - close(sock_fd); + SCK_CloseSocket(sock_fd); return 0; } @@ -159,7 +151,7 @@ if (ioctl(sock_fd, SIOCETHTOOL, &req)) { DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno)); - close(sock_fd); + SCK_CloseSocket(sock_fd); return 0; } @@ -167,13 +159,13 @@ SOF_TIMESTAMPING_RAW_HARDWARE; if ((ts_info.so_timestamping & req_hwts_flags) != req_hwts_flags) { DEBUG_LOG("HW timestamping not supported on %s", req.ifr_name); - close(sock_fd); + SCK_CloseSocket(sock_fd); return 0; } if (ts_info.phc_index < 0) { DEBUG_LOG("PHC missing on %s", req.ifr_name); - close(sock_fd); + SCK_CloseSocket(sock_fd); return 0; } @@ -208,7 +200,8 @@ req.ifr_data = (char *)&ts_config; if (ioctl(sock_fd, SIOCSHWTSTAMP, &req)) { - DEBUG_LOG("ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno)); + LOG(errno == EPERM ? LOGS_ERR : LOGS_DEBUG, + "ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno)); /* Check the current timestamping configuration in case this interface allows only reading of the configuration and it was already configured @@ -219,12 +212,12 @@ ts_config.tx_type != HWTSTAMP_TX_ON || ts_config.rx_filter != rx_filter) #endif { - close(sock_fd); + SCK_CloseSocket(sock_fd); return 0; } } - close(sock_fd); + SCK_CloseSocket(sock_fd); phc_fd = SYS_Linux_OpenPHC(NULL, ts_info.phc_index); if (phc_fd < 0) @@ -293,7 +286,7 @@ struct ifreq req; int sock_fd, link_speed; - sock_fd = socket(AF_INET, SOCK_DGRAM, 0); + sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0); if (sock_fd < 0) return; @@ -306,11 +299,11 @@ if (ioctl(sock_fd, SIOCETHTOOL, &req)) { DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno)); - close(sock_fd); + SCK_CloseSocket(sock_fd); return; } - close(sock_fd); + SCK_CloseSocket(sock_fd); link_speed = ethtool_cmd_speed(&cmd); @@ -328,17 +321,16 @@ { int sock_fd; - sock_fd = socket(AF_INET, SOCK_DGRAM, 0); + sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0); if (sock_fd < 0) return 0; - if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &option, sizeof (option)) < 0) { - DEBUG_LOG("Could not enable timestamping option %x", (unsigned int)option); - close(sock_fd); + if (!SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, option)) { + SCK_CloseSocket(sock_fd); return 0; } - close(sock_fd); + SCK_CloseSocket(sock_fd); return 1; } #endif @@ -350,19 +342,15 @@ { int sock_fd, events = 0; - if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 -#ifdef FEAT_IPV6 - && (sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0 -#endif - ) + sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0); + if (sock_fd < 0) return INVALID_SOCK_FD; if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, 1, &events)) { - close(sock_fd); + SCK_CloseSocket(sock_fd); return INVALID_SOCK_FD; } - UTI_FdSetCloexec(sock_fd); return sock_fd; } @@ -432,7 +420,7 @@ unsigned int i; if (dummy_rxts_socket != INVALID_SOCK_FD) - close(dummy_rxts_socket); + SCK_CloseSocket(dummy_rxts_socket); for (i = 0; i < ARR_GetSize(interfaces); i++) { iface = ARR_GetElement(interfaces, i); @@ -462,14 +450,12 @@ if (client_only || permanent_ts_options) flags |= ts_tx_flags; - if (setsockopt(sock_fd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, &val, sizeof (val)) < 0) { - LOG(LOGS_ERR, "Could not set %s socket option", "SO_SELECT_ERR_QUEUE"); + if (!SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, val)) { ts_flags = 0; return 0; } - if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof (flags)) < 0) { - LOG(LOGS_ERR, "Could not set %s socket option", "SO_TIMESTAMPING"); + if (!SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, flags)) { ts_flags = 0; return 0; } @@ -633,7 +619,6 @@ extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len) { unsigned char *msg_start = msg; - union sockaddr_in46 addr; remote_addr->ip_addr.family = IPADDR_UNSPEC; remote_addr->port = 0; @@ -656,19 +641,21 @@ /* Parse destination address and port from IPv4/IPv6 and UDP headers */ if (len >= 20 && msg[0] >> 4 == 4) { int ihl = (msg[0] & 0xf) * 4; + uint32_t addr; if (len < ihl + 8 || msg[9] != 17) return 0; - memcpy(&addr.in4.sin_addr.s_addr, msg + 16, sizeof (uint32_t)); - addr.in4.sin_port = *(uint16_t *)(msg + ihl + 2); - addr.in4.sin_family = AF_INET; + memcpy(&addr, msg + 16, sizeof (addr)); + remote_addr->ip_addr.addr.in4 = ntohl(addr); + remote_addr->port = ntohs(*(uint16_t *)(msg + ihl + 2)); + remote_addr->ip_addr.family = IPADDR_INET4; len -= ihl + 8, msg += ihl + 8; #ifdef FEAT_IPV6 } else if (len >= 48 && msg[0] >> 4 == 6) { int eh_len, next_header = msg[6]; - memcpy(&addr.in6.sin6_addr.s6_addr, msg + 24, 16); + memcpy(&remote_addr->ip_addr.addr.in6, msg + 24, sizeof (remote_addr->ip_addr.addr.in6)); len -= 40, msg += 40; /* Skip IPv6 extension headers if present */ @@ -700,16 +687,14 @@ len -= eh_len, msg += eh_len; } - addr.in6.sin6_port = *(uint16_t *)(msg + 2); - addr.in6.sin6_family = AF_INET6; + remote_addr->port = ntohs(*(uint16_t *)(msg + 2)); + remote_addr->ip_addr.family = IPADDR_INET6; len -= 8, msg += 8; #endif } else { return 0; } - UTI_SockaddrToIPAndPort(&addr.u, &remote_addr->ip_addr, &remote_addr->port); - /* Move the message to fix alignment of its fields */ if (len > 0) memmove(msg_start, msg, len); @@ -720,72 +705,39 @@ /* ================================================== */ int -NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, - NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length) +NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr, + NTP_Local_Timestamp *local_ts, int event) { struct Interface *iface; - struct cmsghdr *cmsg; int is_tx, ts_if_index, l2_length; - is_tx = hdr->msg_flags & MSG_ERRQUEUE; + is_tx = event == SCH_FILE_EXCEPTION; iface = NULL; - ts_if_index = local_addr->if_index; - l2_length = 0; - for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) { -#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING_PKTINFO) { - struct scm_ts_pktinfo ts_pktinfo; - - memcpy(&ts_pktinfo, CMSG_DATA(cmsg), sizeof (ts_pktinfo)); - - ts_if_index = ts_pktinfo.if_index; - l2_length = ts_pktinfo.pkt_length; - - DEBUG_LOG("Received HW timestamp info if=%d length=%d", ts_if_index, l2_length); + ts_if_index = message->timestamp.if_index; + if (ts_if_index == INVALID_IF_INDEX) + ts_if_index = message->if_index; + l2_length = message->timestamp.l2_length; + + if (!UTI_IsZeroTimespec(&message->timestamp.hw)) { + iface = get_interface(ts_if_index); + if (iface) { + process_hw_timestamp(iface, &message->timestamp.hw, local_ts, !is_tx ? message->length : 0, + message->remote_addr.ip.ip_addr.family, l2_length); + } else { + DEBUG_LOG("HW clock not found for interface %d", ts_if_index); } -#endif - - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) { - struct scm_timestamping ts3; - - memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3)); - - if (!UTI_IsZeroTimespec(&ts3.ts[2])) { - iface = get_interface(ts_if_index); - if (iface) { - process_hw_timestamp(iface, &ts3.ts[2], local_ts, !is_tx ? length : 0, - remote_addr->ip_addr.family, l2_length); - } else { - DEBUG_LOG("HW clock not found for interface %d", ts_if_index); - } - - /* If a HW transmit timestamp was received, resume processing - of non-error messages on this socket */ - if (is_tx) - resume_socket(local_addr->sock_fd); - } - if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&ts3.ts[0]) && - (!is_tx || UTI_IsZeroTimespec(&ts3.ts[2]))) { - LCL_CookTime(&ts3.ts[0], &local_ts->ts, &local_ts->err); - local_ts->source = NTP_TS_KERNEL; - } - } + /* If a HW transmit timestamp was received, resume processing + of non-error messages on this socket */ + if (is_tx) + resume_socket(local_addr->sock_fd); + } - if ((cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) || - (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_RECVERR)) { - struct sock_extended_err err; - - memcpy(&err, CMSG_DATA(cmsg), sizeof (err)); - - if (err.ee_errno != ENOMSG || err.ee_info != SCM_TSTAMP_SND || - err.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) { - DEBUG_LOG("Unknown extended error"); - /* Drop the message */ - return 1; - } - } + if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&message->timestamp.kernel) && + (!is_tx || UTI_IsZeroTimespec(&message->timestamp.hw))) { + LCL_CookTime(&message->timestamp.kernel, &local_ts->ts, &local_ts->err); + local_ts->source = NTP_TS_KERNEL; } /* If the kernel is slow with enabling RX timestamping, open a dummy @@ -803,19 +755,19 @@ /* The data from the error queue includes all layers up to UDP. We have to extract the UDP data and also the destination address with port as there currently doesn't seem to be a better way to get them both. */ - l2_length = length; - length = extract_udp_data(hdr->msg_iov[0].iov_base, remote_addr, length); + l2_length = message->length; + message->length = extract_udp_data(message->data, &message->remote_addr.ip, message->length); - DEBUG_LOG("Received %d (%d) bytes from error queue for %s:%d fd=%d if=%d tss=%u", - l2_length, length, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, - local_addr->sock_fd, local_addr->if_index, local_ts->source); + DEBUG_LOG("Extracted message for %s fd=%d len=%d", + UTI_IPSockAddrToString(&message->remote_addr.ip), + local_addr->sock_fd, message->length); /* Update assumed position of UDP data at layer 2 for next received packet */ - if (iface && length) { - if (remote_addr->ip_addr.family == IPADDR_INET4) - iface->l2_udp4_ntp_start = l2_length - length; - else if (remote_addr->ip_addr.family == IPADDR_INET6) - iface->l2_udp6_ntp_start = l2_length - length; + if (iface && message->length) { + if (message->remote_addr.ip.ip_addr.family == IPADDR_INET4) + iface->l2_udp4_ntp_start = l2_length - message->length; + else if (message->remote_addr.ip.ip_addr.family == IPADDR_INET6) + iface->l2_udp6_ntp_start = l2_length - message->length; } /* Drop the message if it has no timestamp or its processing failed */ @@ -824,24 +776,21 @@ return 1; } - if (length < NTP_NORMAL_PACKET_LENGTH) + if (message->length < NTP_HEADER_LENGTH) return 1; - NSR_ProcessTx(remote_addr, local_addr, local_ts, - (NTP_Packet *)hdr->msg_iov[0].iov_base, length); + NSR_ProcessTx(&message->remote_addr.ip, local_addr, local_ts, message->data, message->length); return 1; } /* ================================================== */ -int -NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd) +void +NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd) { - struct cmsghdr *cmsg; - if (!ts_flags) - return cmsglen; + return; /* If a HW transmit timestamp is requested on a client socket, monitor events on the socket in order to avoid processing of a fast response @@ -851,27 +800,9 @@ /* Check if TX timestamping is disabled on this socket */ if (permanent_ts_options || !NIO_IsServerSocket(sock_fd)) - return cmsglen; - - /* Add control message that will enable TX timestamping for this message. - Don't use CMSG_NXTHDR as the one in glibc is buggy for creating new - control messages. */ - - cmsg = CMSG_FIRSTHDR(msg); - if (!cmsg || cmsglen + CMSG_SPACE(sizeof (ts_tx_flags)) > msg->msg_controllen) - return cmsglen; - - cmsg = (struct cmsghdr *)((char *)cmsg + cmsglen); - memset(cmsg, 0, CMSG_SPACE(sizeof (ts_tx_flags))); - cmsglen += CMSG_SPACE(sizeof (ts_tx_flags)); - - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SO_TIMESTAMPING; - cmsg->cmsg_len = CMSG_LEN(sizeof (ts_tx_flags)); - - memcpy(CMSG_DATA(cmsg), &ts_tx_flags, sizeof (ts_tx_flags)); + return; - return cmsglen; + message->timestamp.tx_flags = ts_tx_flags; } /* ================================================== */ diff -Nru chrony-3.5/ntp_io_linux.h chrony-4.1/ntp_io_linux.h --- chrony-3.5/ntp_io_linux.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/ntp_io_linux.h 2021-05-12 11:06:15.000000000 +0000 @@ -27,6 +27,8 @@ #ifndef GOT_NTP_IO_LINUX_H #define GOT_NTP_IO_LINUX_H +#include "socket.h" + extern void NIO_Linux_Initialise(void); extern void NIO_Linux_Finalise(void); @@ -35,10 +37,10 @@ extern int NIO_Linux_ProcessEvent(int sock_fd, int event); -extern int NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, - NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length); +extern int NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr, + NTP_Local_Timestamp *local_ts, int event); -extern int NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd); +extern void NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd); extern void NIO_Linux_NotifySocketClosing(int sock_fd); diff -Nru chrony-3.5/ntp_signd.c chrony-4.1/ntp_signd.c --- chrony-3.5/ntp_signd.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/ntp_signd.c 2021-05-12 11:06:15.000000000 +0000 @@ -34,6 +34,7 @@ #include "ntp_io.h" #include "ntp_signd.h" #include "sched.h" +#include "socket.h" #include "util.h" /* Declarations per samba/source4/librpc/idl/ntp_signd.idl */ @@ -90,19 +91,11 @@ static unsigned int queue_head; static unsigned int queue_tail; -#define INVALID_SOCK_FD -1 +#define INVALID_SOCK_FD (-6) /* Unix domain socket connected to ntp_signd */ static int sock_fd; -#define MIN_AUTH_DELAY 1.0e-5 -#define MAX_AUTH_DELAY 1.0e-2 - -/* Average time needed for signing one packet. This is used to adjust the - transmit timestamp in NTP packets. The timestamp won't be very accurate as - the delay is variable, but it should be good enough for MS-SNTP clients. */ -static double auth_delay; - /* Flag indicating if the MS-SNTP authentication is enabled */ static int enabled; @@ -116,7 +109,7 @@ close_socket(void) { SCH_RemoveFileHandler(sock_fd); - close(sock_fd); + SCK_CloseSocket(sock_fd); sock_fd = INVALID_SOCK_FD; /* Empty the queue */ @@ -128,35 +121,23 @@ static int open_socket(void) { - struct sockaddr_un s; + char path[PATH_MAX]; - if (sock_fd >= 0) + if (sock_fd != INVALID_SOCK_FD) return 1; - sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock_fd < 0) { - DEBUG_LOG("Could not open signd socket : %s", strerror(errno)); - return 0; - } - - UTI_FdSetCloexec(sock_fd); - SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, NULL); - - s.sun_family = AF_UNIX; - if (snprintf(s.sun_path, sizeof (s.sun_path), "%s/socket", - CNF_GetNtpSigndSocket()) >= sizeof (s.sun_path)) { + if (snprintf(path, sizeof (path), "%s/socket", CNF_GetNtpSigndSocket()) >= sizeof (path)) { DEBUG_LOG("signd socket path too long"); - close_socket(); return 0; } - if (connect(sock_fd, (struct sockaddr *)&s, sizeof (s)) < 0) { - DEBUG_LOG("Could not connect to signd : %s", strerror(errno)); - close_socket(); + sock_fd = SCK_OpenUnixStreamSocket(path, NULL, 0); + if (sock_fd < 0) { + sock_fd = INVALID_SOCK_FD; return 0; } - DEBUG_LOG("Connected to signd"); + SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, NULL); return 1; } @@ -194,10 +175,6 @@ NIO_SendPacket(&inst->response.signed_packet, &inst->remote_addr, &inst->local_addr, ntohl(inst->response.length) + sizeof (inst->response.length) - offsetof(SigndResponse, signed_packet), 0); - - /* Update exponential moving average of the authentication delay */ - delay = CLAMP(MIN_AUTH_DELAY, delay, MAX_AUTH_DELAY); - auth_delay += 0.1 * (delay - auth_delay); } /* ================================================== */ @@ -218,16 +195,14 @@ if (!inst->sent) SCH_GetLastEventTime(NULL, NULL, &inst->request_ts); - s = send(sock_fd, (char *)&inst->request + inst->sent, - inst->request_length - inst->sent, 0); + s = SCK_Send(sock_fd, (char *)&inst->request + inst->sent, + inst->request_length - inst->sent, 0); if (s < 0) { - DEBUG_LOG("signd socket error: %s", strerror(errno)); close_socket(); return; } - DEBUG_LOG("Sent %d bytes to signd", s); inst->sent += s; /* Try again later if the request is not complete yet */ @@ -246,20 +221,14 @@ } assert(inst->received < sizeof (inst->response)); - s = recv(sock_fd, (char *)&inst->response + inst->received, - sizeof (inst->response) - inst->received, 0); + s = SCK_Receive(sock_fd, (char *)&inst->response + inst->received, + sizeof (inst->response) - inst->received, 0); if (s <= 0) { - if (s < 0) - DEBUG_LOG("signd socket error: %s", strerror(errno)); - else - DEBUG_LOG("signd socket closed"); - close_socket(); return; } - DEBUG_LOG("Received %d bytes from signd", s); inst->received += s; if (inst->received < sizeof (inst->response.length)) @@ -293,7 +262,6 @@ NSD_Initialise() { sock_fd = INVALID_SOCK_FD; - auth_delay = MIN_AUTH_DELAY; enabled = CNF_GetNtpSigndSocket() && CNF_GetNtpSigndSocket()[0]; if (!enabled) @@ -320,15 +288,9 @@ /* ================================================== */ -extern int NSD_GetAuthDelay(uint32_t key_id) -{ - return 1.0e9 * auth_delay; -} - -/* ================================================== */ - int -NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length) +NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info, + NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr) { SignInstance *inst; @@ -342,7 +304,7 @@ return 0; } - if (length != NTP_NORMAL_PACKET_LENGTH) { + if (info->length != NTP_HEADER_LENGTH) { DEBUG_LOG("Invalid packet length"); return 0; } @@ -355,7 +317,7 @@ inst->local_addr = *local_addr; inst->sent = 0; inst->received = 0; - inst->request_length = offsetof(SigndRequest, packet_to_sign) + length; + inst->request_length = offsetof(SigndRequest, packet_to_sign) + info->length; /* The length field doesn't include itself */ inst->request.length = htonl(inst->request_length - sizeof (inst->request.length)); @@ -365,7 +327,7 @@ inst->request._pad = 0; inst->request.key_id = htonl(key_id); - memcpy(&inst->request.packet_to_sign, packet, length); + memcpy(&inst->request.packet_to_sign, packet, info->length); /* Enable output if there was no pending request */ if (IS_QUEUE_EMPTY()) diff -Nru chrony-3.5/ntp_signd.h chrony-4.1/ntp_signd.h --- chrony-3.5/ntp_signd.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/ntp_signd.h 2021-05-12 11:06:15.000000000 +0000 @@ -35,10 +35,8 @@ /* Finalisation function */ extern void NSD_Finalise(void); -/* Function to get an estimate of delay due to signing */ -extern int NSD_GetAuthDelay(uint32_t key_id); - /* Function to sign an NTP packet and send it */ -extern int NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length); +extern int NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info, + NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr); #endif diff -Nru chrony-3.5/ntp_sources.c chrony-4.1/ntp_sources.c --- chrony-3.5/ntp_sources.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/ntp_sources.c 2021-05-12 11:06:15.000000000 +0000 @@ -3,7 +3,7 @@ ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 - * Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016 + * Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016, 2020-2021 * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -34,6 +34,7 @@ #include "array.h" #include "ntp_sources.h" #include "ntp_core.h" +#include "ntp_io.h" #include "util.h" #include "logging.h" #include "local.h" @@ -44,17 +45,25 @@ /* ================================================== */ +/* Maximum number of sources */ +#define MAX_SOURCES 65536 + /* Record type private to this file, used to store information about particular sources */ typedef struct { NTP_Remote_Address *remote_addr; /* The address of this source, non-NULL - means this slot in table is in use */ + means this slot in table is in use + (an IPADDR_ID address means the address + is not resolved yet) */ NCR_Instance data; /* Data for the protocol engine for this source */ - char *name; /* Name of the source, may be NULL */ - int pool; /* Number of the pool from which was this source + char *name; /* Name of the source as it was specified + (may be an IP address) */ + int pool_id; /* ID of the pool from which was this source added or INVALID_POOL */ int tentative; /* Flag indicating there was no valid response received from the source yet */ + uint32_t conf_id; /* Configuration ID, which can be shared with + different sources in case of a pool */ } SourceRecord; /* Hash table of SourceRecord, its size is a power of two and it's never @@ -67,21 +76,27 @@ /* Flag indicating new sources will be started automatically when added */ static int auto_start_sources = 0; -/* Source with unknown address (which may be resolved later) */ +/* Flag indicating a record is currently being modified */ +static int record_lock; + +/* Last assigned address ID */ +static uint32_t last_address_id = 0; + +/* Last assigned configuration ID */ +static uint32_t last_conf_id = 0; + +/* Source scheduled for name resolving (first resolving or replacement) */ struct UnresolvedSource { + /* Current address of the source (IPADDR_ID is used for a single source + with unknown address and IPADDR_UNSPEC for a pool of sources) */ + NTP_Remote_Address address; + /* ID of the pool if not a single source */ + int pool_id; + /* Name to be resolved */ char *name; - int port; + /* Flag indicating addresses should be used in a random order */ int random_order; - int replacement; - union { - struct { - NTP_Source_Type type; - SourceParameters params; - int pool; - int max_new_sources; - } new_source; - NTP_Remote_Address replace_source; - }; + /* Next unresolved source in the list */ struct UnresolvedSource *next; }; @@ -92,6 +107,7 @@ static struct UnresolvedSource *unresolved_sources = NULL; static int resolving_interval = 0; +static int resolving_restart = 0; static SCH_TimeoutID resolving_id; static struct UnresolvedSource *resolving_source = NULL; static NSR_SourceResolvingEndHandler resolving_end_handler = NULL; @@ -101,21 +117,37 @@ /* Pool of sources with the same name */ struct SourcePool { - /* Number of sources added from this pool (ignoring tentative sources) */ + /* Number of all sources from the pool */ int sources; - /* Maximum number of sources */ + /* Number of sources with unresolved address */ + int unresolved_sources; + /* Number of non-tentative sources */ + int confirmed_sources; + /* Maximum number of confirmed sources */ int max_sources; }; -/* Array of SourcePool */ +/* Array of SourcePool (indexed by their ID) */ static ARR_Instance pools; +/* Requested update of a source's address */ +struct AddressUpdate { + NTP_Remote_Address old_address; + NTP_Remote_Address new_address; +}; + +/* Update saved when record_lock is true */ +static struct AddressUpdate saved_address_update; + /* ================================================== */ /* Forward prototypes */ -static void resolve_sources(void *arg); +static void resolve_sources(void); static void rehash_records(void); +static void handle_saved_address_update(void); static void clean_source_record(SourceRecord *record); +static void remove_pool_sources(int pool_id, int tentative, int unresolved); +static void remove_unresolved_source(struct UnresolvedSource *us); static void slew_sources(struct timespec *raw, @@ -140,6 +172,14 @@ /* ================================================== */ +static struct SourcePool * +get_pool(unsigned index) +{ + return (struct SourcePool *)ARR_GetElement(pools, index); +} + +/* ================================================== */ + void NSR_Initialise(void) { @@ -159,63 +199,44 @@ void NSR_Finalise(void) { - SourceRecord *record; - struct UnresolvedSource *us; - unsigned int i; + NSR_RemoveAllSources(); - ARR_DestroyInstance(pools); - - for (i = 0; i < ARR_GetSize(records); i++) { - record = get_record(i); - if (record->remote_addr) - clean_source_record(record); - } + LCL_RemoveParameterChangeHandler(slew_sources, NULL); ARR_DestroyInstance(records); + ARR_DestroyInstance(pools); - while (unresolved_sources) { - us = unresolved_sources; - unresolved_sources = us->next; - Free(us->name); - Free(us); - } + while (unresolved_sources) + remove_unresolved_source(unresolved_sources); initialised = 0; } /* ================================================== */ -/* Return slot number and whether the IP address was matched or not. - found = 0 => Neither IP nor port matched, empty slot returned - found = 1 => Only IP matched, port doesn't match - found = 2 => Both IP and port matched. - - It is assumed that there can only ever be one record for a - particular IP address. (If a different port comes up, it probably - means someone is running ntpdate -d or something). Thus, if we - match the IP address we stop the search regardless of whether the - port number matches. +/* Find a slot matching an IP address. It is assumed that there can + only ever be one record for a particular IP address. */ - */ - -static void -find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found) +static int +find_slot(IPAddr *ip_addr, int *slot) { SourceRecord *record; uint32_t hash; unsigned int i, size; - unsigned short port; size = ARR_GetSize(records); *slot = 0; - *found = 0; - if (remote_addr->ip_addr.family != IPADDR_INET4 && - remote_addr->ip_addr.family != IPADDR_INET6) - return; + switch (ip_addr->family) { + case IPADDR_INET4: + case IPADDR_INET6: + case IPADDR_ID: + break; + default: + return 0; + } - hash = UTI_IPToHash(&remote_addr->ip_addr); - port = remote_addr->port; + hash = UTI_IPToHash(ip_addr); for (i = 0; i < size / 2; i++) { /* Use quadratic probing */ @@ -225,12 +246,26 @@ if (!record->remote_addr) break; - if (!UTI_CompareIPs(&record->remote_addr->ip_addr, - &remote_addr->ip_addr, NULL)) { - *found = record->remote_addr->port == port ? 2 : 1; - return; - } + if (UTI_CompareIPs(&record->remote_addr->ip_addr, ip_addr, NULL) == 0) + return 1; } + + return 0; +} + +/* ================================================== */ +/* Find a slot matching an IP address and port. The function returns: + 0 => IP not matched, empty slot returned if a valid address was provided + 1 => Only IP matched, port doesn't match + 2 => Both IP and port matched. */ + +static int +find_slot2(NTP_Remote_Address *remote_addr, int *slot) +{ + if (!find_slot(&remote_addr->ip_addr, slot)) + return 0; + + return get_record(*slot)->remote_addr->port == remote_addr->port ? 2 : 1; } /* ================================================== */ @@ -249,7 +284,9 @@ { SourceRecord *temp_records; unsigned int i, old_size, new_size; - int slot, found; + int slot; + + assert(!record_lock); old_size = ARR_GetSize(records); @@ -269,8 +306,8 @@ if (!temp_records[i].remote_addr) continue; - find_slot(temp_records[i].remote_addr, &slot, &found); - assert(!found); + if (find_slot2(temp_records[i].remote_addr, &slot) != 0) + assert(0); *get_record(slot) = temp_records[i]; } @@ -282,40 +319,62 @@ /* Procedure to add a new source */ static NSR_Status -add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type, SourceParameters *params, int pool) +add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type, + SourceParameters *params, int pool_id, uint32_t conf_id) { SourceRecord *record; - int slot, found; + int slot; assert(initialised); /* Find empty bin & check that we don't have the address already */ - find_slot(remote_addr, &slot, &found); - if (found) { + if (find_slot2(remote_addr, &slot) != 0) { return NSR_AlreadyInUse; + } else if (!name && !UTI_IsIPReal(&remote_addr->ip_addr)) { + /* Name is required for non-real addresses */ + return NSR_InvalidName; + } else if (n_sources >= MAX_SOURCES) { + return NSR_TooManySources; } else { if (remote_addr->ip_addr.family != IPADDR_INET4 && - remote_addr->ip_addr.family != IPADDR_INET6) { + remote_addr->ip_addr.family != IPADDR_INET6 && + remote_addr->ip_addr.family != IPADDR_ID) { return NSR_InvalidAF; } else { n_sources++; if (!check_hashtable_size(n_sources, ARR_GetSize(records))) { rehash_records(); - find_slot(remote_addr, &slot, &found); + if (find_slot2(remote_addr, &slot) != 0) + assert(0); } - assert(!found); + assert(!record_lock); + record_lock = 1; + record = get_record(slot); - record->data = NCR_GetInstance(remote_addr, type, params); + assert(!name || !UTI_IsStringIP(name)); + record->name = Strdup(name ? name : UTI_IPToString(&remote_addr->ip_addr)); + record->data = NCR_CreateInstance(remote_addr, type, params, record->name); record->remote_addr = NCR_GetRemoteAddress(record->data); - record->name = name ? Strdup(name) : NULL; - record->pool = pool; + record->pool_id = pool_id; record->tentative = 1; + record->conf_id = conf_id; + + record_lock = 0; - if (auto_start_sources) + if (record->pool_id != INVALID_POOL) { + get_pool(record->pool_id)->sources++; + if (!UTI_IsIPReal(&remote_addr->ip_addr)) + get_pool(record->pool_id)->unresolved_sources++; + } + + if (auto_start_sources && UTI_IsIPReal(&remote_addr->ip_addr)) NCR_StartInstance(record->data); + /* The new instance is allowed to change its address immediately */ + handle_saved_address_update(); + return NSR_Success; } } @@ -324,39 +383,64 @@ /* ================================================== */ static NSR_Status -replace_source(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr) +change_source_address(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr, + int replacement) { int slot1, slot2, found; SourceRecord *record; - struct SourcePool *pool; + LOG_Severity severity; + char *name; - find_slot(old_addr, &slot1, &found); - if (!found) + found = find_slot2(old_addr, &slot1); + if (found != 2) return NSR_NoSuchSource; - find_slot(new_addr, &slot2, &found); - if (found) + /* Make sure there is no other source using the new address (with the same + or different port), but allow a source to have its port changed */ + found = find_slot2(new_addr, &slot2); + if (found == 2 || (found != 0 && slot1 != slot2)) return NSR_AlreadyInUse; + assert(!record_lock); + record_lock = 1; + record = get_record(slot1); - NCR_ChangeRemoteAddress(record->data, new_addr); - record->remote_addr = NCR_GetRemoteAddress(record->data); + NCR_ChangeRemoteAddress(record->data, new_addr, !replacement); + + if (record->remote_addr != NCR_GetRemoteAddress(record->data) || + UTI_CompareIPs(&record->remote_addr->ip_addr, &new_addr->ip_addr, NULL) != 0) + assert(0); + + if (!UTI_IsIPReal(&old_addr->ip_addr) && UTI_IsIPReal(&new_addr->ip_addr)) { + if (auto_start_sources) + NCR_StartInstance(record->data); + if (record->pool_id != INVALID_POOL) + get_pool(record->pool_id)->unresolved_sources--; + } if (!record->tentative) { record->tentative = 1; - if (record->pool != INVALID_POOL) { - pool = ARR_GetElement(pools, record->pool); - pool->sources--; - } + if (record->pool_id != INVALID_POOL) + get_pool(record->pool_id)->confirmed_sources--; } - /* The hash table must be rebuilt for the new address */ - rehash_records(); + record_lock = 0; - LOG(LOGS_INFO, "Source %s replaced with %s", - UTI_IPToString(&old_addr->ip_addr), - UTI_IPToString(&new_addr->ip_addr)); + name = record->name; + severity = UTI_IsIPReal(&old_addr->ip_addr) ? LOGS_INFO : LOGS_DEBUG; + + if (found == 0) { + /* The hash table must be rebuilt for the changed address */ + rehash_records(); + + LOG(severity, "Source %s %s %s (%s)", UTI_IPToString(&old_addr->ip_addr), + replacement ? "replaced with" : "changed to", + UTI_IPToString(&new_addr->ip_addr), name); + } else { + LOG(severity, "Source %s (%s) changed port to %d", + UTI_IPToString(&new_addr->ip_addr), name, new_addr->port); + } return NSR_Success; } @@ -364,30 +448,74 @@ /* ================================================== */ static void +handle_saved_address_update(void) +{ + if (!UTI_IsIPReal(&saved_address_update.old_address.ip_addr)) + return; + + if (change_source_address(&saved_address_update.old_address, + &saved_address_update.new_address, 0) != NSR_Success) + /* This is expected to happen only if the old address is wrong */ + LOG(LOGS_ERR, "Could not change %s to %s", + UTI_IPSockAddrToString(&saved_address_update.old_address), + UTI_IPSockAddrToString(&saved_address_update.new_address)); + + saved_address_update.old_address.ip_addr.family = IPADDR_UNSPEC; +} + +/* ================================================== */ + +static int +replace_source_connectable(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr) +{ + if (!NIO_IsServerConnectable(new_addr)) { + DEBUG_LOG("%s not connectable", UTI_IPToString(&new_addr->ip_addr)); + return 0; + } + + if (change_source_address(old_addr, new_addr, 1) == NSR_AlreadyInUse) + return 0; + + handle_saved_address_update(); + + return 1; +} + +/* ================================================== */ + +static void process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs) { - NTP_Remote_Address address; - int i, added; + NTP_Remote_Address old_addr, new_addr; + SourceRecord *record; unsigned short first = 0; + int i, j; if (us->random_order) UTI_GetRandomBytes(&first, sizeof (first)); - for (i = added = 0; i < n_addrs; i++) { - address.ip_addr = ip_addrs[((unsigned int)i + first) % n_addrs]; - address.port = us->port; + for (i = 0; i < n_addrs; i++) { + new_addr.ip_addr = ip_addrs[((unsigned int)i + first) % n_addrs]; - DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&address.ip_addr)); + DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&new_addr.ip_addr)); - if (us->replacement) { - if (replace_source(&us->replace_source, &address) != NSR_AlreadyInUse) + if (us->pool_id != INVALID_POOL) { + /* In the pool resolving mode, try to replace a source from + the pool which does not have a real address yet */ + for (j = 0; j < ARR_GetSize(records); j++) { + record = get_record(j); + if (!record->remote_addr || record->pool_id != us->pool_id || + UTI_IsIPReal(&record->remote_addr->ip_addr)) + continue; + old_addr = *record->remote_addr; + new_addr.port = old_addr.port; + if (replace_source_connectable(&old_addr, &new_addr)) + ; break; + } } else { - if (add_source(&address, us->name, us->new_source.type, &us->new_source.params, - us->new_source.pool) == NSR_Success) - added++; - - if (added >= us->new_source.max_new_sources) + new_addr.port = us->address.port; + if (replace_source_connectable(&us->address, &new_addr)) break; } } @@ -395,14 +523,40 @@ /* ================================================== */ +static int +is_resolved(struct UnresolvedSource *us) +{ + int slot; + + if (us->pool_id != INVALID_POOL) { + return get_pool(us->pool_id)->unresolved_sources <= 0; + } else { + /* If the address is no longer present, it was removed or replaced + (i.e. resolved) */ + return find_slot2(&us->address, &slot) == 0; + } +} + +/* ================================================== */ + +static void +resolve_sources_timeout(void *arg) +{ + resolving_id = 0; + resolve_sources(); +} + +/* ================================================== */ + static void name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *anything) { - struct UnresolvedSource *us, **i, *next; + struct UnresolvedSource *us, *next; us = (struct UnresolvedSource *)anything; assert(us == resolving_source); + assert(resolving_id == 0); DEBUG_LOG("%s resolved to %d addrs", us->name, n_addrs); @@ -421,17 +575,16 @@ next = us->next; - /* Remove the source from the list on success or failure, replacements - are removed on any status */ - if (us->replacement || status != DNS_TryAgain) { - for (i = &unresolved_sources; *i; i = &(*i)->next) { - if (*i == us) { - *i = us->next; - Free(us->name); - Free(us); - break; - } - } + /* Don't repeat the resolving if it (permanently) failed, it was a + replacement of a real address, or all addresses are already resolved */ + if (status == DNS_Failure || UTI_IsIPReal(&us->address.ip_addr) || is_resolved(us)) + remove_unresolved_source(us); + + /* If a restart was requested and this was the last source in the list, + start with the first source again (if there still is one) */ + if (!next && resolving_restart) { + next = unresolved_sources; + resolving_restart = 0; } resolving_source = next; @@ -444,12 +597,10 @@ /* This was the last source in the list. If some sources couldn't be resolved, try again in exponentially increasing interval. */ if (unresolved_sources) { - if (resolving_interval < MIN_RESOLVE_INTERVAL) - resolving_interval = MIN_RESOLVE_INTERVAL; - else if (resolving_interval < MAX_RESOLVE_INTERVAL) - resolving_interval++; - resolving_id = SCH_AddTimeoutByDelay(RESOLVE_INTERVAL_UNIT * - (1 << resolving_interval), resolve_sources, NULL); + resolving_interval = CLAMP(MIN_RESOLVE_INTERVAL, resolving_interval + 1, + MAX_RESOLVE_INTERVAL); + resolving_id = SCH_AddTimeoutByDelay(RESOLVE_INTERVAL_UNIT * (1 << resolving_interval), + resolve_sources_timeout, NULL); } else { resolving_interval = 0; } @@ -463,12 +614,22 @@ /* ================================================== */ static void -resolve_sources(void *arg) +resolve_sources(void) { - struct UnresolvedSource *us; + struct UnresolvedSource *us, *next, *i; assert(!resolving_source); + /* Remove sources that don't need to be resolved anymore */ + for (i = unresolved_sources; i; i = next) { + next = i->next; + if (is_resolved(i)) + remove_unresolved_source(i); + } + + if (!unresolved_sources) + return; + PRV_ReloadDNS(); /* Start with the first source in the list, name_resolve_handler @@ -495,49 +656,132 @@ /* ================================================== */ +static void +remove_unresolved_source(struct UnresolvedSource *us) +{ + struct UnresolvedSource **i; + + for (i = &unresolved_sources; *i; i = &(*i)->next) { + if (*i == us) { + *i = us->next; + Free(us->name); + Free(us); + break; + } + } +} + +/* ================================================== */ + +static int get_unused_pool_id(void) +{ + struct UnresolvedSource *us; + int i; + + for (i = 0; i < ARR_GetSize(pools); i++) { + if (get_pool(i)->sources > 0) + continue; + + /* Make sure there is no name waiting to be resolved using this pool */ + for (us = unresolved_sources; us; us = us->next) { + if (us->pool_id == i) + break; + } + if (us) + continue; + + return i; + } + + return INVALID_POOL; +} + +/* ================================================== */ + NSR_Status -NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params) +NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, + SourceParameters *params, uint32_t *conf_id) { - return add_source(remote_addr, NULL, type, params, INVALID_POOL); + NSR_Status s; + + s = add_source(remote_addr, NULL, type, params, INVALID_POOL, last_conf_id + 1); + if (s != NSR_Success) + return s; + + last_conf_id++; + if (conf_id) + *conf_id = last_conf_id; + + return s; } /* ================================================== */ -void -NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, SourceParameters *params) +NSR_Status +NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, + SourceParameters *params, uint32_t *conf_id) { struct UnresolvedSource *us; struct SourcePool *sp; NTP_Remote_Address remote_addr; + int i, new_sources, pool_id; - /* If the name is an IP address, don't bother with full resolving now - or later when trying to replace the source */ + /* If the name is an IP address, add the source with the address directly */ if (UTI_StringToIP(name, &remote_addr.ip_addr)) { remote_addr.port = port; - NSR_AddSource(&remote_addr, type, params); - return; + return NSR_AddSource(&remote_addr, type, params, conf_id); + } + + /* Make sure the name is at least printable and has no spaces */ + for (i = 0; name[i] != '\0'; i++) { + if (!isgraph((unsigned char)name[i])) + return NSR_InvalidName; } us = MallocNew(struct UnresolvedSource); us->name = Strdup(name); - us->port = port; us->random_order = 0; - us->replacement = 0; - us->new_source.type = type; - us->new_source.params = *params; + + remote_addr.ip_addr.family = IPADDR_ID; + remote_addr.ip_addr.addr.id = ++last_address_id; + remote_addr.port = port; if (!pool) { - us->new_source.pool = INVALID_POOL; - us->new_source.max_new_sources = 1; + us->pool_id = INVALID_POOL; + us->address = remote_addr; + new_sources = 1; } else { - sp = (struct SourcePool *)ARR_GetNewElement(pools); + pool_id = get_unused_pool_id(); + if (pool_id != INVALID_POOL) { + sp = get_pool(pool_id); + } else { + sp = ARR_GetNewElement(pools); + pool_id = ARR_GetSize(pools) - 1; + } + sp->sources = 0; - sp->max_sources = params->max_sources; - us->new_source.pool = ARR_GetSize(pools) - 1; - us->new_source.max_new_sources = MAX_POOL_SOURCES; + sp->unresolved_sources = 0; + sp->confirmed_sources = 0; + sp->max_sources = CLAMP(1, params->max_sources, MAX_POOL_SOURCES); + us->pool_id = pool_id; + us->address.ip_addr.family = IPADDR_UNSPEC; + new_sources = MIN(2 * sp->max_sources, MAX_POOL_SOURCES); } append_unresolved_source(us); + + last_conf_id++; + if (conf_id) + *conf_id = last_conf_id; + + for (i = 0; i < new_sources; i++) { + if (i > 0) + remote_addr.ip_addr.addr.id = ++last_address_id; + if (add_source(&remote_addr, name, type, params, us->pool_id, last_conf_id) != NSR_Success) + return NSR_TooManySources; + } + + return NSR_UnresolvedName; } /* ================================================== */ @@ -555,13 +799,17 @@ { /* Try to resolve unresolved sources now */ if (unresolved_sources) { - /* Make sure no resolving is currently running */ + /* Allow only one resolving to be running at a time */ if (!resolving_source) { - if (resolving_interval) { + if (resolving_id != 0) { SCH_RemoveTimeout(resolving_id); + resolving_id = 0; resolving_interval--; } - resolve_sources(NULL); + resolve_sources(); + } else { + /* Try again as soon as the current resolving ends */ + resolving_restart = 1; } } else { /* No unresolved sources, we are done */ @@ -574,10 +822,12 @@ void NSR_StartSources(void) { + NTP_Remote_Address *addr; unsigned int i; for (i = 0; i < ARR_GetSize(records); i++) { - if (!get_record(i)->remote_addr) + addr = get_record(i)->remote_addr; + if (!addr || !UTI_IsIPReal(&addr->ip_addr)) continue; NCR_StartInstance(get_record(i)->data); } @@ -596,10 +846,22 @@ clean_source_record(SourceRecord *record) { assert(record->remote_addr); + + if (record->pool_id != INVALID_POOL) { + struct SourcePool *pool = get_pool(record->pool_id); + + pool->sources--; + if (!UTI_IsIPReal(&record->remote_addr->ip_addr)) + pool->unresolved_sources--; + if (!record->tentative) + pool->confirmed_sources--; + if (pool->max_sources > pool->sources) + pool->max_sources = pool->sources; + } + record->remote_addr = NULL; NCR_DestroyInstance(record->data); - if (record->name) - Free(record->name); + Free(record->name); n_sources--; } @@ -608,19 +870,16 @@ /* Procedure to remove a source. We don't bother whether the port address is matched - we're only interested in removing a record for - the right IP address. Thus the caller can specify the port number - as zero if it wishes. */ + the right IP address. */ NSR_Status -NSR_RemoveSource(NTP_Remote_Address *remote_addr) +NSR_RemoveSource(IPAddr *address) { - int slot, found; + int slot; assert(initialised); - find_slot(remote_addr, &slot, &found); - if (!found) { + if (find_slot(address, &slot) == 0) return NSR_NoSuchSource; - } clean_source_record(get_record(slot)); @@ -635,6 +894,24 @@ /* ================================================== */ void +NSR_RemoveSourcesById(uint32_t conf_id) +{ + SourceRecord *record; + unsigned int i; + + for (i = 0; i < ARR_GetSize(records); i++) { + record = get_record(i); + if (!record->remote_addr || record->conf_id != conf_id) + continue; + clean_source_record(record); + } + + rehash_records(); +} + +/* ================================================== */ + +void NSR_RemoveAllSources(void) { SourceRecord *record; @@ -657,18 +934,18 @@ { struct UnresolvedSource *us; - DEBUG_LOG("trying to replace %s", UTI_IPToString(&record->remote_addr->ip_addr)); + DEBUG_LOG("trying to replace %s (%s)", + UTI_IPToString(&record->remote_addr->ip_addr), record->name); us = MallocNew(struct UnresolvedSource); us->name = Strdup(record->name); - us->port = record->remote_addr->port; /* If there never was a valid reply from this source (e.g. it was a bad replacement), ignore the order of addresses from the resolver to not get stuck to a pair of addresses if the order doesn't change, or a group of IPv4/IPv6 addresses if the resolver prefers inaccessible IP family */ us->random_order = record->tentative; - us->replacement = 1; - us->replace_source = *record->remote_addr; + us->pool_id = INVALID_POOL; + us->address = *record->remote_addr; append_unresolved_source(us); NSR_ResolveSources(); @@ -681,22 +958,20 @@ { static struct timespec last_replacement; struct timespec now; - NTP_Remote_Address remote_addr; SourceRecord *record; - int slot, found; + IPAddr ip_addr; double diff; + int slot; - remote_addr.ip_addr = *address; - remote_addr.port = 0; - - find_slot(&remote_addr, &slot, &found); - if (!found) + if (!find_slot(address, &slot)) return; record = get_record(slot); - /* Only sources with a name can be replaced */ - if (!record->name) + /* Don't try to replace a source specified by an IP address unless the + address changed since the source was added (e.g. by NTS-KE) */ + if (UTI_StringToIP(record->name, &ip_addr) && + UTI_CompareIPs(&record->remote_addr->ip_addr, &ip_addr, NULL) == 0) return; /* Don't resolve names too frequently */ @@ -721,7 +996,7 @@ for (i = 0; i < ARR_GetSize(records); i++) { record = get_record(i); - if (!record->remote_addr || !record->name) + if (!record->remote_addr) continue; resolve_source_replacement(record); @@ -730,7 +1005,36 @@ /* ================================================== */ -static void remove_tentative_pool_sources(int pool) +NSR_Status +NSR_UpdateSourceNtpAddress(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr) +{ + int slot; + + if (!UTI_IsIPReal(&old_addr->ip_addr) || !UTI_IsIPReal(&new_addr->ip_addr)) + return NSR_InvalidAF; + + if (UTI_CompareIPs(&old_addr->ip_addr, &new_addr->ip_addr, NULL) != 0 && + find_slot(&new_addr->ip_addr, &slot)) + return NSR_AlreadyInUse; + + /* If a record is being modified (e.g. by change_source_address(), or the + source is just being created), postpone the change to avoid corruption */ + + if (!record_lock) + return change_source_address(old_addr, new_addr, 0); + + if (UTI_IsIPReal(&saved_address_update.old_address.ip_addr)) + return NSR_TooManySources; + + saved_address_update.old_address = *old_addr; + saved_address_update.new_address = *new_addr; + + return NSR_Success; +} + +/* ================================================== */ + +static void remove_pool_sources(int pool_id, int tentative, int unresolved) { SourceRecord *record; unsigned int i, removed; @@ -738,10 +1042,14 @@ for (i = removed = 0; i < ARR_GetSize(records); i++) { record = get_record(i); - if (!record->remote_addr || record->pool != pool || !record->tentative) + if (!record->remote_addr || record->pool_id != pool_id) + continue; + + if ((tentative && !record->tentative) || + (unresolved && UTI_IsIPReal(&record->remote_addr->ip_addr))) continue; - DEBUG_LOG("removing tentative source %s", + DEBUG_LOG("removing %ssource %s", tentative ? "tentative " : "", UTI_IPToString(&record->remote_addr->ip_addr)); clean_source_record(record); @@ -757,14 +1065,9 @@ uint32_t NSR_GetLocalRefid(IPAddr *address) { - NTP_Remote_Address remote_addr; - int slot, found; - - remote_addr.ip_addr = *address; - remote_addr.port = 0; + int slot; - find_slot(&remote_addr, &slot, &found); - if (!found) + if (!find_slot(address, &slot)) return 0; return NCR_GetLocalRefid(get_record(slot)->data); @@ -772,6 +1075,19 @@ /* ================================================== */ +char * +NSR_GetName(IPAddr *address) +{ + int slot; + + if (!find_slot(address, &slot)) + return NULL; + + return get_record(slot)->name; +} + +/* ================================================== */ + /* This routine is called by ntp_io when a new packet arrives off the network, possibly with an authentication tail */ void @@ -780,12 +1096,12 @@ { SourceRecord *record; struct SourcePool *pool; - int slot, found; + int slot; assert(initialised); - find_slot(remote_addr, &slot, &found); - if (found == 2) { /* Must match IP address AND port number */ + /* Must match IP address AND port number */ + if (find_slot2(remote_addr, &slot) == 2) { record = get_record(slot); if (!NCR_ProcessRxKnown(record->data, local_addr, rx_ts, message, length)) @@ -795,16 +1111,16 @@ /* This was the first good reply from the source */ record->tentative = 0; - if (record->pool != INVALID_POOL) { - pool = ARR_GetElement(pools, record->pool); - pool->sources++; + if (record->pool_id != INVALID_POOL) { + pool = get_pool(record->pool_id); + pool->confirmed_sources++; - DEBUG_LOG("pool %s has %d confirmed sources", record->name, pool->sources); + DEBUG_LOG("pool %s has %d confirmed sources", record->name, pool->confirmed_sources); /* If the number of sources from the pool reached the configured maximum, remove the remaining tentative sources */ - if (pool->sources >= pool->max_sources) - remove_tentative_pool_sources(record->pool); + if (pool->confirmed_sources >= pool->max_sources) + remove_pool_sources(record->pool_id, 1, 0); } } } else { @@ -819,11 +1135,10 @@ NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length) { SourceRecord *record; - int slot, found; - - find_slot(remote_addr, &slot, &found); + int slot; - if (found == 2) { /* Must match IP address AND port number */ + /* Must match IP address AND port number */ + if (find_slot2(remote_addr, &slot) == 2) { record = get_record(slot); NCR_ProcessTxKnown(record->data, local_addr, tx_ts, message, length); } else { @@ -873,7 +1188,10 @@ for (i = 0; i < ARR_GetSize(records); i++) { record = get_record(i); if (record->remote_addr) { - if (address->family == IPADDR_UNSPEC || + /* Ignore SRC_MAYBE_ONLINE connectivity change for unspecified unresolved + sources as they would always end up in the offline state */ + if ((address->family == IPADDR_UNSPEC && + (connectivity != SRC_MAYBE_ONLINE || UTI_IsIPReal(&record->remote_addr->ip_addr))) || !UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) { any = 1; if (NCR_IsSyncPeer(record->data)) { @@ -889,17 +1207,6 @@ if (syncpeer) NCR_SetConnectivity(syncpeer->data, connectivity); - if (address->family == IPADDR_UNSPEC) { - struct UnresolvedSource *us; - - for (us = unresolved_sources; us; us = us->next) { - if (us->replacement) - continue; - any = 1; - us->new_source.params.connectivity = connectivity; - } - } - return any; } @@ -908,18 +1215,13 @@ int NSR_ModifyMinpoll(IPAddr *address, int new_minpoll) { - int slot, found; - NTP_Remote_Address addr; - addr.ip_addr = *address; - addr.port = 0; + int slot; - find_slot(&addr, &slot, &found); - if (found == 0) { + if (!find_slot(address, &slot)) return 0; - } else { - NCR_ModifyMinpoll(get_record(slot)->data, new_minpoll); - return 1; - } + + NCR_ModifyMinpoll(get_record(slot)->data, new_minpoll); + return 1; } /* ================================================== */ @@ -927,18 +1229,13 @@ int NSR_ModifyMaxpoll(IPAddr *address, int new_maxpoll) { - int slot, found; - NTP_Remote_Address addr; - addr.ip_addr = *address; - addr.port = 0; + int slot; - find_slot(&addr, &slot, &found); - if (found == 0) { + if (!find_slot(address, &slot)) return 0; - } else { - NCR_ModifyMaxpoll(get_record(slot)->data, new_maxpoll); - return 1; - } + + NCR_ModifyMaxpoll(get_record(slot)->data, new_maxpoll); + return 1; } /* ================================================== */ @@ -946,18 +1243,13 @@ int NSR_ModifyMaxdelay(IPAddr *address, double new_max_delay) { - int slot, found; - NTP_Remote_Address addr; - addr.ip_addr = *address; - addr.port = 0; + int slot; - find_slot(&addr, &slot, &found); - if (found == 0) { + if (!find_slot(address, &slot)) return 0; - } else { - NCR_ModifyMaxdelay(get_record(slot)->data, new_max_delay); - return 1; - } + + NCR_ModifyMaxdelay(get_record(slot)->data, new_max_delay); + return 1; } /* ================================================== */ @@ -965,18 +1257,13 @@ int NSR_ModifyMaxdelayratio(IPAddr *address, double new_max_delay_ratio) { - int slot, found; - NTP_Remote_Address addr; - addr.ip_addr = *address; - addr.port = 0; + int slot; - find_slot(&addr, &slot, &found); - if (found == 0) { + if (!find_slot(address, &slot)) return 0; - } else { - NCR_ModifyMaxdelayratio(get_record(slot)->data, new_max_delay_ratio); - return 1; - } + + NCR_ModifyMaxdelayratio(get_record(slot)->data, new_max_delay_ratio); + return 1; } /* ================================================== */ @@ -984,18 +1271,13 @@ int NSR_ModifyMaxdelaydevratio(IPAddr *address, double new_max_delay_dev_ratio) { - int slot, found; - NTP_Remote_Address addr; - addr.ip_addr = *address; - addr.port = 0; + int slot; - find_slot(&addr, &slot, &found); - if (found == 0) { + if (!find_slot(address, &slot)) return 0; - } else { - NCR_ModifyMaxdelaydevratio(get_record(slot)->data, new_max_delay_dev_ratio); - return 1; - } + + NCR_ModifyMaxdelaydevratio(get_record(slot)->data, new_max_delay_dev_ratio); + return 1; } /* ================================================== */ @@ -1003,18 +1285,13 @@ int NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum) { - int slot, found; - NTP_Remote_Address addr; - addr.ip_addr = *address; - addr.port = 0; + int slot; - find_slot(&addr, &slot, &found); - if (found == 0) { + if (!find_slot(address, &slot)) return 0; - } else { - NCR_ModifyMinstratum(get_record(slot)->data, new_min_stratum); - return 1; - } + + NCR_ModifyMinstratum(get_record(slot)->data, new_min_stratum); + return 1; } /* ================================================== */ @@ -1022,18 +1299,13 @@ int NSR_ModifyPolltarget(IPAddr *address, int new_poll_target) { - int slot, found; - NTP_Remote_Address addr; - addr.ip_addr = *address; - addr.port = 0; + int slot; - find_slot(&addr, &slot, &found); - if (found == 0) { + if (!find_slot(address, &slot)) return 0; - } else { - NCR_ModifyPolltarget(get_record(slot)->data, new_poll_target); - return 1; - } + + NCR_ModifyPolltarget(get_record(slot)->data, new_poll_target); + return 1; } /* ================================================== */ @@ -1069,13 +1341,9 @@ void NSR_ReportSource(RPT_SourceReport *report, struct timespec *now) { - NTP_Remote_Address rem_addr; - int slot, found; + int slot; - rem_addr.ip_addr = report->ip_addr; - rem_addr.port = 0; - find_slot(&rem_addr, &slot, &found); - if (found) { + if (find_slot(&report->ip_addr, &slot)) { NCR_ReportSource(get_record(slot)->data, report, now); } else { report->poll = 0; @@ -1084,19 +1352,29 @@ } /* ================================================== */ + +int +NSR_GetAuthReport(IPAddr *address, RPT_AuthReport *report) +{ + int slot; + + if (!find_slot(address, &slot)) + return 0; + + NCR_GetAuthReport(get_record(slot)->data, report); + return 1; +} + +/* ================================================== */ /* The ip address is assumed to be completed on input, that is how we identify the source record. */ int NSR_GetNTPReport(RPT_NTPReport *report) { - NTP_Remote_Address rem_addr; - int slot, found; + int slot; - rem_addr.ip_addr = report->remote_addr; - rem_addr.port = 0; - find_slot(&rem_addr, &slot, &found); - if (!found) + if (!find_slot(&report->remote_addr, &slot)) return 0; NCR_GetNTPReport(get_record(slot)->data, report); @@ -1110,28 +1388,39 @@ { SourceRecord *record; unsigned int i; - struct UnresolvedSource *us; report->online = 0; report->offline = 0; report->burst_online = 0; report->burst_offline = 0; + report->unresolved = 0; for (i = 0; i < ARR_GetSize(records); i++) { record = get_record(i); - if (record->remote_addr) { + if (!record->remote_addr) + continue; + + if (!UTI_IsIPReal(&record->remote_addr->ip_addr)) { + report->unresolved++; + } else { NCR_IncrementActivityCounters(record->data, &report->online, &report->offline, &report->burst_online, &report->burst_offline); } } - - report->unresolved = 0; - - for (us = unresolved_sources; us; us = us->next) { - report->unresolved++; - } } - /* ================================================== */ +void +NSR_DumpAuthData(void) +{ + SourceRecord *record; + int i; + + for (i = 0; i < ARR_GetSize(records); i++) { + record = get_record(i); + if (!record->remote_addr) + continue; + NCR_DumpAuthData(record->data); + } +} diff -Nru chrony-3.5/ntp_sources.h chrony-4.1/ntp_sources.h --- chrony-3.5/ntp_sources.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/ntp_sources.h 2021-05-12 11:06:15.000000000 +0000 @@ -44,16 +44,21 @@ NSR_NoSuchSource, /* Remove - attempt to remove a source that is not known */ NSR_AlreadyInUse, /* AddSource - attempt to add a source that is already known */ NSR_TooManySources, /* AddSource - too many sources already present */ - NSR_InvalidAF /* AddSource - attempt to add a source with invalid address family */ + NSR_InvalidAF, /* AddSource - attempt to add a source with invalid address family */ + NSR_InvalidName, /* AddSourceByName - attempt to add a source with invalid name */ + NSR_UnresolvedName, /* AddSourceByName - name will be resolved later */ } NSR_Status; /* Procedure to add a new server or peer source. */ -extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params); +extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, + SourceParameters *params, uint32_t *conf_id); /* Procedure to add a new server, peer source, or pool of servers specified by name instead of address. The name is resolved in exponentially increasing - intervals until it succeeds or fails with a non-temporary error. */ -extern void NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, SourceParameters *params); + intervals until it succeeds or fails with a non-temporary error. If the + name is an address, it is equivalent to NSR_AddSource(). */ +extern NSR_Status NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, + SourceParameters *params, uint32_t *conf_id); /* Function type for handlers to be called back when an attempt * (possibly unsuccessful) to resolve unresolved sources ends */ @@ -72,7 +77,10 @@ extern void NSR_AutoStartSources(void); /* Procedure to remove a source */ -extern NSR_Status NSR_RemoveSource(NTP_Remote_Address *remote_addr); +extern NSR_Status NSR_RemoveSource(IPAddr *address); + +/* Procedure to remove all sources matching a configuration ID */ +extern void NSR_RemoveSourcesById(uint32_t conf_id); /* Procedure to remove all sources */ extern void NSR_RemoveAllSources(void); @@ -83,9 +91,18 @@ /* Procedure to resolve all names again */ extern void NSR_RefreshAddresses(void); +/* Procedure to update the address of a source. The update may be + postponed. */ +extern NSR_Status NSR_UpdateSourceNtpAddress(NTP_Remote_Address *old_addr, + NTP_Remote_Address *new_addr); + /* Procedure to get local reference ID corresponding to a source */ extern uint32_t NSR_GetLocalRefid(IPAddr *address); +/* Procedure to get the name of a source as it was specified (it may be + an IP address) */ +extern char *NSR_GetName(IPAddr *address); + /* This routine is called by ntp_io when a new packet arrives off the network */ extern void NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length); @@ -124,8 +141,12 @@ extern void NSR_ReportSource(RPT_SourceReport *report, struct timespec *now); +extern int NSR_GetAuthReport(IPAddr *address, RPT_AuthReport *report); + extern int NSR_GetNTPReport(RPT_NTPReport *report); extern void NSR_GetActivityReport(RPT_ActivityReport *report); +extern void NSR_DumpAuthData(void); + #endif /* GOT_NTP_SOURCES_H */ diff -Nru chrony-3.5/nts_ke_client.c chrony-4.1/nts_ke_client.c --- chrony-3.5/nts_ke_client.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/nts_ke_client.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,442 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020-2021 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + NTS-KE client + */ + +#include "config.h" + +#include "sysincl.h" + +#include "nts_ke_client.h" + +#include "conf.h" +#include "logging.h" +#include "memory.h" +#include "nameserv_async.h" +#include "nts_ke_session.h" +#include "siv.h" +#include "socket.h" +#include "util.h" + +#define CLIENT_TIMEOUT 16.0 + +struct NKC_Instance_Record { + char *name; + IPSockAddr address; + NKSN_Credentials credentials; + NKSN_Instance session; + int destroying; + int got_response; + int resolving_name; + + NKE_Context context; + NKE_Cookie cookies[NKE_MAX_COOKIES]; + int num_cookies; + char server_name[NKE_MAX_RECORD_BODY_LENGTH + 2]; + IPSockAddr ntp_address; +}; + +/* ================================================== */ + +static NKSN_Credentials default_credentials = NULL; +static int default_credentials_refs = 0; + +/* ================================================== */ + +static void +name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *arg) +{ + NKC_Instance inst = arg; + int i; + + inst->resolving_name = 0; + + if (inst->destroying) { + Free(inst); + return; + } + + if (status != DNS_Success || n_addrs < 1) { + LOG(LOGS_ERR, "Could not resolve NTP server %s from %s", inst->server_name, inst->name); + /* Force restart */ + inst->got_response = 0; + return; + } + + inst->ntp_address.ip_addr = ip_addrs[0]; + + /* Prefer an address in the same family as the NTS-KE server */ + for (i = 0; i < n_addrs; i++) { + DEBUG_LOG("%s resolved to %s", inst->server_name, UTI_IPToString(&ip_addrs[i])); + if (ip_addrs[i].family == inst->address.ip_addr.family) { + inst->ntp_address.ip_addr = ip_addrs[i]; + break; + } + } +} + +/* ================================================== */ + +static int +prepare_request(NKC_Instance inst) +{ + NKSN_Instance session = inst->session; + uint16_t datum; + + NKSN_BeginMessage(session); + + datum = htons(NKE_NEXT_PROTOCOL_NTPV4); + if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum))) + return 0; + + datum = htons(AEAD_AES_SIV_CMAC_256); + if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum))) + return 0; + + if (!NKSN_EndMessage(session)) + return 0; + + return 1; +} + +/* ================================================== */ + +static int +process_response(NKC_Instance inst) +{ + int next_protocol = -1, aead_algorithm = -1, error = 0; + int i, critical, type, length; + uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)]; + + assert(NKE_MAX_COOKIE_LENGTH <= NKE_MAX_RECORD_BODY_LENGTH); + assert(sizeof (data) % sizeof (uint16_t) == 0); + assert(sizeof (uint16_t) == 2); + + inst->num_cookies = 0; + inst->ntp_address.ip_addr.family = IPADDR_UNSPEC; + inst->ntp_address.port = 0; + inst->server_name[0] = '\0'; + + while (!error) { + if (!NKSN_GetRecord(inst->session, &critical, &type, &length, &data, sizeof (data))) + break; + + if (length > sizeof (data)) { + DEBUG_LOG("Record too long type=%d length=%d critical=%d", type, length, critical); + if (critical) + error = 1; + continue; + } + + switch (type) { + case NKE_RECORD_NEXT_PROTOCOL: + if (!critical || length != 2 || ntohs(data[0]) != NKE_NEXT_PROTOCOL_NTPV4) { + DEBUG_LOG("Unexpected NTS-KE next protocol"); + error = 1; + break; + } + next_protocol = NKE_NEXT_PROTOCOL_NTPV4; + break; + case NKE_RECORD_AEAD_ALGORITHM: + if (length != 2 || ntohs(data[0]) != AEAD_AES_SIV_CMAC_256) { + DEBUG_LOG("Unexpected NTS-KE AEAD algorithm"); + error = 1; + break; + } + aead_algorithm = AEAD_AES_SIV_CMAC_256; + inst->context.algorithm = aead_algorithm; + break; + case NKE_RECORD_ERROR: + if (length == 2) + DEBUG_LOG("NTS-KE error %d", ntohs(data[0])); + error = 1; + break; + case NKE_RECORD_WARNING: + if (length == 2) + DEBUG_LOG("NTS-KE warning %d", ntohs(data[0])); + error = 1; + break; + case NKE_RECORD_COOKIE: + DEBUG_LOG("Got cookie length=%d", length); + + if (length < 1 || length > NKE_MAX_COOKIE_LENGTH || length % 4 != 0 || + inst->num_cookies >= NKE_MAX_COOKIES) { + DEBUG_LOG("Unexpected length/cookie"); + break; + } + + assert(NKE_MAX_COOKIE_LENGTH == sizeof (inst->cookies[inst->num_cookies].cookie)); + assert(NKE_MAX_COOKIES == sizeof (inst->cookies) / + sizeof (inst->cookies[inst->num_cookies])); + inst->cookies[inst->num_cookies].length = length; + memcpy(inst->cookies[inst->num_cookies].cookie, data, length); + + inst->num_cookies++; + break; + case NKE_RECORD_NTPV4_SERVER_NEGOTIATION: + if (length < 1 || length >= sizeof (inst->server_name)) { + DEBUG_LOG("Invalid server name"); + error = 1; + break; + } + + memcpy(inst->server_name, data, length); + inst->server_name[length] = '\0'; + + /* Make sure the name is printable and has no spaces */ + for (i = 0; i < length && isgraph((unsigned char)inst->server_name[i]); i++) + ; + if (i != length) { + DEBUG_LOG("Invalid server name"); + error = 1; + break; + } + + DEBUG_LOG("Negotiated server %s", inst->server_name); + break; + case NKE_RECORD_NTPV4_PORT_NEGOTIATION: + if (length != 2) { + DEBUG_LOG("Invalid port"); + error = 1; + break; + } + inst->ntp_address.port = ntohs(data[0]); + DEBUG_LOG("Negotiated port %d", inst->ntp_address.port); + break; + default: + DEBUG_LOG("Unknown record type=%d length=%d critical=%d", type, length, critical); + if (critical) + error = 1; + } + } + + DEBUG_LOG("NTS-KE response: error=%d next=%d aead=%d", + error, next_protocol, aead_algorithm); + + if (error || inst->num_cookies == 0 || + next_protocol != NKE_NEXT_PROTOCOL_NTPV4 || + aead_algorithm != AEAD_AES_SIV_CMAC_256) + return 0; + + return 1; +} + +/* ================================================== */ + +static int +handle_message(void *arg) +{ + NKC_Instance inst = arg; + + if (!process_response(inst)) { + LOG(LOGS_ERR, "Received invalid NTS-KE response from %s", inst->name); + return 0; + } + + if (!NKSN_GetKeys(inst->session, inst->context.algorithm, + &inst->context.c2s, &inst->context.s2c)) + return 0; + + if (inst->server_name[0] != '\0') { + if (inst->resolving_name) + return 0; + if (!UTI_StringToIP(inst->server_name, &inst->ntp_address.ip_addr)) { + int length = strlen(inst->server_name); + + /* Add a trailing dot if not present to force the name to be + resolved as a fully qualified domain name */ + if (length < 1 || length + 1 >= sizeof (inst->server_name)) + return 0; + if (inst->server_name[length - 1] != '.') { + inst->server_name[length] = '.'; + inst->server_name[length + 1] = '\0'; + } + + DNS_Name2IPAddressAsync(inst->server_name, name_resolve_handler, inst); + inst->resolving_name = 1; + } + } + + inst->got_response = 1; + + return 1; +} + +/* ================================================== */ + +NKC_Instance +NKC_CreateInstance(IPSockAddr *address, const char *name, uint32_t cert_set) +{ + const char **trusted_certs; + uint32_t *certs_ids; + NKC_Instance inst; + int n_certs; + + inst = MallocNew(struct NKC_Instance_Record); + + inst->address = *address; + inst->name = Strdup(name); + inst->session = NKSN_CreateInstance(0, inst->name, handle_message, inst); + inst->resolving_name = 0; + inst->destroying = 0; + inst->got_response = 0; + + n_certs = CNF_GetNtsTrustedCertsPaths(&trusted_certs, &certs_ids); + + /* Share the credentials among clients using the default set of trusted + certificates, which likely contains most certificates */ + if (cert_set == 0) { + if (!default_credentials) + default_credentials = NKSN_CreateClientCertCredentials(trusted_certs, certs_ids, + n_certs, cert_set); + inst->credentials = default_credentials; + if (default_credentials) + default_credentials_refs++; + } else { + inst->credentials = NKSN_CreateClientCertCredentials(trusted_certs, certs_ids, + n_certs, cert_set); + } + + return inst; +} + +/* ================================================== */ + +void +NKC_DestroyInstance(NKC_Instance inst) +{ + NKSN_DestroyInstance(inst->session); + + Free(inst->name); + + if (inst->credentials) { + if (inst->credentials == default_credentials) { + default_credentials_refs--; + if (default_credentials_refs <= 0) { + NKSN_DestroyCertCredentials(default_credentials); + default_credentials = NULL; + } + } else { + NKSN_DestroyCertCredentials(inst->credentials); + } + } + + /* If the asynchronous resolver is running, let the handler free + the instance later */ + if (inst->resolving_name) { + inst->destroying = 1; + return; + } + + Free(inst); +} + +/* ================================================== */ + +int +NKC_Start(NKC_Instance inst) +{ + IPSockAddr local_addr; + char label[512], *iface; + int sock_fd; + + assert(!NKC_IsActive(inst)); + + inst->got_response = 0; + + if (!inst->credentials) { + DEBUG_LOG("Missing client credentials"); + return 0; + } + + /* Follow the bindacqaddress and bindacqdevice settings */ + CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr); + local_addr.port = 0; + iface = CNF_GetBindAcquisitionInterface(); + + /* Make a label containing both the address and name of the server */ + if (snprintf(label, sizeof (label), "%s (%s)", + UTI_IPSockAddrToString(&inst->address), inst->name) >= sizeof (label)) + ; + + sock_fd = SCK_OpenTcpSocket(&inst->address, &local_addr, iface, 0); + if (sock_fd < 0) { + LOG(LOGS_ERR, "Could not connect to %s", label); + return 0; + } + + /* Start an NTS-KE session */ + if (!NKSN_StartSession(inst->session, sock_fd, label, inst->credentials, CLIENT_TIMEOUT)) { + SCK_CloseSocket(sock_fd); + return 0; + } + + /* Send a request */ + if (!prepare_request(inst)) { + DEBUG_LOG("Could not prepare NTS-KE request"); + NKSN_StopSession(inst->session); + return 0; + } + + return 1; +} + +/* ================================================== */ + +int +NKC_IsActive(NKC_Instance inst) +{ + return !NKSN_IsStopped(inst->session) || inst->resolving_name; +} + +/* ================================================== */ + +int +NKC_GetNtsData(NKC_Instance inst, NKE_Context *context, + NKE_Cookie *cookies, int *num_cookies, int max_cookies, + IPSockAddr *ntp_address) +{ + int i; + + if (!inst->got_response || inst->resolving_name) + return 0; + + *context = inst->context; + + for (i = 0; i < inst->num_cookies && i < max_cookies; i++) + cookies[i] = inst->cookies[i]; + *num_cookies = i; + + *ntp_address = inst->ntp_address; + + return 1; +} + +/* ================================================== */ + +int +NKC_GetRetryFactor(NKC_Instance inst) +{ + return NKSN_GetRetryFactor(inst->session); +} diff -Nru chrony-3.5/nts_ke_client.h chrony-4.1/nts_ke_client.h --- chrony-3.5/nts_ke_client.h 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/nts_ke_client.h 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,56 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + Header file for the NTS-KE client + */ + +#ifndef GOT_NTS_KE_CLIENT_H +#define GOT_NTS_KE_CLIENT_H + +#include "addressing.h" +#include "nts_ke.h" + +typedef struct NKC_Instance_Record *NKC_Instance; + +/* Create a client NTS-KE instance */ +extern NKC_Instance NKC_CreateInstance(IPSockAddr *address, const char *name, uint32_t cert_set); + +/* Destroy an instance */ +extern void NKC_DestroyInstance(NKC_Instance inst); + +/* Connect to the server, start an NTS-KE session, send an NTS-KE request, and + process the response (asynchronously) */ +extern int NKC_Start(NKC_Instance inst); + +/* Check if the client is still running */ +extern int NKC_IsActive(NKC_Instance inst); + +/* Get the NTS data if the session was successful */ +extern int NKC_GetNtsData(NKC_Instance inst, NKE_Context *context, + NKE_Cookie *cookies, int *num_cookies, int max_cookies, + IPSockAddr *ntp_address); + +/* Get a factor to calculate retry interval (in log2 seconds) */ +extern int NKC_GetRetryFactor(NKC_Instance inst); + +#endif diff -Nru chrony-3.5/nts_ke.h chrony-4.1/nts_ke.h --- chrony-3.5/nts_ke.h 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/nts_ke.h 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,81 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + Header file for the NTS Key Establishment protocol + */ + +#ifndef GOT_NTS_KE_H +#define GOT_NTS_KE_H + +#include "siv.h" + +#define NKE_PORT 4460 + +#define NKE_RECORD_CRITICAL_BIT (1U << 15) +#define NKE_RECORD_END_OF_MESSAGE 0 +#define NKE_RECORD_NEXT_PROTOCOL 1 +#define NKE_RECORD_ERROR 2 +#define NKE_RECORD_WARNING 3 +#define NKE_RECORD_AEAD_ALGORITHM 4 +#define NKE_RECORD_COOKIE 5 +#define NKE_RECORD_NTPV4_SERVER_NEGOTIATION 6 +#define NKE_RECORD_NTPV4_PORT_NEGOTIATION 7 + +#define NKE_NEXT_PROTOCOL_NTPV4 0 + +#define NKE_ERROR_UNRECOGNIZED_CRITICAL_RECORD 0 +#define NKE_ERROR_BAD_REQUEST 1 +#define NKE_ERROR_INTERNAL_SERVER_ERROR 2 + +#define NKE_ALPN_NAME "ntske/1" +#define NKE_EXPORTER_LABEL "EXPORTER-network-time-security" +#define NKE_EXPORTER_CONTEXT_C2S "\x0\x0\x0\xf\x0" +#define NKE_EXPORTER_CONTEXT_S2C "\x0\x0\x0\xf\x1" + +#define NKE_MAX_MESSAGE_LENGTH 16384 +#define NKE_MAX_RECORD_BODY_LENGTH 256 +#define NKE_MAX_COOKIE_LENGTH 256 +#define NKE_MAX_COOKIES 8 +#define NKE_MAX_KEY_LENGTH SIV_MAX_KEY_LENGTH + +#define NKE_RETRY_FACTOR2_CONNECT 4 +#define NKE_RETRY_FACTOR2_TLS 10 +#define NKE_MAX_RETRY_INTERVAL2 19 + +typedef struct { + int length; + unsigned char key[NKE_MAX_KEY_LENGTH]; +} NKE_Key; + +typedef struct { + SIV_Algorithm algorithm; + NKE_Key c2s; + NKE_Key s2c; +} NKE_Context; + +typedef struct { + int length; + unsigned char cookie[NKE_MAX_COOKIE_LENGTH]; +} NKE_Cookie; + +#endif diff -Nru chrony-3.5/nts_ke_server.c chrony-4.1/nts_ke_server.c --- chrony-3.5/nts_ke_server.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/nts_ke_server.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,963 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + NTS-KE server + */ + +#include "config.h" + +#include "sysincl.h" + +#include "nts_ke_server.h" + +#include "array.h" +#include "conf.h" +#include "clientlog.h" +#include "local.h" +#include "logging.h" +#include "memory.h" +#include "ntp_core.h" +#include "nts_ke_session.h" +#include "privops.h" +#include "siv.h" +#include "socket.h" +#include "sched.h" +#include "sys.h" +#include "util.h" + +#define SERVER_TIMEOUT 2.0 + +#define SERVER_COOKIE_SIV AEAD_AES_SIV_CMAC_256 +#define SERVER_COOKIE_NONCE_LENGTH 16 + +#define KEY_ID_INDEX_BITS 2 +#define MAX_SERVER_KEYS (1U << KEY_ID_INDEX_BITS) +#define FUTURE_KEYS 1 + +#define DUMP_FILENAME "ntskeys" +#define DUMP_IDENTIFIER "NKS0\n" + +#define INVALID_SOCK_FD (-7) + +typedef struct { + uint32_t key_id; + unsigned char nonce[SERVER_COOKIE_NONCE_LENGTH]; +} ServerCookieHeader; + +typedef struct { + uint32_t id; + unsigned char key[SIV_MAX_KEY_LENGTH]; + SIV_Instance siv; +} ServerKey; + +typedef struct { + uint32_t key_id; + unsigned char key[SIV_MAX_KEY_LENGTH]; + IPAddr client_addr; + uint16_t client_port; + uint16_t _pad; +} HelperRequest; + +/* ================================================== */ + +static ServerKey server_keys[MAX_SERVER_KEYS]; +static int current_server_key; +static double last_server_key_ts; +static int key_rotation_interval; + +static int server_sock_fd4; +static int server_sock_fd6; + +static int helper_sock_fd; +static int is_helper; + +static int initialised = 0; + +/* Array of NKSN instances */ +static ARR_Instance sessions; +static NKSN_Credentials server_credentials; + +/* ================================================== */ + +static int handle_message(void *arg); + +/* ================================================== */ + +static int +handle_client(int sock_fd, IPSockAddr *addr) +{ + NKSN_Instance inst, *instp; + int i; + + /* Leave at least half of the descriptors which can handled by select() + to other use */ + if (sock_fd > FD_SETSIZE / 2) { + DEBUG_LOG("Rejected connection from %s (%s)", + UTI_IPSockAddrToString(addr), "too many descriptors"); + return 0; + } + + /* Find an unused server slot or one with an already stopped session */ + for (i = 0, inst = NULL; i < ARR_GetSize(sessions); i++) { + instp = ARR_GetElement(sessions, i); + if (!*instp) { + /* NULL handler arg will be replaced with the session instance */ + inst = NKSN_CreateInstance(1, NULL, handle_message, NULL); + *instp = inst; + break; + } else if (NKSN_IsStopped(*instp)) { + inst = *instp; + break; + } + } + + if (!inst) { + DEBUG_LOG("Rejected connection from %s (%s)", + UTI_IPSockAddrToString(addr), "too many connections"); + return 0; + } + + assert(server_credentials); + + if (!NKSN_StartSession(inst, sock_fd, UTI_IPSockAddrToString(addr), + server_credentials, SERVER_TIMEOUT)) + return 0; + + return 1; +} + +/* ================================================== */ + +static void +handle_helper_request(int fd, int event, void *arg) +{ + SCK_Message *message; + HelperRequest *req; + IPSockAddr client_addr; + int sock_fd; + + /* Receive the helper request with the NTS-KE session socket. + With multiple helpers EAGAIN errors are expected here. */ + message = SCK_ReceiveMessage(fd, SCK_FLAG_MSG_DESCRIPTOR); + if (!message) + return; + + sock_fd = message->descriptor; + if (sock_fd < 0) { + /* Message with no descriptor is a shutdown command */ + SCH_QuitProgram(); + return; + } + + if (!initialised) { + DEBUG_LOG("Uninitialised helper"); + SCK_CloseSocket(sock_fd); + return; + } + + if (message->length != sizeof (HelperRequest)) + LOG_FATAL("Invalid helper request"); + + req = message->data; + + /* Extract the current server key and client address from the request */ + server_keys[current_server_key].id = ntohl(req->key_id); + assert(sizeof (server_keys[current_server_key].key) == sizeof (req->key)); + memcpy(server_keys[current_server_key].key, req->key, + sizeof (server_keys[current_server_key].key)); + UTI_IPNetworkToHost(&req->client_addr, &client_addr.ip_addr); + client_addr.port = ntohs(req->client_port); + + if (!SIV_SetKey(server_keys[current_server_key].siv, server_keys[current_server_key].key, + SIV_GetKeyLength(SERVER_COOKIE_SIV))) + LOG_FATAL("Could not set SIV key"); + + if (!handle_client(sock_fd, &client_addr)) { + SCK_CloseSocket(sock_fd); + return; + } + + DEBUG_LOG("Accepted helper request fd=%d", sock_fd); +} + +/* ================================================== */ + +static void +accept_connection(int listening_fd, int event, void *arg) +{ + SCK_Message message; + IPSockAddr addr; + int log_index, sock_fd; + struct timespec now; + + sock_fd = SCK_AcceptConnection(listening_fd, &addr); + if (sock_fd < 0) + return; + + if (!NCR_CheckAccessRestriction(&addr.ip_addr)) { + DEBUG_LOG("Rejected connection from %s (%s)", + UTI_IPSockAddrToString(&addr), "access denied"); + SCK_CloseSocket(sock_fd); + return; + } + + SCH_GetLastEventTime(&now, NULL, NULL); + + log_index = CLG_LogServiceAccess(CLG_NTSKE, &addr.ip_addr, &now); + if (log_index >= 0 && CLG_LimitServiceRate(CLG_NTSKE, log_index)) { + DEBUG_LOG("Rejected connection from %s (%s)", + UTI_IPSockAddrToString(&addr), "rate limit"); + SCK_CloseSocket(sock_fd); + return; + } + + /* Pass the socket to a helper process if enabled. Otherwise, handle the + client in the main process. */ + if (helper_sock_fd != INVALID_SOCK_FD) { + HelperRequest req; + + memset(&req, 0, sizeof (req)); + + /* Include the current server key and client address in the request */ + req.key_id = htonl(server_keys[current_server_key].id); + assert(sizeof (req.key) == sizeof (server_keys[current_server_key].key)); + memcpy(req.key, server_keys[current_server_key].key, sizeof (req.key)); + UTI_IPHostToNetwork(&addr.ip_addr, &req.client_addr); + req.client_port = htons(addr.port); + + SCK_InitMessage(&message, SCK_ADDR_UNSPEC); + message.data = &req; + message.length = sizeof (req); + message.descriptor = sock_fd; + + errno = 0; + if (!SCK_SendMessage(helper_sock_fd, &message, SCK_FLAG_MSG_DESCRIPTOR)) { + /* If sending failed with EPIPE, it means all helpers closed their end of + the socket (e.g. due to a fatal error) */ + if (errno == EPIPE) + LOG_FATAL("NTS-KE helpers failed"); + SCK_CloseSocket(sock_fd); + return; + } + + SCK_CloseSocket(sock_fd); + } else { + if (!handle_client(sock_fd, &addr)) { + SCK_CloseSocket(sock_fd); + return; + } + } + + DEBUG_LOG("Accepted connection from %s fd=%d", UTI_IPSockAddrToString(&addr), sock_fd); +} + +/* ================================================== */ + +static int +open_socket(int family) +{ + IPSockAddr local_addr; + int backlog, sock_fd; + char *iface; + + if (!SCK_IsIpFamilyEnabled(family)) + return INVALID_SOCK_FD; + + CNF_GetBindAddress(family, &local_addr.ip_addr); + local_addr.port = CNF_GetNtsServerPort(); + iface = CNF_GetBindNtpInterface(); + + sock_fd = SCK_OpenTcpSocket(NULL, &local_addr, iface, 0); + if (sock_fd < 0) { + LOG(LOGS_ERR, "Could not open NTS-KE socket on %s", UTI_IPSockAddrToString(&local_addr)); + return INVALID_SOCK_FD; + } + + /* Set the maximum number of waiting connections on the socket to the maximum + number of concurrent sessions */ + backlog = MAX(CNF_GetNtsServerProcesses(), 1) * CNF_GetNtsServerConnections(); + + if (!SCK_ListenOnSocket(sock_fd, backlog)) { + SCK_CloseSocket(sock_fd); + return INVALID_SOCK_FD; + } + + SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, accept_connection, NULL); + + return sock_fd; +} + +/* ================================================== */ + +static void +helper_signal(int x) +{ + SCH_QuitProgram(); +} + +/* ================================================== */ + +static int +prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_algorithm) +{ + NKE_Context context; + NKE_Cookie cookie; + char *ntp_server; + uint16_t datum; + int i; + + DEBUG_LOG("NTS KE response: error=%d next=%d aead=%d", error, next_protocol, aead_algorithm); + + NKSN_BeginMessage(session); + + if (error >= 0) { + datum = htons(error); + if (!NKSN_AddRecord(session, 1, NKE_RECORD_ERROR, &datum, sizeof (datum))) + return 0; + } else if (next_protocol < 0) { + if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, NULL, 0)) + return 0; + } else if (aead_algorithm < 0) { + datum = htons(next_protocol); + if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum))) + return 0; + if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, NULL, 0)) + return 0; + } else { + datum = htons(next_protocol); + if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum))) + return 0; + + datum = htons(aead_algorithm); + if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum))) + return 0; + + if (CNF_GetNTPPort() != NTP_PORT) { + datum = htons(CNF_GetNTPPort()); + if (!NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_PORT_NEGOTIATION, &datum, sizeof (datum))) + return 0; + } + + ntp_server = CNF_GetNtsNtpServer(); + if (ntp_server) { + if (!NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_SERVER_NEGOTIATION, + ntp_server, strlen(ntp_server))) + return 0; + } + + context.algorithm = aead_algorithm; + + if (!NKSN_GetKeys(session, aead_algorithm, &context.c2s, &context.s2c)) + return 0; + + for (i = 0; i < NKE_MAX_COOKIES; i++) { + if (!NKS_GenerateCookie(&context, &cookie)) + return 0; + if (!NKSN_AddRecord(session, 0, NKE_RECORD_COOKIE, cookie.cookie, cookie.length)) + return 0; + } + } + + if (!NKSN_EndMessage(session)) + return 0; + + return 1; +} + +/* ================================================== */ + +static int +process_request(NKSN_Instance session) +{ + int next_protocol_records = 0, aead_algorithm_records = 0; + int next_protocol_values = 0, aead_algorithm_values = 0; + int next_protocol = -1, aead_algorithm = -1, error = -1; + int i, critical, type, length; + uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)]; + + assert(NKE_MAX_RECORD_BODY_LENGTH % sizeof (uint16_t) == 0); + assert(sizeof (uint16_t) == 2); + + while (error < 0) { + if (!NKSN_GetRecord(session, &critical, &type, &length, &data, sizeof (data))) + break; + + switch (type) { + case NKE_RECORD_NEXT_PROTOCOL: + if (!critical || length < 2 || length % 2 != 0) { + error = NKE_ERROR_BAD_REQUEST; + break; + } + + next_protocol_records++; + + for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) { + next_protocol_values++; + if (ntohs(data[i]) == NKE_NEXT_PROTOCOL_NTPV4) + next_protocol = NKE_NEXT_PROTOCOL_NTPV4; + } + break; + case NKE_RECORD_AEAD_ALGORITHM: + if (length < 2 || length % 2 != 0) { + error = NKE_ERROR_BAD_REQUEST; + break; + } + + aead_algorithm_records++; + + for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) { + aead_algorithm_values++; + if (ntohs(data[i]) == AEAD_AES_SIV_CMAC_256) + aead_algorithm = AEAD_AES_SIV_CMAC_256; + } + break; + case NKE_RECORD_ERROR: + case NKE_RECORD_WARNING: + case NKE_RECORD_COOKIE: + error = NKE_ERROR_BAD_REQUEST; + break; + default: + if (critical) + error = NKE_ERROR_UNRECOGNIZED_CRITICAL_RECORD; + } + } + + if (error < 0) { + if (next_protocol_records != 1 || next_protocol_values < 1 || + (next_protocol == NKE_NEXT_PROTOCOL_NTPV4 && + (aead_algorithm_records != 1 || aead_algorithm_values < 1))) + error = NKE_ERROR_BAD_REQUEST; + } + + if (!prepare_response(session, error, next_protocol, aead_algorithm)) + return 0; + + return 1; +} + +/* ================================================== */ + +static int +handle_message(void *arg) +{ + NKSN_Instance session = arg; + + return process_request(session); +} + +/* ================================================== */ + +static void +generate_key(int index) +{ + int key_length; + + if (index < 0 || index >= MAX_SERVER_KEYS) + assert(0); + + key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV); + if (key_length > sizeof (server_keys[index].key)) + assert(0); + + UTI_GetRandomBytesUrandom(server_keys[index].key, key_length); + + if (!server_keys[index].siv || + !SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length)) + LOG_FATAL("Could not set SIV key"); + + UTI_GetRandomBytes(&server_keys[index].id, sizeof (server_keys[index].id)); + + /* Encode the index in the lowest bits of the ID */ + server_keys[index].id &= -1U << KEY_ID_INDEX_BITS; + server_keys[index].id |= index; + + DEBUG_LOG("Generated server key %"PRIX32, server_keys[index].id); + + last_server_key_ts = SCH_GetLastEventMonoTime(); +} + +/* ================================================== */ + +static void +save_keys(void) +{ + char buf[SIV_MAX_KEY_LENGTH * 2 + 1], *dump_dir; + int i, index, key_length; + double last_key_age; + FILE *f; + + /* Don't save the keys if rotation is disabled to enable an external + management of the keys (e.g. share them with another server) */ + if (key_rotation_interval == 0) + return; + + dump_dir = CNF_GetNtsDumpDir(); + if (!dump_dir) + return; + + f = UTI_OpenFile(dump_dir, DUMP_FILENAME, ".tmp", 'w', 0600); + if (!f) + return; + + key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV); + last_key_age = SCH_GetLastEventMonoTime() - last_server_key_ts; + + if (fprintf(f, "%s%d %.1f\n", DUMP_IDENTIFIER, SERVER_COOKIE_SIV, last_key_age) < 0) + goto error; + + for (i = 0; i < MAX_SERVER_KEYS; i++) { + index = (current_server_key + i + 1 + FUTURE_KEYS) % MAX_SERVER_KEYS; + + if (key_length > sizeof (server_keys[index].key) || + !UTI_BytesToHex(server_keys[index].key, key_length, buf, sizeof (buf)) || + fprintf(f, "%08"PRIX32" %s\n", server_keys[index].id, buf) < 0) + goto error; + } + + fclose(f); + + /* Rename the temporary file, or remove it if that fails */ + if (!UTI_RenameTempFile(dump_dir, DUMP_FILENAME, ".tmp", NULL)) { + if (!UTI_RemoveFile(dump_dir, DUMP_FILENAME, ".tmp")) + ; + } + + return; + +error: + DEBUG_LOG("Could not %s server keys", "save"); + fclose(f); + + if (!UTI_RemoveFile(dump_dir, DUMP_FILENAME, NULL)) + ; +} + +/* ================================================== */ + +#define MAX_WORDS 2 + +static int +load_keys(void) +{ + char *dump_dir, line[1024], *words[MAX_WORDS]; + unsigned char key[SIV_MAX_KEY_LENGTH]; + int i, index, key_length, algorithm; + double key_age; + FILE *f; + uint32_t id; + + dump_dir = CNF_GetNtsDumpDir(); + if (!dump_dir) + return 0; + + f = UTI_OpenFile(dump_dir, DUMP_FILENAME, NULL, 'r', 0); + if (!f) + return 0; + + if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 || + !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 || + sscanf(words[0], "%d", &algorithm) != 1 || algorithm != SERVER_COOKIE_SIV || + sscanf(words[1], "%lf", &key_age) != 1) + goto error; + + key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV); + last_server_key_ts = SCH_GetLastEventMonoTime() - MAX(key_age, 0.0); + + for (i = 0; i < MAX_SERVER_KEYS && fgets(line, sizeof (line), f); i++) { + if (UTI_SplitString(line, words, MAX_WORDS) != 2 || + sscanf(words[0], "%"PRIX32, &id) != 1) + goto error; + + if (UTI_HexToBytes(words[1], key, sizeof (key)) != key_length) + goto error; + + index = id % MAX_SERVER_KEYS; + + server_keys[index].id = id; + assert(sizeof (server_keys[index].key) == sizeof (key)); + memcpy(server_keys[index].key, key, key_length); + + if (!SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length)) + LOG_FATAL("Could not set SIV key"); + + DEBUG_LOG("Loaded key %"PRIX32, id); + + current_server_key = (index + MAX_SERVER_KEYS - FUTURE_KEYS) % MAX_SERVER_KEYS; + } + + fclose(f); + + return 1; + +error: + DEBUG_LOG("Could not %s server keys", "load"); + fclose(f); + + return 0; +} + +/* ================================================== */ + +static void +key_timeout(void *arg) +{ + current_server_key = (current_server_key + 1) % MAX_SERVER_KEYS; + generate_key((current_server_key + FUTURE_KEYS) % MAX_SERVER_KEYS); + save_keys(); + + SCH_AddTimeoutByDelay(key_rotation_interval, key_timeout, NULL); +} + +/* ================================================== */ + +static void +run_helper(uid_t uid, gid_t gid, int scfilter_level) +{ + LOG_Severity log_severity; + + /* Finish minimal initialisation and run using the scheduler loop + similarly to the main process */ + + DEBUG_LOG("Helper started"); + + /* Suppress a log message about disabled clock control */ + log_severity = LOG_GetMinSeverity(); + LOG_SetMinSeverity(LOGS_ERR); + + SYS_Initialise(0); + LOG_SetMinSeverity(log_severity); + + if (!geteuid() && (uid || gid)) + SYS_DropRoot(uid, gid, SYS_NTSKE_HELPER); + + NKS_Initialise(); + + UTI_SetQuitSignalsHandler(helper_signal, 1); + if (scfilter_level != 0) + SYS_EnableSystemCallFilter(scfilter_level, SYS_NTSKE_HELPER); + + SCH_MainLoop(); + + DEBUG_LOG("Helper exiting"); + + NKS_Finalise(); + SCK_Finalise(); + SYS_Finalise(); + SCH_Finalise(); + LCL_Finalise(); + PRV_Finalise(); + CNF_Finalise(); + LOG_Finalise(); + + exit(0); +} + +/* ================================================== */ + +void +NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level) +{ + int i, processes, sock_fd1, sock_fd2; + const char **certs, **keys; + char prefix[16]; + pid_t pid; + + helper_sock_fd = INVALID_SOCK_FD; + is_helper = 0; + + if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0) + return; + + processes = CNF_GetNtsServerProcesses(); + if (processes <= 0) + return; + + /* Start helper processes to perform (computationally expensive) NTS-KE + sessions with clients on sockets forwarded from the main process */ + + sock_fd1 = SCK_OpenUnixSocketPair(0, &sock_fd2); + if (sock_fd1 < 0) + LOG_FATAL("Could not open socket pair"); + + for (i = 0; i < processes; i++) { + pid = fork(); + + if (pid < 0) + LOG_FATAL("fork() failed : %s", strerror(errno)); + + if (pid > 0) + continue; + + is_helper = 1; + + snprintf(prefix, sizeof (prefix), "nks#%d:", i + 1); + LOG_SetDebugPrefix(prefix); + LOG_CloseParentFd(); + + SCK_CloseSocket(sock_fd1); + SCH_AddFileHandler(sock_fd2, SCH_FILE_INPUT, handle_helper_request, NULL); + + run_helper(uid, gid, scfilter_level); + } + + SCK_CloseSocket(sock_fd2); + helper_sock_fd = sock_fd1; +} + +/* ================================================== */ + +void +NKS_Initialise(void) +{ + const char **certs, **keys; + int i, n_certs_keys; + double key_delay; + + server_sock_fd4 = INVALID_SOCK_FD; + server_sock_fd6 = INVALID_SOCK_FD; + + n_certs_keys = CNF_GetNtsServerCertAndKeyFiles(&certs, &keys); + if (n_certs_keys <= 0) + return; + + if (helper_sock_fd == INVALID_SOCK_FD) { + server_credentials = NKSN_CreateServerCertCredentials(certs, keys, n_certs_keys); + if (!server_credentials) + return; + } else { + server_credentials = NULL; + } + + sessions = ARR_CreateInstance(sizeof (NKSN_Instance)); + for (i = 0; i < CNF_GetNtsServerConnections(); i++) + *(NKSN_Instance *)ARR_GetNewElement(sessions) = NULL; + + /* Generate random keys, even if they will be replaced by reloaded keys, + or unused (in the helper) */ + for (i = 0; i < MAX_SERVER_KEYS; i++) { + server_keys[i].siv = SIV_CreateInstance(SERVER_COOKIE_SIV); + generate_key(i); + } + + current_server_key = MAX_SERVER_KEYS - 1; + + if (!is_helper) { + server_sock_fd4 = open_socket(IPADDR_INET4); + server_sock_fd6 = open_socket(IPADDR_INET6); + + 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) { + key_delay = key_rotation_interval - (SCH_GetLastEventMonoTime() - last_server_key_ts); + SCH_AddTimeoutByDelay(MAX(key_delay, 0.0), key_timeout, NULL); + } + } + + initialised = 1; +} + +/* ================================================== */ + +void +NKS_Finalise(void) +{ + int i; + + if (!initialised) + return; + + if (helper_sock_fd != INVALID_SOCK_FD) { + /* Send the helpers a request to exit */ + for (i = 0; i < CNF_GetNtsServerProcesses(); i++) { + if (!SCK_Send(helper_sock_fd, "", 1, 0)) + ; + } + SCK_CloseSocket(helper_sock_fd); + } + if (server_sock_fd4 != INVALID_SOCK_FD) + SCK_CloseSocket(server_sock_fd4); + if (server_sock_fd6 != INVALID_SOCK_FD) + SCK_CloseSocket(server_sock_fd6); + + if (!is_helper) + save_keys(); + + for (i = 0; i < MAX_SERVER_KEYS; i++) + SIV_DestroyInstance(server_keys[i].siv); + + for (i = 0; i < ARR_GetSize(sessions); i++) { + NKSN_Instance session = *(NKSN_Instance *)ARR_GetElement(sessions, i); + if (session) + NKSN_DestroyInstance(session); + } + ARR_DestroyInstance(sessions); + + if (server_credentials) + NKSN_DestroyCertCredentials(server_credentials); +} + +/* ================================================== */ + +void +NKS_DumpKeys(void) +{ + save_keys(); +} + +/* ================================================== */ + +void +NKS_ReloadKeys(void) +{ + /* Don't load the keys if they are expected to be generated by this server + instance (i.e. they are already loaded) to not delay the next rotation */ + if (key_rotation_interval > 0) + return; + + load_keys(); +} + +/* ================================================== */ + +/* A server cookie consists of key ID, nonce, and encrypted C2S+S2C keys */ + +int +NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie) +{ + unsigned char plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext; + int plaintext_length, tag_length; + ServerCookieHeader *header; + ServerKey *key; + + if (!initialised) { + DEBUG_LOG("NTS server disabled"); + return 0; + } + + /* The algorithm is hardcoded for now */ + if (context->algorithm != AEAD_AES_SIV_CMAC_256) { + DEBUG_LOG("Unexpected SIV algorithm"); + return 0; + } + + if (context->c2s.length < 0 || context->c2s.length > NKE_MAX_KEY_LENGTH || + context->s2c.length < 0 || context->s2c.length > NKE_MAX_KEY_LENGTH) { + DEBUG_LOG("Invalid key length"); + return 0; + } + + key = &server_keys[current_server_key]; + + header = (ServerCookieHeader *)cookie->cookie; + + header->key_id = htonl(key->id); + UTI_GetRandomBytes(header->nonce, sizeof (header->nonce)); + + plaintext_length = context->c2s.length + context->s2c.length; + assert(plaintext_length <= sizeof (plaintext)); + memcpy(plaintext, context->c2s.key, context->c2s.length); + memcpy(plaintext + context->c2s.length, context->s2c.key, context->s2c.length); + + tag_length = SIV_GetTagLength(key->siv); + cookie->length = sizeof (*header) + plaintext_length + tag_length; + assert(cookie->length <= sizeof (cookie->cookie)); + ciphertext = cookie->cookie + sizeof (*header); + + if (!SIV_Encrypt(key->siv, header->nonce, sizeof (header->nonce), + "", 0, + plaintext, plaintext_length, + ciphertext, plaintext_length + tag_length)) { + DEBUG_LOG("Could not encrypt cookie"); + return 0; + } + + return 1; +} + +/* ================================================== */ + +int +NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context) +{ + unsigned char plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext; + int ciphertext_length, plaintext_length, tag_length; + ServerCookieHeader *header; + ServerKey *key; + uint32_t key_id; + + if (!initialised) { + DEBUG_LOG("NTS server disabled"); + return 0; + } + + if (cookie->length <= (int)sizeof (*header)) { + DEBUG_LOG("Invalid cookie length"); + return 0; + } + + header = (ServerCookieHeader *)cookie->cookie; + ciphertext = cookie->cookie + sizeof (*header); + ciphertext_length = cookie->length - sizeof (*header); + + key_id = ntohl(header->key_id); + key = &server_keys[key_id % MAX_SERVER_KEYS]; + if (key_id != key->id) { + DEBUG_LOG("Unknown key %"PRIX32, key_id); + return 0; + } + + tag_length = SIV_GetTagLength(key->siv); + if (tag_length >= ciphertext_length) { + DEBUG_LOG("Invalid cookie length"); + return 0; + } + + plaintext_length = ciphertext_length - tag_length; + if (plaintext_length > sizeof (plaintext) || plaintext_length % 2 != 0) { + DEBUG_LOG("Invalid cookie length"); + return 0; + } + + if (!SIV_Decrypt(key->siv, header->nonce, sizeof (header->nonce), + "", 0, + ciphertext, ciphertext_length, + plaintext, plaintext_length)) { + DEBUG_LOG("Could not decrypt cookie"); + return 0; + } + + context->algorithm = AEAD_AES_SIV_CMAC_256; + + context->c2s.length = plaintext_length / 2; + context->s2c.length = plaintext_length / 2; + assert(context->c2s.length <= sizeof (context->c2s.key)); + + memcpy(context->c2s.key, plaintext, context->c2s.length); + memcpy(context->s2c.key, plaintext + context->c2s.length, context->s2c.length); + + return 1; +} diff -Nru chrony-3.5/nts_ke_server.h chrony-4.1/nts_ke_server.h --- chrony-3.5/nts_ke_server.h 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/nts_ke_server.h 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,49 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + Header file for the NTS-KE server + */ + +#ifndef GOT_NTS_KE_SERVER_H +#define GOT_NTS_KE_SERVER_H + +#include "nts_ke.h" + +/* Init and fini functions */ +extern void NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level); +extern void NKS_Initialise(void); +extern void NKS_Finalise(void); + +/* Save the current server keys */ +extern void NKS_DumpKeys(void); + +/* Reload the keys */ +extern void NKS_ReloadKeys(void); + +/* Generate an NTS cookie with a given context */ +extern int NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie); + +/* Validate a cookie and decode the context */ +extern int NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context); + +#endif diff -Nru chrony-3.5/nts_ke_session.c chrony-4.1/nts_ke_session.c --- chrony-3.5/nts_ke_session.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/nts_ke_session.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,920 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020-2021 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + NTS-KE session used by server and client + */ + +#include "config.h" + +#include "sysincl.h" + +#include "nts_ke_session.h" + +#include "conf.h" +#include "local.h" +#include "logging.h" +#include "memory.h" +#include "siv.h" +#include "socket.h" +#include "sched.h" +#include "util.h" + +#include +#include + +#define INVALID_SOCK_FD (-8) + +struct RecordHeader { + uint16_t type; + uint16_t body_length; +}; + +struct Message { + int length; + int sent; + int parsed; + int complete; + unsigned char data[NKE_MAX_MESSAGE_LENGTH]; +}; + +typedef enum { + KE_WAIT_CONNECT, + KE_HANDSHAKE, + KE_SEND, + KE_RECEIVE, + KE_SHUTDOWN, + KE_STOPPED, +} KeState; + +struct NKSN_Instance_Record { + int server; + char *server_name; + NKSN_MessageHandler handler; + void *handler_arg; + + KeState state; + int sock_fd; + char *label; + gnutls_session_t tls_session; + SCH_TimeoutID timeout_id; + int retry_factor; + + struct Message message; + int new_message; +}; + +/* ================================================== */ + +static gnutls_priority_t priority_cache; + +static int credentials_counter = 0; + +static int clock_updates = 0; + +/* ================================================== */ + +static void +reset_message(struct Message *message) +{ + message->length = 0; + message->sent = 0; + message->parsed = 0; + message->complete = 0; +} + +/* ================================================== */ + +static int +add_record(struct Message *message, int critical, int type, const void *body, int body_length) +{ + struct RecordHeader header; + + assert(message->length <= sizeof (message->data)); + + if (body_length < 0 || body_length > 0xffff || type < 0 || type > 0x7fff || + message->length + sizeof (header) + body_length > sizeof (message->data)) + return 0; + + header.type = htons(!!critical * NKE_RECORD_CRITICAL_BIT | type); + header.body_length = htons(body_length); + + memcpy(&message->data[message->length], &header, sizeof (header)); + message->length += sizeof (header); + + if (body_length > 0) { + memcpy(&message->data[message->length], body, body_length); + message->length += body_length; + } + + return 1; +} + +/* ================================================== */ + +static void +reset_message_parsing(struct Message *message) +{ + message->parsed = 0; +} + +/* ================================================== */ + +static int +get_record(struct Message *message, int *critical, int *type, int *body_length, + void *body, int buffer_length) +{ + struct RecordHeader header; + int blen, rlen; + + if (message->length < message->parsed + sizeof (header) || + buffer_length < 0) + return 0; + + memcpy(&header, &message->data[message->parsed], sizeof (header)); + + blen = ntohs(header.body_length); + rlen = sizeof (header) + blen; + assert(blen >= 0 && rlen > 0); + + if (message->length < message->parsed + rlen) + return 0; + + if (critical) + *critical = !!(ntohs(header.type) & NKE_RECORD_CRITICAL_BIT); + if (type) + *type = ntohs(header.type) & ~NKE_RECORD_CRITICAL_BIT; + if (body) + memcpy(body, &message->data[message->parsed + sizeof (header)], MIN(buffer_length, blen)); + if (body_length) + *body_length = blen; + + message->parsed += rlen; + + return 1; +} + +/* ================================================== */ + +static int +check_message_format(struct Message *message, int eof) +{ + int critical = 0, type = -1, length = -1, ends = 0; + + reset_message_parsing(message); + message->complete = 0; + + while (get_record(message, &critical, &type, &length, NULL, 0)) { + if (type == NKE_RECORD_END_OF_MESSAGE) { + if (!critical || length != 0 || ends > 0) + return 0; + ends++; + } + } + + /* If the message cannot be fully parsed, but more data may be coming, + consider the format to be ok */ + if (message->length == 0 || message->parsed < message->length) + return !eof; + + if (type != NKE_RECORD_END_OF_MESSAGE) + return !eof; + + message->complete = 1; + + return 1; +} + +/* ================================================== */ + +static gnutls_session_t +create_tls_session(int server_mode, int sock_fd, const char *server_name, + gnutls_certificate_credentials_t credentials, + gnutls_priority_t priority) +{ + unsigned char alpn_name[sizeof (NKE_ALPN_NAME)]; + gnutls_session_t session; + gnutls_datum_t alpn; + 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) +{ + if (inst->state == KE_STOPPED) + return; + + inst->state = KE_STOPPED; + + SCH_RemoveFileHandler(inst->sock_fd); + SCK_CloseSocket(inst->sock_fd); + inst->sock_fd = INVALID_SOCK_FD; + + Free(inst->label); + inst->label = NULL; + + gnutls_deinit(inst->tls_session); + inst->tls_session = NULL; + + SCH_RemoveTimeout(inst->timeout_id); + inst->timeout_id = 0; +} + +/* ================================================== */ + +static void +session_timeout(void *arg) +{ + NKSN_Instance inst = arg; + + LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE session with %s timed out", inst->label); + + inst->timeout_id = 0; + stop_session(inst); +} + +/* ================================================== */ + +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) +{ + SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_INPUT, !output); + SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT, output); +} + +/* ================================================== */ + +static void +change_state(NKSN_Instance inst, KeState state) +{ + int output; + + switch (state) { + case KE_HANDSHAKE: + output = !inst->server; + break; + case KE_WAIT_CONNECT: + case KE_SEND: + case KE_SHUTDOWN: + output = 1; + break; + case KE_RECEIVE: + output = 0; + break; + default: + assert(0); + } + + set_input_output(inst, output); + + inst->state = state; +} + +/* ================================================== */ + +static int +handle_event(NKSN_Instance inst, int event) +{ + struct Message *message = &inst->message; + int r; + + DEBUG_LOG("Session event %d fd=%d state=%d", event, inst->sock_fd, (int)inst->state); + + switch (inst->state) { + case KE_WAIT_CONNECT: + /* Check if connect() succeeded */ + if (event != SCH_FILE_OUTPUT) + return 0; + + /* Get the socket error */ + if (!SCK_GetIntOption(inst->sock_fd, SOL_SOCKET, SO_ERROR, &r)) + r = EINVAL; + + if (r != 0) { + LOG(LOGS_ERR, "Could not connect to %s : %s", inst->label, strerror(r)); + stop_session(inst); + return 0; + } + + DEBUG_LOG("Connected to %s", inst->label); + + change_state(inst, KE_HANDSHAKE); + return 0; + + case KE_HANDSHAKE: + r = gnutls_handshake(inst->tls_session); + + if (r < 0) { + if (gnutls_error_is_fatal(r)) { + 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); + + 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) + 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; + + case KE_SEND: + assert(inst->new_message && message->complete); + assert(message->length <= sizeof (message->data) && message->length > message->sent); + + r = gnutls_record_send(inst->tls_session, &message->data[message->sent], + message->length - message->sent); + + 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)); + stop_session(inst); + } + return 0; + } + + DEBUG_LOG("Sent %d bytes to %s", r, inst->label); + + message->sent += r; + if (message->sent < message->length) + return 0; + + /* Client will receive a response */ + change_state(inst, inst->server ? KE_SHUTDOWN : KE_RECEIVE); + reset_message(&inst->message); + inst->new_message = 0; + return 0; + + case KE_RECEIVE: + do { + if (message->length >= sizeof (message->data)) { + DEBUG_LOG("Message is too long"); + stop_session(inst); + return 0; + } + + r = gnutls_record_recv(inst->tls_session, &message->data[message->length], + sizeof (message->data) - message->length); + + if (r < 0) { + /* Handle a renegotiation request on both client and server as + a protocol error */ + if (gnutls_error_is_fatal(r) || r == GNUTLS_E_REHANDSHAKE) { + LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, + "Could not receive NTS-KE message from %s : %s", + inst->label, gnutls_strerror(r)); + stop_session(inst); + } + return 0; + } + + DEBUG_LOG("Received %d bytes from %s", r, inst->label); + + message->length += r; + + } while (gnutls_record_check_pending(inst->tls_session) > 0); + + if (!check_message_format(message, r == 0)) { + LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, + "Received invalid NTS-KE message from %s", inst->label); + stop_session(inst); + return 0; + } + + /* Wait for more data if the message is not complete yet */ + if (!message->complete) + return 0; + + /* Server will send a response to the client */ + change_state(inst, inst->server ? KE_SEND : KE_SHUTDOWN); + + /* Return success to process the received message */ + return 1; + + case KE_SHUTDOWN: + r = gnutls_bye(inst->tls_session, GNUTLS_SHUT_RDWR); + + if (r < 0) { + if (gnutls_error_is_fatal(r)) { + DEBUG_LOG("Shutdown with %s failed : %s", inst->label, gnutls_strerror(r)); + 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); + stop_session(inst); + + DEBUG_LOG("Shutdown completed"); + return 0; + + default: + assert(0); + return 0; + } +} + +/* ================================================== */ + +static void +read_write_socket(int fd, int event, void *arg) +{ + NKSN_Instance inst = arg; + + if (!handle_event(inst, event)) + return; + + /* A valid message was received. Call the handler to process the message, + and prepare a response if it is a server. */ + + reset_message_parsing(&inst->message); + + if (!(inst->handler)(inst->handler_arg)) { + stop_session(inst); + return; + } +} + +/* ================================================== */ + +static time_t +get_time(time_t *t) +{ + struct timespec now; + + LCL_ReadCookedTime(&now, NULL); + if (t) + *t = now.tv_sec; + + return now.tv_sec; +} + +/* ================================================== */ + +static void +handle_step(struct timespec *raw, struct timespec *cooked, double dfreq, + double doffset, LCL_ChangeType change_type, void *anything) +{ + if (change_type != LCL_ChangeUnknownStep && clock_updates < INT_MAX) + clock_updates++; +} + +/* ================================================== */ + +static int gnutls_initialised = 0; + +static void +init_gnutls(void) +{ + int r; + + if (gnutls_initialised) + return; + + r = gnutls_global_init(); + if (r < 0) + LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r)); + + /* 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_FATAL("Could not initialise %s : %s", "priority cache", gnutls_strerror(r)); + + /* Use our clock instead of the system clock in certificate verification */ + gnutls_global_set_time_function(get_time); + + gnutls_initialised = 1; + DEBUG_LOG("Initialised"); + + LCL_AddParameterChangeHandler(handle_step, NULL); +} + +/* ================================================== */ + +static void +deinit_gnutls(void) +{ + if (!gnutls_initialised || credentials_counter > 0) + return; + + LCL_RemoveParameterChangeHandler(handle_step, NULL); + + gnutls_priority_deinit(priority_cache); + gnutls_global_deinit(); + gnutls_initialised = 0; + DEBUG_LOG("Deinitialised"); +} + +/* ================================================== */ + +static NKSN_Credentials +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; + + init_gnutls(); + + r = gnutls_certificate_allocate_credentials(&credentials); + if (r < 0) + goto error; + + if (certs && keys) { + if (trusted_certs || trusted_certs_ids) + assert(0); + + for (i = 0; i < n_certs_keys; i++) { + r = gnutls_certificate_set_x509_key_file(credentials, certs[i], keys[i], + GNUTLS_X509_FMT_PEM); + if (r < 0) + goto error; + } + } else { + if (certs || keys || n_certs_keys > 0) + assert(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_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; +} + +/* ================================================== */ + +NKSN_Credentials +NKSN_CreateServerCertCredentials(const char **certs, const char **keys, int n_certs_keys) +{ + return create_credentials(certs, keys, n_certs_keys, NULL, NULL, 0, 0); +} + +/* ================================================== */ + +NKSN_Credentials +NKSN_CreateClientCertCredentials(const char **certs, uint32_t *ids, + int n_certs_ids, uint32_t trusted_cert_set) +{ + return create_credentials(NULL, NULL, 0, certs, ids, n_certs_ids, trusted_cert_set); +} + +/* ================================================== */ + +void +NKSN_DestroyCertCredentials(NKSN_Credentials credentials) +{ + gnutls_certificate_free_credentials((gnutls_certificate_credentials_t)credentials); + credentials_counter--; + deinit_gnutls(); +} + +/* ================================================== */ + +NKSN_Instance +NKSN_CreateInstance(int server_mode, const char *server_name, + NKSN_MessageHandler handler, void *handler_arg) +{ + NKSN_Instance inst; + + inst = MallocNew(struct NKSN_Instance_Record); + + inst->server = server_mode; + inst->server_name = server_name ? Strdup(server_name) : NULL; + inst->handler = handler; + inst->handler_arg = handler_arg; + /* Replace a NULL argument with the session itself */ + if (!inst->handler_arg) + inst->handler_arg = inst; + + inst->state = KE_STOPPED; + inst->sock_fd = INVALID_SOCK_FD; + inst->label = NULL; + inst->tls_session = NULL; + inst->timeout_id = 0; + inst->retry_factor = NKE_RETRY_FACTOR2_CONNECT; + + return inst; +} + +/* ================================================== */ + +void +NKSN_DestroyInstance(NKSN_Instance inst) +{ + stop_session(inst); + + Free(inst->server_name); + Free(inst); +} + +/* ================================================== */ + +int +NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label, + NKSN_Credentials credentials, double timeout) +{ + 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); + if (!inst->tls_session) + return 0; + + inst->sock_fd = sock_fd; + SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, inst); + + inst->label = Strdup(label); + inst->timeout_id = SCH_AddTimeoutByDelay(timeout, session_timeout, inst); + inst->retry_factor = NKE_RETRY_FACTOR2_CONNECT; + + reset_message(&inst->message); + inst->new_message = 0; + + change_state(inst, inst->server ? KE_HANDSHAKE : KE_WAIT_CONNECT); + + return 1; +} + +/* ================================================== */ + +void +NKSN_BeginMessage(NKSN_Instance inst) +{ + reset_message(&inst->message); + inst->new_message = 1; +} + +/* ================================================== */ + +int +NKSN_AddRecord(NKSN_Instance inst, int critical, int type, const void *body, int body_length) +{ + assert(inst->new_message && !inst->message.complete); + assert(type != NKE_RECORD_END_OF_MESSAGE); + + return add_record(&inst->message, critical, type, body, body_length); +} + +/* ================================================== */ + +int +NKSN_EndMessage(NKSN_Instance inst) +{ + assert(!inst->message.complete); + + /* Terminate the message */ + if (!add_record(&inst->message, 1, NKE_RECORD_END_OF_MESSAGE, NULL, 0)) + return 0; + + inst->message.complete = 1; + + return 1; +} + +/* ================================================== */ + +int +NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length, + void *body, int buffer_length) +{ + int type2; + + assert(inst->message.complete); + + if (body_length) + *body_length = 0; + + if (!get_record(&inst->message, critical, &type2, body_length, body, buffer_length)) + return 0; + + /* Hide the end-of-message record */ + if (type2 == NKE_RECORD_END_OF_MESSAGE) + return 0; + + if (type) + *type = type2; + + return 1; +} + +/* ================================================== */ + +int +NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c) +{ + int length = SIV_GetKeyLength(siv); + + if (length <= 0 || length > sizeof (c2s->key) || length > sizeof (s2c->key)) { + DEBUG_LOG("Invalid algorithm"); + return 0; + } + + if (gnutls_prf_rfc5705(inst->tls_session, + sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL, + sizeof (NKE_EXPORTER_CONTEXT_C2S) - 1, NKE_EXPORTER_CONTEXT_C2S, + length, (char *)c2s->key) < 0 || + gnutls_prf_rfc5705(inst->tls_session, + sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL, + sizeof (NKE_EXPORTER_CONTEXT_S2C) - 1, NKE_EXPORTER_CONTEXT_S2C, + length, (char *)s2c->key) < 0) { + DEBUG_LOG("Could not export key"); + return 0; + } + + c2s->length = length; + s2c->length = length; + + return 1; +} + +/* ================================================== */ + +int +NKSN_IsStopped(NKSN_Instance inst) +{ + return inst->state == KE_STOPPED; +} + +/* ================================================== */ + +void +NKSN_StopSession(NKSN_Instance inst) +{ + stop_session(inst); +} + +/* ================================================== */ + +int +NKSN_GetRetryFactor(NKSN_Instance inst) +{ + return inst->retry_factor; +} diff -Nru chrony-3.5/nts_ke_session.h chrony-4.1/nts_ke_session.h --- chrony-3.5/nts_ke_session.h 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/nts_ke_session.h 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,93 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + Header file for the NTS-KE session + */ + +#ifndef GOT_NTS_KE_SESSION_H +#define GOT_NTS_KE_SESSION_H + +#include "nts_ke.h" +#include "siv.h" + +typedef struct NKSN_Credentials_Record *NKSN_Credentials; + +typedef struct NKSN_Instance_Record *NKSN_Instance; + +/* Handler for received NTS-KE messages. A zero return code stops + the session. */ +typedef int (*NKSN_MessageHandler)(void *arg); + +/* Get server or client credentials using a server certificate and key, + or certificates of trusted CAs. The credentials may be shared between + different clients or servers. */ +extern NKSN_Credentials NKSN_CreateServerCertCredentials(const char **certs, const char **keys, + int n_certs_keys); +extern NKSN_Credentials NKSN_CreateClientCertCredentials(const char **certs, uint32_t *ids, + int n_certs_ids, + uint32_t trusted_cert_set); + +/* Destroy the credentials */ +extern void NKSN_DestroyCertCredentials(NKSN_Credentials credentials); + +/* Create an instance */ +extern NKSN_Instance NKSN_CreateInstance(int server_mode, const char *server_name, + NKSN_MessageHandler handler, void *handler_arg); + +/* Destroy an instance */ +extern void NKSN_DestroyInstance(NKSN_Instance inst); + +/* Start a new NTS-KE session */ +extern int NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label, + NKSN_Credentials credentials, double timeout); + +/* Begin an NTS-KE message. A request should be made right after starting + the session and response should be made in the message handler. */ +extern void NKSN_BeginMessage(NKSN_Instance inst); + +/* Add a record to the message */ +extern int NKSN_AddRecord(NKSN_Instance inst, int critical, int type, + const void *body, int body_length); + +/* Terminate the message */ +extern int NKSN_EndMessage(NKSN_Instance inst); + +/* Get the next record from the received message. This function should be + called from the message handler. */ +extern int NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length, + void *body, int buffer_length); + +/* Export NTS keys for a specified algorithm */ +extern int NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c); + +/* Check if the session has stopped */ +extern int NKSN_IsStopped(NKSN_Instance inst); + +/* Stop the session */ +extern void NKSN_StopSession(NKSN_Instance inst); + +/* Get a factor to calculate retry interval (in log2 seconds) + based on the session state or how it was terminated */ +extern int NKSN_GetRetryFactor(NKSN_Instance inst); + +#endif diff -Nru chrony-3.5/nts_ntp_auth.c chrony-4.1/nts_ntp_auth.c --- chrony-3.5/nts_ntp_auth.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/nts_ntp_auth.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,183 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + NTS Authenticator and Encrypted Extension Fields extension field + */ + +#include "config.h" + +#include "sysincl.h" + +#include "nts_ntp_auth.h" + +#include "logging.h" +#include "ntp_ext.h" +#include "nts_ntp.h" +#include "siv.h" +#include "util.h" + +struct AuthHeader { + uint16_t nonce_length; + uint16_t ciphertext_length; +}; + +/* ================================================== */ + +static int +get_padding_length(int length) +{ + return length % 4U ? 4 - length % 4U : 0; +} + +/* ================================================== */ + +static int +get_padded_length(int length) +{ + return length + get_padding_length(length); +} + +/* ================================================== */ + +int +NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, + const unsigned char *nonce, int nonce_length, + const unsigned char *plaintext, int plaintext_length, + int min_ef_length) +{ + int auth_length, ciphertext_length, assoc_length; + int nonce_padding, ciphertext_padding, additional_padding; + unsigned char *ciphertext, *body; + struct AuthHeader *header; + + assert(sizeof (*header) == 4); + + if (nonce_length <= 0 || plaintext_length < 0) { + DEBUG_LOG("Invalid nonce/plaintext length"); + return 0; + } + + assoc_length = info->length; + ciphertext_length = SIV_GetTagLength(siv) + plaintext_length; + nonce_padding = get_padding_length(nonce_length); + ciphertext_padding = get_padding_length(ciphertext_length); + min_ef_length = get_padded_length(min_ef_length); + + auth_length = sizeof (*header) + nonce_length + nonce_padding + + ciphertext_length + ciphertext_padding; + additional_padding = MAX(min_ef_length - auth_length - 4, 0); + additional_padding = MAX(NTS_MIN_UNPADDED_NONCE_LENGTH - nonce_length - nonce_padding, + additional_padding); + auth_length += additional_padding; + + if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, auth_length, + (void **)&header)) { + DEBUG_LOG("Could not add EF"); + return 0; + } + + header->nonce_length = htons(nonce_length); + header->ciphertext_length = htons(ciphertext_length); + + body = (unsigned char *)(header + 1); + ciphertext = body + nonce_length + nonce_padding; + + if ((unsigned char *)header + auth_length != + ciphertext + ciphertext_length + ciphertext_padding + additional_padding) + assert(0); + + memcpy(body, nonce, nonce_length); + memset(body + nonce_length, 0, nonce_padding); + + if (!SIV_Encrypt(siv, nonce, nonce_length, packet, assoc_length, + plaintext, plaintext_length, ciphertext, ciphertext_length)) { + DEBUG_LOG("SIV encrypt failed"); + info->length = assoc_length; + return 0; + } + + memset(ciphertext + ciphertext_length, 0, ciphertext_padding + additional_padding); + + return 1; +} + +/* ================================================== */ + +int +NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, int ef_start, + unsigned char *plaintext, int buffer_length, int *plaintext_length) +{ + unsigned int siv_tag_length, nonce_length, ciphertext_length; + unsigned char *nonce, *ciphertext; + int ef_type, ef_body_length; + void *ef_body; + struct AuthHeader *header; + + if (buffer_length < 0) + return 0; + + if (!NEF_ParseField(packet, info->length, ef_start, + NULL, &ef_type, &ef_body, &ef_body_length)) + return 0; + + if (ef_type != NTP_EF_NTS_AUTH_AND_EEF || ef_body_length < sizeof (*header)) + return 0; + + header = ef_body; + + nonce_length = ntohs(header->nonce_length); + ciphertext_length = ntohs(header->ciphertext_length); + + if (get_padded_length(nonce_length) + + get_padded_length(ciphertext_length) > ef_body_length) + return 0; + + nonce = (unsigned char *)(header + 1); + ciphertext = nonce + get_padded_length(nonce_length); + + siv_tag_length = SIV_GetTagLength(siv); + + if (nonce_length < 1 || + ciphertext_length < siv_tag_length || + ciphertext_length - siv_tag_length > buffer_length) { + DEBUG_LOG("Unexpected nonce/ciphertext length"); + return 0; + } + + if (ef_body_length < sizeof (*header) + + NTS_MIN_UNPADDED_NONCE_LENGTH + get_padded_length(ciphertext_length)) { + DEBUG_LOG("Missing padding"); + return 0; + } + + *plaintext_length = ciphertext_length - siv_tag_length; + assert(*plaintext_length >= 0); + + if (!SIV_Decrypt(siv, nonce, nonce_length, packet, ef_start, + ciphertext, ciphertext_length, plaintext, *plaintext_length)) { + DEBUG_LOG("SIV decrypt failed"); + return 0; + } + + return 1; +} diff -Nru chrony-3.5/nts_ntp_auth.h chrony-4.1/nts_ntp_auth.h --- chrony-3.5/nts_ntp_auth.h 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/nts_ntp_auth.h 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,43 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + Header for NTS Authenticator and Encrypted Extension Fields + extension field + */ + +#ifndef GOT_NTS_NTP_AUTH_H +#define GOT_NTS_NTP_AUTH_H + +#include "ntp.h" +#include "siv.h" + +extern int NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, + const unsigned char *nonce, int nonce_length, + const unsigned char *plaintext, int plaintext_length, + int min_ef_length); + +extern int NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, + int ef_start, unsigned char *plaintext, int buffer_length, + int *plaintext_length); + +#endif diff -Nru chrony-3.5/nts_ntp_client.c chrony-4.1/nts_ntp_client.c --- chrony-3.5/nts_ntp_client.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/nts_ntp_client.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,709 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + Client NTS-NTP authentication + */ + +#include "config.h" + +#include "sysincl.h" + +#include "nts_ntp_client.h" + +#include "conf.h" +#include "logging.h" +#include "memory.h" +#include "ntp.h" +#include "ntp_ext.h" +#include "ntp_sources.h" +#include "nts_ke_client.h" +#include "nts_ntp.h" +#include "nts_ntp_auth.h" +#include "sched.h" +#include "siv.h" +#include "util.h" + +/* Maximum length of all cookies to avoid IP fragmentation */ +#define MAX_TOTAL_COOKIE_LENGTH (8 * 108) + +/* Magic string of files containing keys and cookies */ +#define DUMP_IDENTIFIER "NNC0\n" + +struct NNC_Instance_Record { + /* Address of NTS-KE server */ + IPSockAddr nts_address; + /* Hostname or IP address for certificate verification */ + char *name; + /* ID of trusted certificates */ + uint32_t cert_set; + /* Configured NTP port */ + uint16_t default_ntp_port; + /* Address of NTP server (can be negotiated in NTS-KE) */ + IPSockAddr ntp_address; + + NKC_Instance nke; + SIV_Instance siv; + + int nke_attempts; + double next_nke_attempt; + double last_nke_success; + + NKE_Context context; + unsigned int context_id; + NKE_Cookie cookies[NTS_MAX_COOKIES]; + int num_cookies; + int cookie_index; + int auth_ready; + int nak_response; + int ok_response; + unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH]; + unsigned char uniq_id[NTS_MIN_UNIQ_ID_LENGTH]; +}; + +/* ================================================== */ + +static void save_cookies(NNC_Instance inst); +static void load_cookies(NNC_Instance inst); + +/* ================================================== */ + +static void +reset_instance(NNC_Instance inst) +{ + if (inst->nke) + NKC_DestroyInstance(inst->nke); + inst->nke = NULL; + if (inst->siv) + SIV_DestroyInstance(inst->siv); + inst->siv = NULL; + + inst->nke_attempts = 0; + inst->next_nke_attempt = 0.0; + inst->last_nke_success = 0.0; + + memset(&inst->context, 0, sizeof (inst->context)); + inst->context_id = 0; + memset(inst->cookies, 0, sizeof (inst->cookies)); + inst->num_cookies = 0; + inst->cookie_index = 0; + inst->auth_ready = 0; + inst->nak_response = 0; + inst->ok_response = 1; + memset(inst->nonce, 0, sizeof (inst->nonce)); + memset(inst->uniq_id, 0, sizeof (inst->uniq_id)); +} + +/* ================================================== */ + +NNC_Instance +NNC_CreateInstance(IPSockAddr *nts_address, const char *name, uint32_t cert_set, uint16_t ntp_port) +{ + NNC_Instance inst; + + inst = MallocNew(struct NNC_Instance_Record); + + inst->nts_address = *nts_address; + inst->name = Strdup(name); + inst->cert_set = cert_set; + inst->default_ntp_port = ntp_port; + inst->ntp_address.ip_addr = nts_address->ip_addr; + inst->ntp_address.port = ntp_port; + inst->siv = NULL; + inst->nke = NULL; + + reset_instance(inst); + + /* Try to reload saved keys and cookies */ + load_cookies(inst); + + return inst; +} + +/* ================================================== */ + +void +NNC_DestroyInstance(NNC_Instance inst) +{ + save_cookies(inst); + + reset_instance(inst); + + Free(inst->name); + Free(inst); +} + +/* ================================================== */ + +static int +check_cookies(NNC_Instance inst) +{ + /* Force a new NTS-KE session if a NAK was received without a valid response, + or the keys encrypting the cookies need to be refreshed */ + if (inst->num_cookies > 0 && + ((inst->nak_response && !inst->ok_response) || + SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())) { + inst->num_cookies = 0; + DEBUG_LOG("Dropped cookies"); + } + + return inst->num_cookies > 0; +} + +/* ================================================== */ + +static int +set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address) +{ + NTP_Remote_Address old_address, new_address; + + old_address = inst->ntp_address; + new_address = *negotiated_address; + + if (new_address.ip_addr.family == IPADDR_UNSPEC) + new_address.ip_addr = inst->nts_address.ip_addr; + if (new_address.port == 0) + new_address.port = inst->default_ntp_port; + + if (UTI_CompareIPs(&old_address.ip_addr, &new_address.ip_addr, NULL) == 0 && + old_address.port == new_address.port) + /* Nothing to do */ + return 1; + + if (NSR_UpdateSourceNtpAddress(&old_address, &new_address) != NSR_Success) { + LOG(LOGS_ERR, "Could not change %s to negotiated address %s", + UTI_IPToString(&old_address.ip_addr), UTI_IPToString(&new_address.ip_addr)); + return 0; + } + + inst->ntp_address = new_address; + + return 1; +} + +/* ================================================== */ + +static void +update_next_nke_attempt(NNC_Instance inst, double now) +{ + int factor, interval; + + if (!inst->nke) + return; + + factor = NKC_GetRetryFactor(inst->nke); + interval = MIN(factor + inst->nke_attempts - 1, NKE_MAX_RETRY_INTERVAL2); + inst->next_nke_attempt = now + UTI_Log2ToDouble(interval); +} + +/* ================================================== */ + +static int +get_cookies(NNC_Instance inst) +{ + NTP_Remote_Address ntp_address; + double now; + int got_data; + + assert(inst->num_cookies == 0); + + now = SCH_GetLastEventMonoTime(); + + /* Create and start a new NTS-KE session if not already present */ + if (!inst->nke) { + if (now < inst->next_nke_attempt) { + DEBUG_LOG("Limiting NTS-KE request rate (%f seconds)", + inst->next_nke_attempt - now); + return 0; + } + + inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name, inst->cert_set); + + inst->nke_attempts++; + update_next_nke_attempt(inst, now); + + if (!NKC_Start(inst->nke)) + return 0; + } + + update_next_nke_attempt(inst, now); + + /* Wait until the session stops */ + if (NKC_IsActive(inst->nke)) + return 0; + + assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES); + + /* Get the new keys, cookies and NTP address if the session was successful */ + got_data = NKC_GetNtsData(inst->nke, &inst->context, + inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES, + &ntp_address); + + NKC_DestroyInstance(inst->nke); + inst->nke = NULL; + + if (!got_data) + return 0; + + if (inst->siv) + SIV_DestroyInstance(inst->siv); + inst->siv = NULL; + + inst->context_id++; + + /* Force a new session if the NTP address is used by another source, with + an expectation that it will eventually get a non-conflicting address */ + if (!set_ntp_address(inst, &ntp_address)) { + inst->num_cookies = 0; + return 0; + } + + inst->last_nke_success = now; + inst->cookie_index = 0; + + return 1; +} + +/* ================================================== */ + +int +NNC_PrepareForAuth(NNC_Instance inst) +{ + 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)); + + /* Get new cookies if there are not any, or they are no longer usable */ + if (!check_cookies(inst)) { + if (!get_cookies(inst)) + return 0; + } + + inst->nak_response = 0; + + if (!inst->siv) + inst->siv = SIV_CreateInstance(inst->context.algorithm); + + if (!inst->siv || + !SIV_SetKey(inst->siv, inst->context.c2s.key, inst->context.c2s.length)) { + DEBUG_LOG("Could not set SIV key"); + return 0; + } + + inst->auth_ready = 1; + + return 1; +} + +/* ================================================== */ + +int +NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet, + NTP_PacketInfo *info) +{ + NKE_Cookie *cookie; + int i, req_cookies; + void *ef_body; + + if (!inst->auth_ready) + return 0; + + inst->auth_ready = 0; + + if (inst->num_cookies <= 0 || !inst->siv) + return 0; + + if (info->mode != MODE_CLIENT) + return 0; + + cookie = &inst->cookies[inst->cookie_index]; + 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)); + + if (!NEF_AddField(packet, info, NTP_EF_NTS_UNIQUE_IDENTIFIER, + inst->uniq_id, sizeof (inst->uniq_id))) + return 0; + + if (!NEF_AddField(packet, info, NTP_EF_NTS_COOKIE, + cookie->cookie, cookie->length)) + return 0; + + for (i = 0; i < req_cookies - 1; i++) { + if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_COOKIE_PLACEHOLDER, + cookie->length, &ef_body)) + return 0; + memset(ef_body, 0, cookie->length); + } + + if (!NNA_GenerateAuthEF(packet, info, inst->siv, inst->nonce, sizeof (inst->nonce), + (const unsigned char *)"", 0, NTP_MAX_V4_MAC_LENGTH + 4)) + return 0; + + inst->ok_response = 0; + + return 1; +} + +/* ================================================== */ + +static int +parse_encrypted_efs(NNC_Instance inst, unsigned char *plaintext, int length) +{ + int ef_length, parsed; + + for (parsed = 0; parsed < length; parsed += ef_length) { + if (!NEF_ParseSingleField(plaintext, length, parsed, &ef_length, NULL, NULL, NULL)) { + DEBUG_LOG("Could not parse encrypted EF"); + return 0; + } + } + + return 1; +} + +/* ================================================== */ + +static int +extract_cookies(NNC_Instance inst, unsigned char *plaintext, int length) +{ + int ef_type, ef_body_length, ef_length, parsed, index, acceptable, saved; + void *ef_body; + + acceptable = saved = 0; + + for (parsed = 0; parsed < length; parsed += ef_length) { + if (!NEF_ParseSingleField(plaintext, length, parsed, + &ef_length, &ef_type, &ef_body, &ef_body_length)) + return 0; + + if (ef_type != NTP_EF_NTS_COOKIE) + continue; + + if (ef_length < NTP_MIN_EF_LENGTH || ef_body_length > sizeof (inst->cookies[0].cookie)) { + DEBUG_LOG("Unexpected cookie length %d", ef_body_length); + continue; + } + + acceptable++; + + if (inst->num_cookies >= NTS_MAX_COOKIES) + continue; + + index = (inst->cookie_index + inst->num_cookies) % NTS_MAX_COOKIES; + assert(index >= 0 && index < NTS_MAX_COOKIES); + assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES); + + memcpy(inst->cookies[index].cookie, ef_body, ef_body_length); + inst->cookies[index].length = ef_body_length; + inst->num_cookies++; + + saved++; + } + + DEBUG_LOG("Extracted %d cookies (saved %d)", acceptable, saved); + + return acceptable > 0; +} + +/* ================================================== */ + +int +NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet, + NTP_PacketInfo *info) +{ + int ef_type, ef_body_length, ef_length, parsed, plaintext_length; + int has_valid_uniq_id = 0, has_valid_auth = 0; + unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH]; + void *ef_body; + + if (info->ext_fields == 0 || info->mode != MODE_SERVER) + return 0; + + /* Accept at most one response per request */ + if (inst->ok_response || inst->auth_ready) + return 0; + + if (!inst->siv || + !SIV_SetKey(inst->siv, inst->context.s2c.key, inst->context.s2c.length)) { + DEBUG_LOG("Could not set SIV key"); + return 0; + } + + for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) { + if (!NEF_ParseField(packet, info->length, parsed, + &ef_length, &ef_type, &ef_body, &ef_body_length)) + /* This is not expected as the packet already passed NAU_ParsePacket() */ + return 0; + + switch (ef_type) { + case NTP_EF_NTS_UNIQUE_IDENTIFIER: + if (ef_body_length != sizeof (inst->uniq_id) || + memcmp(ef_body, inst->uniq_id, sizeof (inst->uniq_id)) != 0) { + DEBUG_LOG("Invalid uniq id"); + return 0; + } + has_valid_uniq_id = 1; + break; + case NTP_EF_NTS_COOKIE: + DEBUG_LOG("Unencrypted cookie"); + break; + case NTP_EF_NTS_AUTH_AND_EEF: + if (parsed + ef_length != info->length) { + DEBUG_LOG("Auth not last EF"); + return 0; + } + + if (!NNA_DecryptAuthEF(packet, info, inst->siv, parsed, + plaintext, sizeof (plaintext), &plaintext_length)) + return 0; + + if (!parse_encrypted_efs(inst, plaintext, plaintext_length)) + return 0; + + has_valid_auth = 1; + break; + default: + break; + } + } + + if (!has_valid_uniq_id || !has_valid_auth) { + if (has_valid_uniq_id && packet->stratum == NTP_INVALID_STRATUM && + ntohl(packet->reference_id) == NTP_KOD_NTS_NAK) { + DEBUG_LOG("NTS NAK"); + inst->nak_response = 1; + return 0; + } + + DEBUG_LOG("Missing NTS EF"); + return 0; + } + + if (!extract_cookies(inst, plaintext, plaintext_length)) + return 0; + + inst->ok_response = 1; + + /* At this point we know the client interoperates with the server. Allow a + new NTS-KE session to be started as soon as the cookies run out. */ + inst->nke_attempts = 0; + inst->next_nke_attempt = 0.0; + + return 1; +} + +/* ================================================== */ + +void +NNC_ChangeAddress(NNC_Instance inst, IPAddr *address) +{ + save_cookies(inst); + + inst->nts_address.ip_addr = *address; + inst->ntp_address.ip_addr = *address; + + reset_instance(inst); + + DEBUG_LOG("NTS reset"); + + load_cookies(inst); +} + +/* ================================================== */ + +static void +save_cookies(NNC_Instance inst) +{ + char buf[2 * NKE_MAX_COOKIE_LENGTH + 2], *dump_dir, *filename; + struct timespec now; + double context_time; + FILE *f; + int i; + + if (inst->num_cookies < 1 || !UTI_IsIPReal(&inst->nts_address.ip_addr)) + return; + + dump_dir = CNF_GetNtsDumpDir(); + if (!dump_dir) + return; + + filename = UTI_IPToString(&inst->nts_address.ip_addr); + + f = UTI_OpenFile(dump_dir, filename, ".tmp", 'w', 0600); + if (!f) + return; + + SCH_GetLastEventTime(&now, NULL, NULL); + context_time = inst->last_nke_success - SCH_GetLastEventMonoTime(); + context_time += UTI_TimespecToDouble(&now); + + if (fprintf(f, "%s%s\n%.1f\n%s %d\n%u %d ", + DUMP_IDENTIFIER, inst->name, context_time, + 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)) || + fprintf(f, "%s ", buf) < 0 || + !UTI_BytesToHex(inst->context.c2s.key, inst->context.c2s.length, buf, sizeof (buf)) || + fprintf(f, "%s\n", buf) < 0) + goto error; + + for (i = 0; i < inst->num_cookies; i++) { + if (!UTI_BytesToHex(inst->cookies[i].cookie, inst->cookies[i].length, buf, sizeof (buf)) || + fprintf(f, "%s\n", buf) < 0) + goto error; + } + + fclose(f); + + if (!UTI_RenameTempFile(dump_dir, filename, ".tmp", ".nts")) + ; + return; + +error: + DEBUG_LOG("Could not %s cookies for %s", "save", filename); + fclose(f); + + if (!UTI_RemoveFile(dump_dir, filename, ".nts")) + ; +} + +/* ================================================== */ + +#define MAX_WORDS 4 + +static void +load_cookies(NNC_Instance inst) +{ + char line[2 * NKE_MAX_COOKIE_LENGTH + 2], *dump_dir, *filename, *words[MAX_WORDS]; + unsigned int context_id; + int i, algorithm, port; + double context_time; + struct timespec now; + IPSockAddr ntp_addr; + FILE *f; + + dump_dir = CNF_GetNtsDumpDir(); + if (!dump_dir) + return; + + filename = UTI_IPToString(&inst->nts_address.ip_addr); + + f = UTI_OpenFile(dump_dir, filename, ".nts", 'r', 0); + if (!f) + return; + + /* Don't load this file again */ + if (!UTI_RemoveFile(dump_dir, filename, ".nts")) + ; + + if (inst->siv) + SIV_DestroyInstance(inst->siv); + inst->siv = NULL; + + if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 || + !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 || + strcmp(words[0], inst->name) != 0 || + !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 || + sscanf(words[0], "%lf", &context_time) != 1 || + !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 || + !UTI_StringToIP(words[0], &ntp_addr.ip_addr) || sscanf(words[1], "%d", &port) != 1 || + !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 4 || + sscanf(words[0], "%u", &context_id) != 1 || sscanf(words[1], "%d", &algorithm) != 1) + goto error; + + inst->context.algorithm = algorithm; + inst->context.s2c.length = UTI_HexToBytes(words[2], inst->context.s2c.key, + sizeof (inst->context.s2c.key)); + inst->context.c2s.length = UTI_HexToBytes(words[3], inst->context.c2s.key, + sizeof (inst->context.c2s.key)); + + if (inst->context.s2c.length != SIV_GetKeyLength(algorithm) || + inst->context.c2s.length != inst->context.s2c.length) + goto error; + + for (i = 0; i < NTS_MAX_COOKIES && fgets(line, sizeof (line), f); i++) { + if (UTI_SplitString(line, words, MAX_WORDS) != 1) + goto error; + + inst->cookies[i].length = UTI_HexToBytes(words[0], inst->cookies[i].cookie, + sizeof (inst->cookies[i].cookie)); + if (inst->cookies[i].length == 0) + goto error; + } + + inst->num_cookies = i; + + ntp_addr.port = port; + if (!set_ntp_address(inst, &ntp_addr)) + goto error; + + SCH_GetLastEventTime(&now, NULL, NULL); + context_time -= UTI_TimespecToDouble(&now); + if (context_time > 0) + context_time = 0; + inst->last_nke_success = context_time + SCH_GetLastEventMonoTime(); + inst->context_id = context_id; + + fclose(f); + + DEBUG_LOG("Loaded %d cookies for %s", i, filename); + return; + +error: + DEBUG_LOG("Could not %s cookies for %s", "load", filename); + fclose(f); + + memset(&inst->context, 0, sizeof (inst->context)); + inst->num_cookies = 0; +} + +/* ================================================== */ + +void +NNC_DumpData(NNC_Instance inst) +{ + save_cookies(inst); +} + +/* ================================================== */ + +void +NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report) +{ + report->key_id = inst->context_id; + report->key_type = inst->context.algorithm; + report->key_length = 8 * inst->context.s2c.length; + report->ke_attempts = inst->nke_attempts; + if (report->key_length > 0) + report->last_ke_ago = SCH_GetLastEventMonoTime() - inst->last_nke_success; + else + report->last_ke_ago = -1; + report->cookies = inst->num_cookies; + report->cookie_length = inst->num_cookies > 0 ? inst->cookies[inst->cookie_index].length : 0; + report->nak = inst->nak_response; +} diff -Nru chrony-3.5/nts_ntp_client.h chrony-4.1/nts_ntp_client.h --- chrony-3.5/nts_ntp_client.h 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/nts_ntp_client.h 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,51 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + Header file for client NTS-NTP authentication + */ + +#ifndef GOT_NTS_NTP_CLIENT_H +#define GOT_NTS_NTP_CLIENT_H + +#include "addressing.h" +#include "ntp.h" +#include "reports.h" + +typedef struct NNC_Instance_Record *NNC_Instance; + +extern NNC_Instance NNC_CreateInstance(IPSockAddr *nts_address, const char *name, + uint32_t cert_set, uint16_t ntp_port); +extern void NNC_DestroyInstance(NNC_Instance inst); +extern int NNC_PrepareForAuth(NNC_Instance inst); +extern int NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet, + NTP_PacketInfo *info); +extern int NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet, + NTP_PacketInfo *info); + +extern void NNC_ChangeAddress(NNC_Instance inst, IPAddr *address); + +extern void NNC_DumpData(NNC_Instance inst); + +extern void NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report); + +#endif diff -Nru chrony-3.5/nts_ntp.h chrony-4.1/nts_ntp.h --- chrony-3.5/nts_ntp.h 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/nts_ntp.h 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,41 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + Header file for the NTS-NTP protocol + */ + +#ifndef GOT_NTS_NTP_H +#define GOT_NTS_NTP_H + +#define NTP_EF_NTS_UNIQUE_IDENTIFIER 0x0104 +#define NTP_EF_NTS_COOKIE 0x0204 +#define NTP_EF_NTS_COOKIE_PLACEHOLDER 0x0304 +#define NTP_EF_NTS_AUTH_AND_EEF 0x0404 + +#define NTP_KOD_NTS_NAK 0x4e54534e + +#define NTS_MIN_UNIQ_ID_LENGTH 32 +#define NTS_MIN_UNPADDED_NONCE_LENGTH 16 +#define NTS_MAX_COOKIES 8 + +#endif diff -Nru chrony-3.5/nts_ntp_server.c chrony-4.1/nts_ntp_server.c --- chrony-3.5/nts_ntp_server.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/nts_ntp_server.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,283 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + Server NTS-NTP authentication + */ + +#include "config.h" + +#include "sysincl.h" + +#include "nts_ntp_server.h" + +#include "conf.h" +#include "logging.h" +#include "memory.h" +#include "ntp.h" +#include "ntp_ext.h" +#include "nts_ke_server.h" +#include "nts_ntp.h" +#include "nts_ntp_auth.h" +#include "siv.h" +#include "util.h" + +#define SERVER_SIV AEAD_AES_SIV_CMAC_256 + +struct NtsServer { + SIV_Instance siv; + unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH]; + NKE_Cookie cookies[NTS_MAX_COOKIES]; + int num_cookies; + NTP_int64 req_tx; +}; + +/* The server instance handling all requests */ +struct NtsServer *server; + +/* ================================================== */ + +void +NNS_Initialise(void) +{ + const char **certs, **keys; + + /* Create an NTS-NTP server instance only if NTS-KE server is enabled */ + if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0) { + server = NULL; + return; + } + + server = Malloc(sizeof (struct NtsServer)); + server->siv = SIV_CreateInstance(SERVER_SIV); + if (!server->siv) + LOG_FATAL("Could not initialise SIV cipher"); +} + +/* ================================================== */ + +void +NNS_Finalise(void) +{ + if (!server) + return; + + SIV_DestroyInstance(server->siv); + Free(server); + server = NULL; +} + +/* ================================================== */ + +int +NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod) +{ + int ef_type, ef_body_length, ef_length, has_uniq_id = 0, has_auth = 0, has_cookie = 0; + int i, plaintext_length, parsed, requested_cookies, cookie_length = -1, auth_start = 0; + unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH]; + NKE_Context context; + NKE_Cookie cookie; + void *ef_body; + + *kod = 0; + + if (!server) + return 0; + + server->num_cookies = 0; + server->req_tx = packet->transmit_ts; + + if (info->ext_fields == 0 || info->mode != MODE_CLIENT) + return 0; + + requested_cookies = 0; + + for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) { + if (!NEF_ParseField(packet, info->length, parsed, + &ef_length, &ef_type, &ef_body, &ef_body_length)) + /* This is not expected as the packet already passed NAU_ParsePacket() */ + return 0; + + switch (ef_type) { + case NTP_EF_NTS_UNIQUE_IDENTIFIER: + has_uniq_id = 1; + break; + case NTP_EF_NTS_COOKIE: + if (has_cookie || ef_body_length > sizeof (cookie.cookie)) { + DEBUG_LOG("Unexpected cookie/length"); + return 0; + } + cookie.length = ef_body_length; + memcpy(cookie.cookie, ef_body, ef_body_length); + has_cookie = 1; + /* Fall through */ + case NTP_EF_NTS_COOKIE_PLACEHOLDER: + requested_cookies++; + + if (cookie_length >= 0 && cookie_length != ef_body_length) { + DEBUG_LOG("Invalid cookie/placeholder length"); + return 0; + } + cookie_length = ef_body_length; + break; + case NTP_EF_NTS_AUTH_AND_EEF: + if (parsed + ef_length != info->length) { + DEBUG_LOG("Auth not last EF"); + return 0; + } + + auth_start = parsed; + has_auth = 1; + break; + default: + break; + } + } + + if (!has_uniq_id || !has_cookie || !has_auth) { + DEBUG_LOG("Missing an NTS EF"); + return 0; + } + + if (!NKS_DecodeCookie(&cookie, &context)) { + *kod = NTP_KOD_NTS_NAK; + return 0; + } + + if (context.algorithm != SERVER_SIV) { + DEBUG_LOG("Unexpected SIV"); + return 0; + } + + if (!SIV_SetKey(server->siv, context.c2s.key, context.c2s.length)) { + DEBUG_LOG("Could not set C2S key"); + return 0; + } + + if (!NNA_DecryptAuthEF(packet, info, server->siv, auth_start, + plaintext, sizeof (plaintext), &plaintext_length)) { + *kod = NTP_KOD_NTS_NAK; + return 0; + } + + for (parsed = 0; parsed < plaintext_length; parsed += ef_length) { + if (!NEF_ParseSingleField(plaintext, plaintext_length, parsed, + &ef_length, &ef_type, &ef_body, &ef_body_length)) { + DEBUG_LOG("Could not parse encrypted EF"); + return 0; + } + + switch (ef_type) { + case NTP_EF_NTS_COOKIE_PLACEHOLDER: + if (cookie_length != ef_body_length) { + DEBUG_LOG("Invalid cookie/placeholder length"); + return 0; + } + requested_cookies++; + break; + default: + break; + } + } + + if (!SIV_SetKey(server->siv, context.s2c.key, context.s2c.length)) { + DEBUG_LOG("Could not set S2C key"); + return 0; + } + + /* Prepare data for NNS_GenerateResponseAuth() to minimise the time spent + there (when the TX timestamp is already set) */ + + UTI_GetRandomBytes(server->nonce, sizeof (server->nonce)); + + assert(sizeof (server->cookies) / sizeof (server->cookies[0]) == NTS_MAX_COOKIES); + for (i = 0; i < NTS_MAX_COOKIES && i < requested_cookies; i++) + if (!NKS_GenerateCookie(&context, &server->cookies[i])) + return 0; + + server->num_cookies = i; + + return 1; +} + +/* ================================================== */ + +int +NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info, + NTP_Packet *response, NTP_PacketInfo *res_info, + uint32_t kod) +{ + int i, ef_type, ef_body_length, ef_length, parsed; + void *ef_body; + unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH]; + int plaintext_length; + + if (!server || req_info->mode != MODE_CLIENT || res_info->mode != MODE_SERVER) + return 0; + + /* Make sure this is a response to the request from the last call + of NNS_CheckRequestAuth() */ + if (UTI_CompareNtp64(&server->req_tx, &request->transmit_ts) != 0) + assert(0); + + for (parsed = NTP_HEADER_LENGTH; parsed < req_info->length; parsed += ef_length) { + if (!NEF_ParseField(request, req_info->length, parsed, + &ef_length, &ef_type, &ef_body, &ef_body_length)) + /* This is not expected as the packet already passed NAU_ParsePacket() */ + return 0; + + switch (ef_type) { + case NTP_EF_NTS_UNIQUE_IDENTIFIER: + /* Copy the ID from the request */ + if (!NEF_AddField(response, res_info, ef_type, ef_body, ef_body_length)) + return 0; + default: + break; + } + } + + /* NTS NAK response does not have any other fields */ + if (kod) + return 1; + + for (i = 0, plaintext_length = 0; i < server->num_cookies; i++) { + if (!NEF_SetField(plaintext, sizeof (plaintext), plaintext_length, + NTP_EF_NTS_COOKIE, server->cookies[i].cookie, + server->cookies[i].length, &ef_length)) + return 0; + + plaintext_length += ef_length; + assert(plaintext_length <= sizeof (plaintext)); + } + + server->num_cookies = 0; + + /* Generate an authenticator field which will make the length + of the response equal to the length of the request */ + if (!NNA_GenerateAuthEF(response, res_info, server->siv, + server->nonce, sizeof (server->nonce), + plaintext, plaintext_length, + req_info->length - res_info->length)) + return 0; + + return 1; +} diff -Nru chrony-3.5/nts_ntp_server.h chrony-4.1/nts_ntp_server.h --- chrony-3.5/nts_ntp_server.h 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/nts_ntp_server.h 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,40 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + Header file for server NTS-NTP authentication + */ + +#ifndef GOT_NTS_NTP_SERVER_H +#define GOT_NTS_NTP_SERVER_H + +#include "ntp.h" + +extern void NNS_Initialise(void); +extern void NNS_Finalise(void); + +extern int NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod); +extern int NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info, + NTP_Packet *response, NTP_PacketInfo *res_info, + uint32_t kod); + +#endif diff -Nru chrony-3.5/pktlength.c chrony-4.1/pktlength.c --- chrony-3.5/pktlength.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/pktlength.c 2021-05-12 11:06:15.000000000 +0000 @@ -87,7 +87,7 @@ REQ_LENGTH_ENTRY(del_source, null), /* DEL_SOURCE */ REQ_LENGTH_ENTRY(null, null), /* WRITERTC */ REQ_LENGTH_ENTRY(dfreq, null), /* DFREQ */ - REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET */ + { 0, 0 }, /* DOFFSET - not supported */ REQ_LENGTH_ENTRY(null, tracking), /* TRACKING */ REQ_LENGTH_ENTRY(sourcestats, sourcestats), /* SOURCESTATS */ REQ_LENGTH_ENTRY(null, rtc), /* RTCREPORT */ @@ -110,16 +110,25 @@ REQ_LENGTH_ENTRY(smoothtime, null), /* SMOOTHTIME */ REQ_LENGTH_ENTRY(null, null), /* REFRESH */ REQ_LENGTH_ENTRY(null, server_stats), /* SERVER_STATS */ - REQ_LENGTH_ENTRY(client_accesses_by_index, - client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */ + { 0, 0 }, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */ REQ_LENGTH_ENTRY(local, null), /* LOCAL2 */ REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */ { 0, 0 }, /* ADD_SERVER2 */ { 0, 0 }, /* ADD_PEER2 */ - REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER3 */ - REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER3 */ + { 0, 0 }, /* ADD_SERVER3 */ + { 0, 0 }, /* ADD_PEER3 */ REQ_LENGTH_ENTRY(null, null), /* SHUTDOWN */ REQ_LENGTH_ENTRY(null, null), /* ONOFFLINE */ + REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SOURCE */ + REQ_LENGTH_ENTRY(ntp_source_name, + ntp_source_name), /* NTP_SOURCE_NAME */ + REQ_LENGTH_ENTRY(null, null), /* RESET_SOURCES */ + REQ_LENGTH_ENTRY(auth_data, auth_data), /* AUTH_DATA */ + REQ_LENGTH_ENTRY(client_accesses_by_index, + client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */ + REQ_LENGTH_ENTRY(select_data, select_data), /* SELECT_DATA */ + REQ_LENGTH_ENTRY(null, null), /* RELOAD_SOURCES */ + REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET2 */ }; static const uint16_t reply_lengths[] = { @@ -137,11 +146,16 @@ 0, /* MANUAL_LIST - not supported */ RPY_LENGTH_ENTRY(activity), /* ACTIVITY */ RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */ - RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */ - RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */ + 0, /* SERVER_STATS - not supported */ + 0, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */ RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */ RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */ RPY_LENGTH_ENTRY(manual_list), /* MANUAL_LIST2 */ + RPY_LENGTH_ENTRY(ntp_source_name), /* NTP_SOURCE_NAME */ + RPY_LENGTH_ENTRY(auth_data), /* AUTH_DATA */ + RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */ + RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS2 */ + RPY_LENGTH_ENTRY(select_data), /* SELECT_DATA */ }; /* ================================================== */ diff -Nru chrony-3.5/privops.c chrony-4.1/privops.c --- chrony-3.5/privops.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/privops.c 2021-05-12 11:06:15.000000000 +0000 @@ -33,6 +33,7 @@ #include "nameserv.h" #include "logging.h" #include "privops.h" +#include "socket.h" #include "util.h" #define OP_ADJUSTTIME 1024 @@ -158,7 +159,7 @@ static int send_response(int fd, const PrvResponse *res) { - if (send(fd, res, sizeof (*res), 0) != sizeof (*res)) + if (SCK_Send(fd, res, sizeof (*res), 0) != sizeof (*res)) return 0; return 1; @@ -170,37 +171,23 @@ static int receive_from_daemon(int fd, PrvRequest *req) { - struct msghdr msg; - struct cmsghdr *cmsg; - struct iovec iov; - char cmsgbuf[256]; - - iov.iov_base = req; - iov.iov_len = sizeof (*req); - - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = (void *)cmsgbuf; - msg.msg_controllen = sizeof (cmsgbuf); - msg.msg_flags = MSG_WAITALL; + SCK_Message *message; - /* read the data */ - if (recvmsg(fd, &msg, 0) != sizeof (*req)) + message = SCK_ReceiveMessage(fd, SCK_FLAG_MSG_DESCRIPTOR); + if (!message || message->length != sizeof (*req)) return 0; + memcpy(req, message->data, sizeof (*req)); + if (req->op == OP_BINDSOCKET) { - /* extract transferred descriptor */ - req->data.bind_socket.sock = -1; - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) - memcpy(&req->data.bind_socket.sock, CMSG_DATA(cmsg), sizeof (int)); - } + req->data.bind_socket.sock = message->descriptor; /* return error if valid descriptor not found */ if (req->data.bind_socket.sock < 0) return 0; + } else if (message->descriptor >= 0) { + SCK_CloseSocket(message->descriptor); + return 0; } return 1; @@ -257,8 +244,7 @@ static void do_bind_socket(ReqBindSocket *req, PrvResponse *res) { - unsigned short port; - IPAddr ip; + IPSockAddr ip_saddr; int sock_fd; struct sockaddr *sa; socklen_t sa_len; @@ -267,10 +253,11 @@ sa_len = req->sa_len; sock_fd = req->sock; - UTI_SockaddrToIPAndPort(sa, &ip, &port); - if (port && port != CNF_GetNTPPort() && port != CNF_GetAcquisitionPort()) { - close(sock_fd); - res_fatal(res, "Invalid port %d", port); + SCK_SockaddrToIPSockAddr(sa, sa_len, &ip_saddr); + if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() && + ip_saddr.port != CNF_GetAcquisitionPort()) { + SCK_CloseSocket(sock_fd); + res_fatal(res, "Invalid port %d", ip_saddr.port); return; } @@ -279,7 +266,7 @@ res->res_errno = errno; /* sock is still open on daemon side, but we're done with it in the helper */ - close(sock_fd); + SCK_CloseSocket(sock_fd); } #endif @@ -373,7 +360,7 @@ send_response(fd, &res); } - close(fd); + SCK_CloseSocket(fd); exit(0); } @@ -386,7 +373,7 @@ { int resp_len; - resp_len = recv(helper_fd, res, sizeof (*res), 0); + resp_len = SCK_Receive(helper_fd, res, sizeof (*res), 0); if (resp_len < 0) LOG_FATAL("Could not read from helper : %s", strerror(errno)); if (resp_len != sizeof (*res)) @@ -409,41 +396,22 @@ static void send_request(PrvRequest *req) { - struct msghdr msg; - struct iovec iov; - char cmsgbuf[256]; - - iov.iov_base = req; - iov.iov_len = sizeof (*req); - - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = 0; - - if (req->op == OP_BINDSOCKET) { - /* send file descriptor as a control message */ - struct cmsghdr *cmsg; - int *ptr_send_fd; + SCK_Message message; + int flags; - msg.msg_control = cmsgbuf; - msg.msg_controllen = CMSG_SPACE(sizeof (int)); + SCK_InitMessage(&message, SCK_ADDR_UNSPEC); - cmsg = CMSG_FIRSTHDR(&msg); - memset(cmsg, 0, CMSG_SPACE(sizeof (int))); + message.data = req; + message.length = sizeof (*req); + flags = 0; - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof (int)); - - ptr_send_fd = (int *)CMSG_DATA(cmsg); - *ptr_send_fd = req->data.bind_socket.sock; + if (req->op == OP_BINDSOCKET) { + /* send file descriptor as a control message */ + message.descriptor = req->data.bind_socket.sock; + flags |= SCK_FLAG_MSG_DESCRIPTOR; } - if (sendmsg(helper_fd, &msg, 0) < 0) { + if (!SCK_SendMessage(helper_fd, &message, flags)) { /* don't try to send another request from exit() */ helper_fd = -1; LOG_FATAL("Could not send to helper : %s", strerror(errno)); @@ -573,13 +541,13 @@ int PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len) { + IPSockAddr ip_saddr; PrvRequest req; PrvResponse res; - IPAddr ip; - unsigned short port; - UTI_SockaddrToIPAndPort(address, &ip, &port); - if (port && port != CNF_GetNTPPort() && port != CNF_GetAcquisitionPort()) + SCK_SockaddrToIPSockAddr(address, address_len, &ip_saddr); + if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() && + ip_saddr.port != CNF_GetAcquisitionPort()) assert(0); if (!have_helper()) @@ -589,6 +557,7 @@ req.op = OP_BINDSOCKET; req.data.bind_socket.sock = sock; req.data.bind_socket.sa_len = address_len; + assert(address_len <= sizeof (req.data.bind_socket.sa)); memcpy(&req.data.bind_socket.sa.u, address, address_len); submit_request(&req, &res); @@ -616,7 +585,6 @@ req.op = OP_NAME2IPADDRESS; if (snprintf(req.data.name_to_ipaddress.name, sizeof (req.data.name_to_ipaddress.name), "%s", name) >= sizeof (req.data.name_to_ipaddress.name)) { - DEBUG_LOG("Name too long"); return DNS_Failure; } @@ -670,20 +638,14 @@ PRV_StartHelper(void) { pid_t pid; - int fd, sock_pair[2]; + int fd, sock_fd1, sock_fd2; if (have_helper()) LOG_FATAL("Helper already running"); - if ( -#ifdef SOCK_SEQPACKET - socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock_pair) && -#endif - socketpair(AF_UNIX, SOCK_DGRAM, 0, sock_pair)) - LOG_FATAL("socketpair() failed : %s", strerror(errno)); - - UTI_FdSetCloexec(sock_pair[0]); - UTI_FdSetCloexec(sock_pair[1]); + sock_fd1 = SCK_OpenUnixSocketPair(SCK_FLAG_BLOCK, &sock_fd2); + if (sock_fd1 < 0) + LOG_FATAL("Could not open socket pair"); pid = fork(); if (pid < 0) @@ -691,23 +653,24 @@ if (pid == 0) { /* child process */ - close(sock_pair[0]); + SCK_CloseSocket(sock_fd1); - /* close other descriptors inherited from the parent process */ - for (fd = 0; fd < 1024; fd++) { - if (fd != sock_pair[1]) + /* close other descriptors inherited from the parent process, except + stdin, stdout, and stderr */ + for (fd = STDERR_FILENO + 1; fd < 1024; fd++) { + if (fd != sock_fd2) close(fd); } /* ignore signals, the process will exit on OP_QUIT request */ UTI_SetQuitSignalsHandler(SIG_IGN, 1); - helper_main(sock_pair[1]); + helper_main(sock_fd2); } else { /* parent process */ - close(sock_pair[1]); - helper_fd = sock_pair[0]; + SCK_CloseSocket(sock_fd2); + helper_fd = sock_fd1; helper_pid = pid; /* stop the helper even when not exiting cleanly from the main function */ diff -Nru chrony-3.5/README chrony-4.1/README --- chrony-3.5/README 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/README 2021-05-12 11:06:15.000000000 +0000 @@ -29,9 +29,7 @@ The software is known to work on Linux, FreeBSD, NetBSD, macOS and Solaris. Closely related systems may work too. Any other system will -likely require a porting exercise. You would need to start from one -of the existing system-specific drivers and look into the quirks of -certain system calls and the kernel on your target system. +likely require a porting exercise. How do I set it up? =================== @@ -55,24 +53,20 @@ ================================= There is a low volume mailing list where new versions and other -important news relating to chrony is announced. You can join this list +important news relating to chrony are announced. You can join this list by sending mail with the subject "subscribe" to chrony-announce-request@chrony.tuxfamily.org -These messages will be copied to chrony-users (see below). - How can I get support for chrony? -and where can I discuss new features, possible bugs etc? -======================================================== +================================= -There are 3 mailing lists relating to chrony. chrony-announce was -mentioned above. chrony-users is a users' discussion list, e.g. for -general questions and answers about using chrony. chrony-dev is a more -technical list, e.g. for discussing how new features should be -implemented, exchange of information between developers etc. To -subscribe to either of these lists, send a message with the subject -"subscribe" to +There are two other mailing lists relating to chrony. chrony-users is a +discussion list for users, e.g. for questions about chrony configuration +and bug reports. chrony-dev is a more technical list for developers, +e.g. for submitting patches and discussing how new features should be +implemented. To subscribe to either of these lists, send a message with +the subject "subscribe" to chrony-users-request@chrony.tuxfamily.org or @@ -80,12 +74,6 @@ as applicable. -When you are reporting a bug, please send us all the information you can. -Unfortunately, chrony has proven to be one of those programs where it is very -difficult to reproduce bugs in a different environment. So we may have to -interact with you quite a lot to obtain enough extra logging and tracing to -pin-point the problem in some cases. Please be patient and plan for this! - License ======= @@ -100,26 +88,30 @@ Acknowledgements ================ -In writing the chronyd program, extensive use has been made of RFC 1305 -and RFC 5905, written by David Mills. The source code of the NTP reference -implementation has been used to check the details of the protocol. +In writing the chronyd program, extensive use has been made of the NTPv3 (RFC +1305) and NTPv4 (RFC 5905) specification. The source code of the xntpd/ntpd +implementation written by Dennis Fergusson, Lars Mathiesen, David Mills, and +others has been used to check the details of the protocol. The following people have provided patches and other major contributions -to the program : +to chrony: Lonnie Abelbeck Benny Lyne Amorsen Andrew Bishop Vincent Blut Stephan I. Boettcher +David Bohman Goswin Brederlow Leigh Brown Erik Bryer Jonathan Cameron Bryan Christianson Juliusz Chroboczek +Kamil Dudka Christian Ehrhardt Paul Elliott +Robert Fairley Stefan R. Filipek Mike Fleetwood Alexander Gretencord @@ -133,6 +125,7 @@ Håkan Johansson Jim Knoble Antti Jrvinen +Uwe Kleine-König Eric Lammerts Stefan Lucke Victor Lum @@ -146,6 +139,8 @@ Chris Perl Gautier PHILIPPON Andreas Piesk +Baruch Siach +Foster Snowhill Andreas Steinmetz NAKAMURA Takumi Timo Teras @@ -157,6 +152,7 @@ Joachim Wiedorn Ralf Wildenhues Ulrich Windl +Michael Witten Doug Woodward Thomas Zajic diff -Nru chrony-3.5/refclock.c chrony-4.1/refclock.c --- chrony-3.5/refclock.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/refclock.c 2021-05-12 11:06:15.000000000 +0000 @@ -40,6 +40,9 @@ #include "samplefilt.h" #include "sched.h" +/* Maximum offset of locked reference as a fraction of the PPS interval */ +#define PPS_LOCK_LIMIT 0.4 + /* list of refclock drivers */ extern RefclockDriver RCL_SHM_driver; extern RefclockDriver RCL_SOCK_driver; @@ -181,7 +184,7 @@ LOG_FATAL("refclock tai option requires leapsectz"); inst->data = NULL; - inst->driver_parameter = params->driver_parameter; + inst->driver_parameter = Strdup(params->driver_parameter); inst->driver_parameter_length = 0; inst->driver_poll = params->driver_poll; inst->poll = params->poll; @@ -253,22 +256,21 @@ inst->filter = SPF_CreateInstance(MIN(params->filter_length, 4), params->filter_length, params->max_dispersion, 0.6); - inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_options, NULL, - params->min_samples, params->max_samples, 0.0, 0.0); + inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, 0, params->sel_options, + NULL, params->min_samples, params->max_samples, + 0.0, 0.0); DEBUG_LOG("refclock %s refid=%s poll=%d dpoll=%d filter=%d", params->driver_name, UTI_RefidToString(inst->ref_id), inst->poll, inst->driver_poll, params->filter_length); - Free(params->driver_name); - return 1; } void RCL_StartRefclocks(void) { - unsigned int i, j, n; + unsigned int i, j, n, lock_index; n = ARR_GetSize(refclocks); @@ -278,13 +280,31 @@ SRC_SetActive(inst->source); inst->timeout_id = SCH_AddTimeoutByDelay(0.0, poll_timeout, (void *)inst); - if (inst->lock_ref) { - /* Replace lock refid with index to refclocks */ - for (j = 0; j < n && get_refclock(j)->ref_id != inst->lock_ref; j++) - ; - inst->lock_ref = j < n ? j : -1; - } else - inst->lock_ref = -1; + /* Replace lock refid with the refclock's index, or -1 if not valid */ + + lock_index = -1; + + if (inst->lock_ref != 0) { + for (j = 0; j < n; j++) { + RCL_Instance inst2 = get_refclock(j); + + if (inst->lock_ref != inst2->ref_id) + continue; + + if (inst->driver->poll && inst2->driver->poll && + (double)inst->max_lock_age / inst->pps_rate < UTI_Log2ToDouble(inst2->driver_poll)) + LOG(LOGS_WARN, "%s maxlockage too small for %s", + UTI_RefidToString(inst->ref_id), UTI_RefidToString(inst2->ref_id)); + + lock_index = j; + break; + } + + if (lock_index == -1 || lock_index == i) + LOG(LOGS_WARN, "Invalid lock refid %s", UTI_RefidToString(inst->lock_ref)); + } + + inst->lock_ref = lock_index; } } @@ -415,13 +435,6 @@ sample.root_delay = instance->delay; sample.peer_dispersion = dispersion; sample.root_dispersion = dispersion; - sample.leap = instance->leap_status; - - /* Handle special case when PPS is used with the local reference */ - if (instance->pps_active && instance->lock_ref == -1) - sample.stratum = pps_stratum(instance, &sample.time); - else - sample.stratum = instance->stratum; return SPF_AccumulateSample(instance->filter, &sample); } @@ -567,7 +580,7 @@ offset += shift; if (fabs(ref_sample.offset - offset) + - ref_sample.root_dispersion + dispersion >= 0.2 / rate) { + ref_sample.root_dispersion + dispersion > PPS_LOCK_LIMIT / rate) { DEBUG_LOG("refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f", ref_sample.offset - offset, ref_sample.root_dispersion, dispersion); return 0; @@ -687,7 +700,7 @@ poll_timeout(void *arg) { NTP_Sample sample; - int poll; + int poll, stratum; RCL_Instance inst = (RCL_Instance)arg; @@ -703,7 +716,14 @@ inst->driver_polled = 0; if (SPF_GetFilteredSample(inst->filter, &sample)) { + /* Handle special case when PPS is used with the local reference */ + if (inst->pps_active && inst->lock_ref == -1) + stratum = pps_stratum(inst, &sample.time); + else + stratum = inst->stratum; + SRC_UpdateReachability(inst->source, 1); + SRC_UpdateStatus(inst->source, stratum, inst->leap_status); SRC_AccumulateSample(inst->source, &sample); SRC_SelectSource(inst->source); diff -Nru chrony-3.5/refclock_phc.c chrony-4.1/refclock_phc.c --- chrony-3.5/refclock_phc.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/refclock_phc.c 2021-05-12 11:06:15.000000000 +0000 @@ -66,10 +66,8 @@ path = RCL_GetDriverParameter(instance); phc_fd = SYS_Linux_OpenPHC(path, 0); - if (phc_fd < 0) { + if (phc_fd < 0) LOG_FATAL("Could not open PHC"); - return 0; - } phc = MallocNew(struct phc_instance); phc->fd = phc_fd; diff -Nru chrony-3.5/refclock_pps.c chrony-4.1/refclock_pps.c --- chrony-3.5/refclock_pps.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/refclock_pps.c 2021-05-12 11:06:15.000000000 +0000 @@ -61,49 +61,36 @@ edge_clear = RCL_GetDriverOption(instance, "clear") ? 1 : 0; fd = open(path, O_RDWR); - if (fd < 0) { + if (fd < 0) LOG_FATAL("Could not open %s : %s", path, strerror(errno)); - return 0; - } UTI_FdSetCloexec(fd); - if (time_pps_create(fd, &handle) < 0) { + if (time_pps_create(fd, &handle) < 0) LOG_FATAL("time_pps_create() failed on %s : %s", path, strerror(errno)); - return 0; - } - if (time_pps_getcap(handle, &mode) < 0) { + if (time_pps_getcap(handle, &mode) < 0) LOG_FATAL("time_pps_getcap() failed on %s : %s", path, strerror(errno)); - return 0; - } - if (time_pps_getparams(handle, ¶ms) < 0) { + if (time_pps_getparams(handle, ¶ms) < 0) LOG_FATAL("time_pps_getparams() failed on %s : %s", path, strerror(errno)); - return 0; - } if (!edge_clear) { - if (!(mode & PPS_CAPTUREASSERT)) { + if (!(mode & PPS_CAPTUREASSERT)) LOG_FATAL("CAPTUREASSERT not supported on %s", path); - return 0; - } + params.mode |= PPS_CAPTUREASSERT; params.mode &= ~PPS_CAPTURECLEAR; } else { - if (!(mode & PPS_CAPTURECLEAR)) { + if (!(mode & PPS_CAPTURECLEAR)) LOG_FATAL("CAPTURECLEAR not supported on %s", path); - return 0; - } + params.mode |= PPS_CAPTURECLEAR; params.mode &= ~PPS_CAPTUREASSERT; } - if (time_pps_setparams(handle, ¶ms) < 0) { + if (time_pps_setparams(handle, ¶ms) < 0) LOG_FATAL("time_pps_setparams() failed on %s : %s", path, strerror(errno)); - return 0; - } - pps = MallocNew(struct pps_instance); pps->handle = handle; diff -Nru chrony-3.5/refclock_sock.c chrony-4.1/refclock_sock.c --- chrony-3.5/refclock_sock.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/refclock_sock.c 2021-05-12 11:06:15.000000000 +0000 @@ -33,6 +33,7 @@ #include "logging.h" #include "util.h" #include "sched.h" +#include "socket.h" #define SOCK_MAGIC 0x534f434b @@ -97,7 +98,6 @@ static int sock_initialise(RCL_Instance instance) { - struct sockaddr_un s; int sockfd; char *path; @@ -105,25 +105,9 @@ path = RCL_GetDriverParameter(instance); - s.sun_family = AF_UNIX; - if (snprintf(s.sun_path, sizeof (s.sun_path), "%s", path) >= sizeof (s.sun_path)) { - LOG_FATAL("Path %s too long", path); - return 0; - } - - sockfd = socket(AF_UNIX, SOCK_DGRAM, 0); - if (sockfd < 0) { - LOG_FATAL("socket() failed"); - return 0; - } - - UTI_FdSetCloexec(sockfd); - - unlink(path); - if (bind(sockfd, (struct sockaddr *)&s, sizeof (s)) < 0) { - LOG_FATAL("bind(%s) failed : %s", path, strerror(errno)); - return 0; - } + sockfd = SCK_OpenUnixDatagramSocket(NULL, path, 0); + if (sockfd < 0) + LOG_FATAL("Could not open socket %s", path); RCL_SetDriverData(instance, (void *)(long)sockfd); SCH_AddFileHandler(sockfd, SCH_FILE_INPUT, read_sample, instance); @@ -136,7 +120,8 @@ sockfd = (long)RCL_GetDriverData(instance); SCH_RemoveFileHandler(sockfd); - close(sockfd); + SCK_RemoveSocket(sockfd); + SCK_CloseSocket(sockfd); } RefclockDriver RCL_SOCK_driver = { diff -Nru chrony-3.5/reference.c chrony-4.1/reference.c --- chrony-3.5/reference.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/reference.c 2021-05-12 11:06:15.000000000 +0000 @@ -3,7 +3,7 @@ ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 - * Copyright (C) Miroslav Lichvar 2009-2018 + * Copyright (C) Miroslav Lichvar 2009-2018, 2020 * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -42,11 +42,18 @@ /* The minimum allowed skew */ #define MIN_SKEW 1.0e-12 +/* The update interval of the reference in the local reference mode */ +#define LOCAL_REF_UPDATE_INTERVAL 64.0 + +/* Interval between updates of the drift file */ +#define MAX_DRIFTFILE_AGE 3600.0 + static int are_we_synchronised; static int enable_local_stratum; static int local_stratum; static int local_orphan; static double local_distance; +static struct timespec local_ref_time; static NTP_Leap our_leap_status; static int our_leap_sec; static int our_tai_offset; @@ -58,6 +65,8 @@ static double our_residual_freq; static double our_root_delay; static double our_root_dispersion; +static double our_offset_sd; +static double our_frequency_sd; static double max_update_skew; @@ -103,6 +112,9 @@ /* Leap second handling mode */ static REF_LeapMode leap_mode; +/* Time of UTC midnight of the upcoming or previous leap second */ +static time_t leap_when; + /* Flag indicating the clock was recently corrected for leap second and it may not have correct time yet (missing 23:59:60 in the UTC time scale) */ static int leap_in_progress; @@ -134,8 +146,8 @@ static int next_fb_drift; static SCH_TimeoutID fb_drift_timeout_id; -/* Timestamp of last reference update */ -static struct timespec last_ref_update; +/* Monotonic timestamp of the last reference update */ +static double last_ref_update; static double last_ref_update_interval; /* ================================================== */ @@ -160,9 +172,8 @@ UTI_AdjustTimespec(&our_ref_time, cooked, &our_ref_time, &delta, dfreq, doffset); if (change_type == LCL_ChangeUnknownStep) { - UTI_ZeroTimespec(&last_ref_update); - } else if (last_ref_update.tv_sec) { - UTI_AdjustTimespec(&last_ref_update, cooked, &last_ref_update, &delta, dfreq, doffset); + last_ref_update = 0.0; + REF_SetUnsynchronised(); } /* When the clock was stepped, check if that doesn't change our leap status @@ -194,12 +205,14 @@ our_frequency_ppm = 0.0; our_skew = 1.0; /* i.e. rather bad */ our_residual_freq = 0.0; + our_frequency_sd = 0.0; + our_offset_sd = 0.0; drift_file_age = 0.0; /* Now see if we can get the drift file opened */ drift_file = CNF_GetDriftFile(); if (drift_file) { - in = fopen(drift_file, "r"); + in = UTI_OpenFile(NULL, drift_file, NULL, 'r', 0); if (in) { if (fscanf(in, "%lf%lf", &file_freq_ppm, &file_skew_ppm) == 2) { /* We have read valid data */ @@ -234,7 +247,9 @@ correction_time_ratio = CNF_GetCorrectionTimeRatio(); enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan, &local_distance); + UTI_ZeroTimespec(&local_ref_time); + leap_when = 0; leap_timeout_id = 0; leap_in_progress = 0; leap_mode = CNF_GetLeapSecMode(); @@ -269,7 +284,7 @@ } UTI_ZeroTimespec(&our_ref_time); - UTI_ZeroTimespec(&last_ref_update); + last_ref_update = 0.0; last_ref_update_interval = 0.0; LCL_AddParameterChangeHandler(handle_slew, NULL); @@ -289,6 +304,8 @@ update_drift_file(LCL_ReadAbsoluteFrequency(), our_skew); } + LCL_RemoveParameterChangeHandler(handle_slew, NULL); + Free(fb_drifts); initialised = 0; @@ -331,61 +348,20 @@ static void update_drift_file(double freq_ppm, double skew) { - struct stat buf; - char *temp_drift_file; FILE *out; - int r1, r2; /* Create a temporary file with a '.tmp' extension. */ - - temp_drift_file = (char*) Malloc(strlen(drift_file)+8); - - if(!temp_drift_file) { + out = UTI_OpenFile(NULL, drift_file, ".tmp", 'w', 0644); + if (!out) return; - } - - strcpy(temp_drift_file,drift_file); - strcat(temp_drift_file,".tmp"); - - out = fopen(temp_drift_file, "w"); - if (!out) { - Free(temp_drift_file); - LOG(LOGS_WARN, "Could not open temporary driftfile %s.tmp for writing", - drift_file); - return; - } /* Write the frequency and skew parameters in ppm */ - r1 = fprintf(out, "%20.6f %20.6f\n", freq_ppm, 1.0e6 * skew); - r2 = fclose(out); - if (r1 < 0 || r2) { - Free(temp_drift_file); - LOG(LOGS_WARN, "Could not write to temporary driftfile %s.tmp", - drift_file); - return; - } - - /* Clone the file attributes from the existing file if there is one. */ - - if (!stat(drift_file,&buf)) { - if (chown(temp_drift_file,buf.st_uid,buf.st_gid) || - chmod(temp_drift_file,buf.st_mode & 0777)) { - LOG(LOGS_WARN, "Could not change ownership or permissions of temporary driftfile %s.tmp", - drift_file); - } - } + fprintf(out, "%20.6f %20.6f\n", freq_ppm, 1.0e6 * skew); + fclose(out); - /* Rename the temporary file to the correct location (see rename(2) for details). */ - - if (rename(temp_drift_file,drift_file)) { - unlink(temp_drift_file); - Free(temp_drift_file); - LOG(LOGS_WARN, "Could not replace old driftfile %s with new one %s.tmp", - drift_file,drift_file); - return; - } - - Free(temp_drift_file); + /* Rename the temporary file to the correct location */ + if (!UTI_RenameTempFile(NULL, drift_file, ".tmp", NULL)) + ; } /* ================================================== */ @@ -451,16 +427,16 @@ /* ================================================== */ static void -schedule_fb_drift(struct timespec *now) +schedule_fb_drift(void) { int i, c, secs; - double unsynchronised; - struct timespec when; + double unsynchronised, now; if (fb_drift_timeout_id) return; /* already scheduled */ - unsynchronised = UTI_DiffTimespecsToDouble(now, &last_ref_update); + now = SCH_GetLastEventMonoTime(); + unsynchronised = now - last_ref_update; for (c = secs = 0, i = fb_drift_min; i <= fb_drift_max; i++) { secs = 1 << i; @@ -482,8 +458,7 @@ if (i <= fb_drift_max) { next_fb_drift = i; - UTI_AddDoubleToTimespec(now, secs - unsynchronised, &when); - fb_drift_timeout_id = SCH_AddTimeout(&when, fb_drift_timeout, NULL); + fb_drift_timeout_id = SCH_AddTimeoutByDelay(secs - unsynchronised, fb_drift_timeout, NULL); DEBUG_LOG("Fallback drift %d scheduled", i); } } @@ -516,8 +491,7 @@ abs_offset = fabs(offset); if (abs_offset > log_change_threshold) { - LOG(LOGS_WARN, "System clock wrong by %.6f seconds, adjustment started", - -offset); + LOG(LOGS_WARN, "System clock wrong by %.6f seconds", -offset); } if (do_mail_change && @@ -583,8 +557,7 @@ return 1; } - offset = fabs(offset); - if (offset > max_offset) { + if (fabs(offset) > max_offset) { LOG(LOGS_WARN, "Adjustment of %.3f seconds exceeds the allowed maximum of %.3f seconds (%s) ", -offset, max_offset, !max_offset_ignore ? "exiting" : "ignored"); @@ -751,10 +724,12 @@ if (!our_leap_sec) return; + leap_when = (now / (24 * 3600) + 1) * (24 * 3600); + /* Insert leap second at 0:00:00 UTC, delete at 23:59:59 UTC. If the clock will be corrected by the system, timeout slightly sooner to be sure it will happen before the system correction. */ - when.tv_sec = (now / (24 * 3600) + 1) * (24 * 3600); + when.tv_sec = leap_when; when.tv_nsec = 0; if (our_leap_sec < 0) when.tv_sec--; @@ -798,7 +773,7 @@ } if ((leap_sec != our_leap_sec || tai_offset != our_tai_offset) - && !REF_IsLeapSecondClose()) { + && !REF_IsLeapSecondClose(NULL, 0.0)) { our_leap_sec = leap_sec; our_tai_offset = tai_offset; @@ -838,6 +813,20 @@ /* ================================================== */ static void +update_sync_status(struct timespec *now) +{ + double elapsed; + + elapsed = fabs(UTI_DiffTimespecsToDouble(now, &our_ref_time)); + + LCL_SetSyncStatus(are_we_synchronised, + our_offset_sd + elapsed * our_frequency_sd, + our_root_delay / 2.0 + get_root_dispersion(now)); +} + +/* ================================================== */ + +static void write_log(struct timespec *now, int combined_sources, double freq, double offset, double offset_sd, double uncorrected_offset, double orig_root_distance) @@ -959,6 +948,18 @@ /* ================================================== */ +static void +fuzz_ref_time(struct timespec *ts) +{ + uint32_t rnd; + + /* Add a random value from interval [-1.0, 0.0] */ + UTI_GetRandomBytes(&rnd, sizeof (rnd)); + UTI_AddDoubleToTimespec(ts, -(double)rnd / (uint32_t)-1, ts); +} + +/* ================================================== */ + void REF_SetReference(int stratum, NTP_Leap leap, int combined_sources, uint32_t ref_id, IPAddr *ref_ip, struct timespec *ref_time, @@ -968,9 +969,8 @@ { double uncorrected_offset, accumulate_offset, step_offset; double residual_frequency, local_abs_frequency; - double elapsed, update_interval, correction_rate, orig_root_distance; + double elapsed, mono_now, update_interval, correction_rate, orig_root_distance; struct timespec now, raw_now; - NTP_int64 ref_fuzz; int manual; assert(initialised); @@ -983,17 +983,16 @@ manual = leap == LEAP_Unsynchronised; + mono_now = SCH_GetLastEventMonoTime(); LCL_ReadRawTime(&raw_now); LCL_GetOffsetCorrection(&raw_now, &uncorrected_offset, NULL); UTI_AddDoubleToTimespec(&raw_now, uncorrected_offset, &now); elapsed = UTI_DiffTimespecsToDouble(&now, ref_time); offset += elapsed * frequency; - offset_sd += elapsed * frequency_sd; - if (last_ref_update.tv_sec) { - update_interval = UTI_DiffTimespecsToDouble(&now, &last_ref_update); - update_interval = MAX(update_interval, 0.0); + if (last_ref_update != 0.0) { + update_interval = mono_now - last_ref_update; } else { update_interval = 0.0; } @@ -1019,7 +1018,9 @@ our_residual_freq = residual_frequency; our_root_delay = root_delay; our_root_dispersion = root_dispersion; - last_ref_update = now; + our_frequency_sd = frequency_sd; + our_offset_sd = offset_sd; + last_ref_update = mono_now; last_ref_update_interval = update_interval; last_offset = offset; @@ -1051,7 +1052,6 @@ /* Adjust the clock */ LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset, correction_rate); - update_leap_status(leap, raw_now.tv_sec, 0); maybe_log_offset(offset, raw_now.tv_sec); if (step_offset != 0.0) { @@ -1059,17 +1059,13 @@ LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", -step_offset); } - LCL_SetSyncStatus(are_we_synchronised, offset_sd, - root_delay / 2.0 + get_root_dispersion(&now)); + update_leap_status(leap, raw_now.tv_sec, 0); + update_sync_status(&now); /* Add a random error of up to one second to the reference time to make it less useful when disclosed to NTP and cmdmon clients for estimating receive timestamps in the interleaved symmetric NTP mode */ - UTI_GetNtp64Fuzz(&ref_fuzz, 0); - UTI_TimespecToNtp64(&our_ref_time, &ref_fuzz, &ref_fuzz); - UTI_Ntp64ToTimespec(&ref_fuzz, &our_ref_time); - if (UTI_CompareTimespecs(&our_ref_time, ref_time) >= 0) - our_ref_time.tv_sec--; + fuzz_ref_time(&our_ref_time); local_abs_frequency = LCL_ReadAbsoluteFrequency(); @@ -1079,7 +1075,7 @@ if (drift_file) { /* Update drift file at most once per hour */ drift_file_age += update_interval; - if (drift_file_age < 0.0 || drift_file_age > 3600.0) { + if (drift_file_age >= MAX_DRIFTFILE_AGE) { update_drift_file(local_abs_frequency, our_skew); drift_file_age = 0.0; } @@ -1088,7 +1084,7 @@ /* Update fallback drifts */ if (fb_drifts && are_we_synchronised) { update_fb_drifts(local_abs_frequency, update_interval); - schedule_fb_drift(&now); + schedule_fb_drift(); } /* Update the moving average of squares of offset, quickly on start */ @@ -1141,7 +1137,7 @@ UTI_AddDoubleToTimespec(&now_raw, uncorrected_offset, &now); if (fb_drifts) { - schedule_fb_drift(&now); + schedule_fb_drift(); } update_leap_status(LEAP_Unsynchronised, 0, 0); @@ -1159,6 +1155,25 @@ /* ================================================== */ void +REF_UpdateLeapStatus(NTP_Leap leap) +{ + struct timespec raw_now, now; + + /* Wait for a full reference update if not already synchronised */ + if (!are_we_synchronised) + return; + + SCH_GetLastEventTime(&now, NULL, &raw_now); + + update_leap_status(leap, raw_now.tv_sec, 0); + + /* Update also the synchronisation status */ + update_sync_status(&now); +} + +/* ================================================== */ + +void REF_GetReferenceParams ( struct timespec *local_time, @@ -1171,7 +1186,7 @@ double *root_dispersion ) { - double dispersion; + double dispersion, delta; assert(initialised); @@ -1203,13 +1218,17 @@ *stratum = local_stratum; *ref_id = NTP_REFID_LOCAL; - /* Make the reference time be now less a second - this will - scarcely affect the client, but will ensure that the transmit - timestamp cannot come before this (which would cause test 7 to - fail in the client's read routine) if the local system clock's - read routine is broken in any way. */ - *ref_time = *local_time; - --ref_time->tv_sec; + + /* Keep the reference timestamp up to date. Adjust the timestamp to make + sure that the transmit timestamp cannot come before this (which might + fail a test of an NTP client). */ + delta = UTI_DiffTimespecsToDouble(local_time, &local_ref_time); + if (delta > LOCAL_REF_UPDATE_INTERVAL || delta < 1.0) { + UTI_AddDoubleToTimespec(local_time, -1.0, &local_ref_time); + fuzz_ref_time(&local_ref_time); + } + + *ref_time = local_ref_time; /* Not much else we can do for leap second bits - maybe need to have a way for the administrator to feed leap bits in */ @@ -1311,22 +1330,24 @@ #define LEAP_SECOND_CLOSE 5 -int REF_IsLeapSecondClose(void) +static int +is_leap_close(time_t t) { - struct timespec now, now_raw; - time_t t; + return t >= leap_when - LEAP_SECOND_CLOSE && t < leap_when + LEAP_SECOND_CLOSE; +} - if (!our_leap_sec) - return 0; +/* ================================================== */ + +int REF_IsLeapSecondClose(struct timespec *ts, double offset) +{ + struct timespec now, now_raw; SCH_GetLastEventTime(&now, NULL, &now_raw); - t = now.tv_sec > 0 ? now.tv_sec : -now.tv_sec; - if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE) + if (is_leap_close(now.tv_sec) || is_leap_close(now_raw.tv_sec)) return 1; - t = now_raw.tv_sec > 0 ? now_raw.tv_sec : -now_raw.tv_sec; - if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE) + if (ts && (is_leap_close(ts->tv_sec) || is_leap_close(ts->tv_sec + offset))) return 1; return 0; diff -Nru chrony-3.5/reference.h chrony-4.1/reference.h --- chrony-3.5/reference.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/reference.h 2021-05-12 11:06:15.000000000 +0000 @@ -162,6 +162,9 @@ extern void REF_SetUnsynchronised(void); +/* Announce a leap second before the full reference update */ +extern void REF_UpdateLeapStatus(NTP_Leap leap); + /* Return the current stratum of this host or 16 if the host is not synchronised */ extern int REF_GetOurStratum(void); @@ -181,9 +184,9 @@ extern void REF_EnableLocal(int stratum, double distance, int orphan); extern void REF_DisableLocal(void); -/* Check if current raw or cooked time is close to a leap second - and is better to discard any measurements */ -extern int REF_IsLeapSecondClose(void); +/* Check if either of the current raw and cooked time, and optionally a + provided timestamp with an offset, is close to a leap second */ +extern int REF_IsLeapSecondClose(struct timespec *ts, double offset); /* Return TAI-UTC offset corresponding to a time in UTC if available */ extern int REF_GetTaiOffset(struct timespec *ts); diff -Nru chrony-3.5/reports.h chrony-4.1/reports.h --- chrony-3.5/reports.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/reports.h 2021-05-12 11:06:15.000000000 +0000 @@ -36,8 +36,14 @@ int stratum; int poll; enum {RPT_NTP_CLIENT, RPT_NTP_PEER, RPT_LOCAL_REFERENCE} mode; - enum {RPT_SYNC, RPT_UNREACH, RPT_FALSETICKER, RPT_JITTERY, RPT_CANDIDATE, RPT_OUTLIER} state; - int sel_options; + enum { + RPT_NONSELECTABLE, + RPT_FALSETICKER, + RPT_JITTERY, + RPT_SELECTABLE, + RPT_UNSELECTED, + RPT_SELECTED, + } state; int reachability; unsigned long latest_meas_ago; /* seconds */ @@ -78,8 +84,8 @@ typedef struct { struct timespec ref_time; - unsigned short n_samples; - unsigned short n_runs; + unsigned long n_samples; + unsigned long n_runs; unsigned long span_seconds; double rtc_seconds_fast; double rtc_gain_rate_ppm; @@ -88,22 +94,29 @@ typedef struct { IPAddr ip_addr; uint32_t ntp_hits; + uint32_t nke_hits; uint32_t cmd_hits; uint16_t ntp_drops; + uint16_t nke_drops; uint16_t cmd_drops; int8_t ntp_interval; + int8_t nke_interval; int8_t cmd_interval; int8_t ntp_timeout_interval; uint32_t last_ntp_hit_ago; + uint32_t last_nke_hit_ago; uint32_t last_cmd_hit_ago; } RPT_ClientAccessByIndex_Report; typedef struct { uint32_t ntp_hits; + uint32_t nke_hits; uint32_t cmd_hits; uint32_t ntp_drops; + uint32_t nke_drops; uint32_t cmd_drops; uint32_t log_drops; + uint32_t ntp_auth_hits; } RPT_ServerStatsReport; typedef struct { @@ -160,4 +173,30 @@ uint32_t total_valid_count; } RPT_NTPReport; +typedef struct { + NTP_AuthMode mode; + uint32_t key_id; + int key_type; + int key_length; + int ke_attempts; + uint32_t last_ke_ago; + int cookies; + int cookie_length; + int nak; +} RPT_AuthReport; + +typedef struct { + uint32_t ref_id; + IPAddr ip_addr; + char state_char; + int authentication; + NTP_Leap leap; + int conf_options; + int eff_options; + uint32_t last_sample_ago; + double score; + double lo_limit; + double hi_limit; +} RPT_SelectReport; + #endif /* GOT_REPORTS_H */ diff -Nru chrony-3.5/rtc.c chrony-4.1/rtc.c --- chrony-3.5/rtc.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/rtc.c 2021-05-12 11:06:15.000000000 +0000 @@ -148,6 +148,8 @@ if (driver.init) { if ((driver.init)()) { driver_initialised = 1; + } else { + LOG(LOGS_ERR, "RTC driver could not be initialised"); } } else { LOG(LOGS_ERR, "RTC not supported on this operating system"); @@ -160,7 +162,7 @@ void RTC_Finalise(void) { - if (driver.fini) { + if (driver_initialised) { (driver.fini)(); } } diff -Nru chrony-3.5/rtc_linux.c chrony-4.1/rtc_linux.c --- chrony-3.5/rtc_linux.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/rtc_linux.c 2021-05-12 11:06:15.000000000 +0000 @@ -390,12 +390,9 @@ if (!hwclock_file || !hwclock_file[0]) return; - in = fopen(hwclock_file, "r"); - if (!in) { - LOG(LOGS_WARN, "Could not open %s : %s", - hwclock_file, strerror(errno)); + in = UTI_OpenFile(NULL, hwclock_file, NULL, 'r', 0); + if (!in) return; - } /* Read third line from the file. */ for (i = 0; i < 3; i++) { @@ -445,7 +442,8 @@ tried_to_load_coefs = 1; - if (coefs_file_name && (in = fopen(coefs_file_name, "r"))) { + if (coefs_file_name && + (in = UTI_OpenFile(NULL, coefs_file_name, NULL, 'r', 0))) { if (fscanf(in, "%d%ld%lf%lf", &valid_coefs_from_file, &file_ref_time, @@ -466,68 +464,41 @@ static int write_coefs_to_file(int valid,time_t ref_time,double offset,double rate) { - struct stat buf; - char *temp_coefs_file_name; FILE *out; - int r1, r2; /* Create a temporary file with a '.tmp' extension. */ - - temp_coefs_file_name = (char*) Malloc(strlen(coefs_file_name)+8); - - if(!temp_coefs_file_name) { - return RTC_ST_BADFILE; - } - - strcpy(temp_coefs_file_name,coefs_file_name); - strcat(temp_coefs_file_name,".tmp"); - - out = fopen(temp_coefs_file_name, "w"); - if (!out) { - Free(temp_coefs_file_name); - LOG(LOGS_WARN, "Could not open temporary RTC file %s.tmp for writing", - coefs_file_name); + out = UTI_OpenFile(NULL, coefs_file_name, ".tmp", 'w', 0644); + if (!out) return RTC_ST_BADFILE; - } /* Gain rate is written out in ppm */ - r1 = fprintf(out, "%1d %ld %.6f %.3f\n", - valid, ref_time, offset, 1.0e6 * rate); - r2 = fclose(out); - if (r1 < 0 || r2) { - Free(temp_coefs_file_name); - LOG(LOGS_WARN, "Could not write to temporary RTC file %s.tmp", - coefs_file_name); - return RTC_ST_BADFILE; - } + fprintf(out, "%1d %ld %.6f %.3f\n", valid, ref_time, offset, 1.0e6 * rate); + fclose(out); - /* Clone the file attributes from the existing file if there is one. */ + /* Rename the temporary file to the correct location */ + if (!UTI_RenameTempFile(NULL, coefs_file_name, ".tmp", NULL)) + return RTC_ST_BADFILE; - if (!stat(coefs_file_name,&buf)) { - if (chown(temp_coefs_file_name,buf.st_uid,buf.st_gid) || - chmod(temp_coefs_file_name,buf.st_mode & 0777)) { - LOG(LOGS_WARN, - "Could not change ownership or permissions of temporary RTC file %s.tmp", - coefs_file_name); - } - } + return RTC_ST_OK; +} - /* Rename the temporary file to the correct location (see rename(2) for details). */ +/* ================================================== */ - if (rename(temp_coefs_file_name,coefs_file_name)) { - unlink(temp_coefs_file_name); - Free(temp_coefs_file_name); - LOG(LOGS_WARN, "Could not replace old RTC file %s.tmp with new one %s", - coefs_file_name, coefs_file_name); - return RTC_ST_BADFILE; +static int +switch_interrupts(int on_off) +{ + if (ioctl(fd, on_off ? RTC_UIE_ON : RTC_UIE_OFF, 0) < 0) { + LOG(LOGS_ERR, "Could not %s RTC interrupt : %s", + on_off ? "enable" : "disable", strerror(errno)); + return 0; } - Free(temp_coefs_file_name); + if (on_off) + skip_interrupts = 1; - return RTC_ST_OK; + return 1; } - /* ================================================== */ /* file_name is the name of the file where we save the RTC params between executions. Return status is whether we could initialise @@ -536,6 +507,23 @@ int RTC_Linux_Initialise(void) { + /* Try to open the device */ + fd = open(CNF_GetRtcDevice(), O_RDWR); + if (fd < 0) { + LOG(LOGS_ERR, "Could not open RTC device %s : %s", + CNF_GetRtcDevice(), strerror(errno)); + return 0; + } + + /* Make sure the RTC supports interrupts */ + if (!switch_interrupts(1) || !switch_interrupts(0)) { + close(fd); + return 0; + } + + /* Close on exec */ + UTI_FdSetCloexec(fd); + rtc_sec = MallocArray(time_t, MAX_SAMPLES); rtc_trim = MallocArray(double, MAX_SAMPLES); system_times = MallocArray(struct timespec, MAX_SAMPLES); @@ -546,18 +534,6 @@ /* In case it didn't get done by pre-init */ coefs_file_name = CNF_GetRtcFile(); - /* Try to open device */ - - fd = open (CNF_GetRtcDevice(), O_RDWR); - if (fd < 0) { - LOG(LOGS_ERR, "Could not open RTC device %s : %s", - CNF_GetRtcDevice(), strerror(errno)); - return 0; - } - - /* Close on exec */ - UTI_FdSetCloexec(fd); - n_samples = 0; n_samples_since_regression = 0; n_runs = 0; @@ -590,12 +566,17 @@ /* Remove input file handler */ if (fd >= 0) { SCH_RemoveFileHandler(fd); + switch_interrupts(0); close(fd); /* Save the RTC data */ (void) RTC_Linux_WriteParameters(); } + + if (rtc_sec) + LCL_RemoveParameterChangeHandler(slew_samples, NULL); + Free(rtc_sec); Free(rtc_trim); Free(system_times); @@ -603,29 +584,6 @@ /* ================================================== */ -static void -switch_interrupts(int onoff) -{ - int status; - - if (onoff) { - status = ioctl(fd, RTC_UIE_ON, 0); - if (status < 0) { - LOG(LOGS_ERR, "Could not %s RTC interrupt : %s", "enable", strerror(errno)); - return; - } - skip_interrupts = 1; - } else { - status = ioctl(fd, RTC_UIE_OFF, 0); - if (status < 0) { - LOG(LOGS_ERR, "Could not %s RTC interrupt : %s", "disable", strerror(errno)); - return; - } - } -} - -/* ================================================== */ - static void measurement_timeout(void *any) { diff -Nru chrony-3.5/samplefilt.c chrony-4.1/samplefilt.c --- chrony-3.5/samplefilt.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/samplefilt.c 2021-05-12 11:06:15.000000000 +0000 @@ -267,7 +267,7 @@ } } - for (i = j = 0, k = -1; i < filter->used; i++) { + for (i = j = 0; i < filter->used; i++) { if (selected[i] != -1) selected[j++] = (selected[i] + filter->used - o) % filter->used; } @@ -386,8 +386,6 @@ result->root_dispersion = MAX(disp, mean_root_dispersion); result->peer_delay = mean_peer_delay; result->root_delay = mean_root_delay; - result->stratum = last_sample->stratum; - result->leap = last_sample->leap; return 1; } diff -Nru chrony-3.5/sched.c chrony-4.1/sched.c --- chrony-3.5/sched.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sched.c 2021-05-12 11:06:15.000000000 +0000 @@ -65,6 +65,12 @@ static struct timespec last_select_ts, last_select_ts_raw; static double last_select_ts_err; +#define TS_MONO_PRECISION_NS 10000000U + +/* Monotonic low-precision timestamp measuring interval since the start */ +static double last_select_ts_mono; +static uint32_t last_select_ts_mono_ns; + /* ================================================== */ /* Variables to handler the timer queue */ @@ -105,7 +111,8 @@ /* ================================================== */ -static int need_to_exit; +/* Flag terminating the main loop, which can be set from a signal handler */ +static volatile int need_to_exit; /* ================================================== */ @@ -136,6 +143,8 @@ LCL_ReadRawTime(&last_select_ts_raw); last_select_ts = last_select_ts_raw; + last_select_ts_mono = 0.0; + last_select_ts_mono_ns = 0; initialised = 1; } @@ -147,6 +156,8 @@ SCH_Finalise(void) { ARR_DestroyInstance(file_handlers); + LCL_RemoveParameterChangeHandler(handle_slew, NULL); + initialised = 0; } @@ -247,6 +258,14 @@ /* ================================================== */ +double +SCH_GetLastEventMonoTime(void) +{ + return last_select_ts_mono; +} + +/* ================================================== */ + #define TQE_ALLOC_QUANTUM 32 static TimerQueueEntry * @@ -480,12 +499,15 @@ static void dispatch_timeouts(struct timespec *now) { + unsigned long n_done, n_entries_on_start; TimerQueueEntry *ptr; SCH_TimeoutHandler handler; SCH_ArbitraryArgument arg; - int n_done = 0, n_entries_on_start = n_timer_queue_entries; - while (1) { + n_entries_on_start = n_timer_queue_entries; + n_done = 0; + + do { LCL_ReadRawTime(now); if (!(n_timer_queue_entries > 0 && @@ -508,16 +530,21 @@ /* Increment count of timeouts handled */ ++n_done; - /* If more timeouts were handled than there were in the timer queue on - start and there are now, assume some code is scheduling timeouts with - negative delays and abort. Make the actual limit higher in case the - machine is temporarily overloaded and dispatching the handlers takes - more time than was delay of a scheduled timeout. */ - if (n_done > n_timer_queue_entries * 4 && - n_done > n_entries_on_start * 4) { + /* If the number of dispatched timeouts is significantly larger than the + length of the queue on start and now, assume there is a bug causing + an infinite loop by constantly adding a timeout with a zero or negative + delay. Check the actual rate of timeouts to avoid false positives in + case the execution slowed down so much (e.g. due to memory thrashing) + that it repeatedly takes more time to handle the timeout than is its + delay. This is a safety mechanism intended to stop a full-speed flood + of NTP requests due to a bug in the NTP polling. */ + + if (n_done > 20 && + n_done > 4 * MAX(n_timer_queue_entries, n_entries_on_start) && + fabs(UTI_DiffTimespecsToDouble(now, &last_select_ts_raw)) / n_done < 0.01) LOG_FATAL("Possible infinite loop in scheduling"); - } - } + + } while (!need_to_exit); } /* ================================================== */ @@ -706,6 +733,31 @@ /* ================================================== */ +static void +update_monotonic_time(struct timespec *now, struct timespec *before) +{ + struct timespec diff; + + /* Avoid frequent floating-point operations and handle small + increments to a large value */ + + UTI_DiffTimespecs(&diff, now, before); + if (diff.tv_sec == 0) { + last_select_ts_mono_ns += diff.tv_nsec; + } else { + last_select_ts_mono += fabs(UTI_TimespecToDouble(&diff) + + last_select_ts_mono_ns / 1.0e9); + last_select_ts_mono_ns = 0; + } + + if (last_select_ts_mono_ns > TS_MONO_PRECISION_NS) { + last_select_ts_mono += last_select_ts_mono_ns / 1.0e9; + last_select_ts_mono_ns = 0; + } +} + +/* ================================================== */ + void SCH_MainLoop(void) { @@ -756,6 +808,8 @@ LCL_ReadRawTime(&now); LCL_CookTime(&now, &cooked, &err); + update_monotonic_time(&now, &last_select_ts_raw); + /* Check if the time didn't jump unexpectedly */ if (!check_current_time(&saved_now, &now, status == 0, &saved_tv, ptv)) { /* Cook the time again after handling the step */ diff -Nru chrony-3.5/sched.h chrony-4.1/sched.h --- chrony-3.5/sched.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sched.h 2021-05-12 11:06:15.000000000 +0000 @@ -65,6 +65,9 @@ /* Get the time stamp taken after a file descriptor became ready or a timeout expired */ extern void SCH_GetLastEventTime(struct timespec *cooked, double *err, struct timespec *raw); +/* Get a low-precision monotonic timestamp (starting at 0.0) */ +extern double SCH_GetLastEventMonoTime(void); + /* This queues a timeout to elapse at a given (raw) local time */ extern SCH_TimeoutID SCH_AddTimeout(struct timespec *ts, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg); diff -Nru chrony-3.5/siv_gnutls.c chrony-4.1/siv_gnutls.c --- chrony-3.5/siv_gnutls.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/siv_gnutls.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,256 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + SIV ciphers using the GnuTLS library + */ + +#include "config.h" + +#include "sysincl.h" + +#include + +#include "logging.h" +#include "memory.h" +#include "siv.h" + +struct SIV_Instance_Record { + gnutls_cipher_algorithm_t algorithm; + gnutls_aead_cipher_hd_t cipher; +}; + +/* ================================================== */ + +static int instance_counter = 0; +static int gnutls_initialised = 0; + +/* ================================================== */ + +static void +init_gnutls(void) +{ + int r; + + if (gnutls_initialised) + return; + + r = gnutls_global_init(); + if (r < 0) + LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r)); + + DEBUG_LOG("Initialised"); + gnutls_initialised = 1; +} + +/* ================================================== */ + +static void +deinit_gnutls(void) +{ + assert(gnutls_initialised); + gnutls_global_deinit(); + gnutls_initialised = 0; + DEBUG_LOG("Deinitialised"); +} + +/* ================================================== */ + +static gnutls_cipher_algorithm_t +get_cipher_algorithm(SIV_Algorithm algorithm) +{ + switch (algorithm) { + case AEAD_AES_SIV_CMAC_256: + return GNUTLS_CIPHER_AES_128_SIV; + default: + return 0; + } +} + +/* ================================================== */ + +SIV_Instance +SIV_CreateInstance(SIV_Algorithm algorithm) +{ + gnutls_cipher_algorithm_t calgo; + SIV_Instance instance; + + calgo = get_cipher_algorithm(algorithm); + if (calgo == 0) + return NULL; + + if (instance_counter == 0) + init_gnutls(); + + /* Check if the cipher is actually supported */ + if (gnutls_cipher_get_tag_size(calgo) == 0) + return NULL; + + instance = MallocNew(struct SIV_Instance_Record); + instance->algorithm = calgo; + instance->cipher = NULL; + + instance_counter++; + + return instance; +} + +/* ================================================== */ + +void +SIV_DestroyInstance(SIV_Instance instance) +{ + if (instance->cipher) + gnutls_aead_cipher_deinit(instance->cipher); + Free(instance); + + instance_counter--; + if (instance_counter == 0) + deinit_gnutls(); +} + +/* ================================================== */ + +int +SIV_GetKeyLength(SIV_Algorithm algorithm) +{ + gnutls_cipher_algorithm_t calgo = get_cipher_algorithm(algorithm); + int len; + + if (calgo == 0) + return 0; + + len = gnutls_cipher_get_key_size(calgo); + + if (len < 1 || len > SIV_MAX_KEY_LENGTH) + LOG_FATAL("Invalid key length"); + + return len; +} + +/* ================================================== */ + +int +SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length) +{ + gnutls_aead_cipher_hd_t cipher; + gnutls_datum_t datum; + int r; + + if (length <= 0 || length != gnutls_cipher_get_key_size(instance->algorithm)) + return 0; + + datum.data = (unsigned char *)key; + datum.size = length; + + /* Initialise a new cipher with the provided key (gnutls does not seem to + have a function to change the key directly) */ + r = gnutls_aead_cipher_init(&cipher, instance->algorithm, &datum); + if (r < 0) { + DEBUG_LOG("Could not initialise %s : %s", "cipher", gnutls_strerror(r)); + return 0; + } + + /* Replace the previous cipher */ + if (instance->cipher) + gnutls_aead_cipher_deinit(instance->cipher); + instance->cipher = cipher; + + return 1; +} + +/* ================================================== */ + +int +SIV_GetTagLength(SIV_Instance instance) +{ + int len; + + len = gnutls_cipher_get_tag_size(instance->algorithm); + + if (len < 1 || len > SIV_MAX_TAG_LENGTH) + LOG_FATAL("Invalid tag length"); + + return len; +} + +/* ================================================== */ + +int +SIV_Encrypt(SIV_Instance instance, + const unsigned char *nonce, int nonce_length, + const void *assoc, int assoc_length, + const void *plaintext, int plaintext_length, + unsigned char *ciphertext, int ciphertext_length) +{ + size_t clen = ciphertext_length; + + if (!instance->cipher) + return 0; + + if (nonce_length < 1 || assoc_length < 0 || + plaintext_length < 0 || ciphertext_length < 0) + return 0; + + assert(assoc && plaintext); + + if (gnutls_aead_cipher_encrypt(instance->cipher, + nonce, nonce_length, assoc, assoc_length, 0, + plaintext, plaintext_length, ciphertext, &clen) < 0) + return 0; + + if (clen != ciphertext_length) + return 0; + + return 1; +} + +/* ================================================== */ + +int +SIV_Decrypt(SIV_Instance instance, + const unsigned char *nonce, int nonce_length, + const void *assoc, int assoc_length, + const unsigned char *ciphertext, int ciphertext_length, + void *plaintext, int plaintext_length) +{ + size_t plen = plaintext_length; + + if (!instance->cipher) + return 0; + + if (nonce_length < 1 || assoc_length < 0 || + plaintext_length < 0 || ciphertext_length < 0) + return 0; + + assert(assoc && plaintext); + + if (gnutls_aead_cipher_decrypt(instance->cipher, + nonce, nonce_length, assoc, assoc_length, 0, + ciphertext, ciphertext_length, plaintext, &plen) < 0) + return 0; + + if (plen != plaintext_length) + return 0; + + return 1; +} diff -Nru chrony-3.5/siv.h chrony-4.1/siv.h --- chrony-3.5/siv.h 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/siv.h 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,70 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + Header file for Synthetic Initialization Vector (SIV) ciphers. + + */ + +#ifndef GOT_SIV_H +#define GOT_SIV_H + +/* Maximum key length of all supported SIVs */ +#define SIV_MAX_KEY_LENGTH 32 + +/* Maximum difference between lengths of ciphertext and plaintext */ +#define SIV_MAX_TAG_LENGTH 16 + +/* Identifiers of SIV algorithms following the IANA AEAD registry */ +typedef enum { + AEAD_AES_SIV_CMAC_256 = 15, + AEAD_AES_SIV_CMAC_384 = 16, + AEAD_AES_SIV_CMAC_512 = 17, + AEAD_AES_128_GCM_SIV = 30, + AEAD_AES_256_GCM_SIV = 31, +} SIV_Algorithm; + +typedef struct SIV_Instance_Record *SIV_Instance; + +extern SIV_Instance SIV_CreateInstance(SIV_Algorithm algorithm); + +extern void SIV_DestroyInstance(SIV_Instance instance); + +extern int SIV_GetKeyLength(SIV_Algorithm algorithm); + +extern int SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length); + +extern int SIV_GetTagLength(SIV_Instance instance); + +extern int SIV_Encrypt(SIV_Instance instance, + const unsigned char *nonce, int nonce_length, + const void *assoc, int assoc_length, + const void *plaintext, int plaintext_length, + unsigned char *ciphertext, int ciphertext_length); + +extern int SIV_Decrypt(SIV_Instance instance, + const unsigned char *nonce, int nonce_length, + const void *assoc, int assoc_length, + const unsigned char *ciphertext, int ciphertext_length, + void *plaintext, int plaintext_length); + +#endif diff -Nru chrony-3.5/siv_nettle.c chrony-4.1/siv_nettle.c --- chrony-3.5/siv_nettle.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/siv_nettle.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,156 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + SIV ciphers using the Nettle library + */ + +#include "config.h" + +#include "sysincl.h" + +#ifdef HAVE_NETTLE_SIV_CMAC +#include +#else +#include "siv_nettle_int.c" +#endif + +#include "memory.h" +#include "siv.h" + +struct SIV_Instance_Record { + struct siv_cmac_aes128_ctx siv; + int key_set; +}; + +/* ================================================== */ + +SIV_Instance +SIV_CreateInstance(SIV_Algorithm algorithm) +{ + SIV_Instance instance; + + if (algorithm != AEAD_AES_SIV_CMAC_256) + return NULL; + + instance = MallocNew(struct SIV_Instance_Record); + instance->key_set = 0; + + return instance; +} + +/* ================================================== */ + +void +SIV_DestroyInstance(SIV_Instance instance) +{ + Free(instance); +} + +/* ================================================== */ + +int +SIV_GetKeyLength(SIV_Algorithm algorithm) +{ + assert(32 <= SIV_MAX_KEY_LENGTH); + + if (algorithm == AEAD_AES_SIV_CMAC_256) + return 32; + return 0; +} + +/* ================================================== */ + +int +SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length) +{ + if (length != 32) + return 0; + + siv_cmac_aes128_set_key(&instance->siv, key); + + instance->key_set = 1; + + return 1; +} + +/* ================================================== */ + +int +SIV_GetTagLength(SIV_Instance instance) +{ + assert(SIV_DIGEST_SIZE <= SIV_MAX_TAG_LENGTH); + + return SIV_DIGEST_SIZE; +} + +/* ================================================== */ + +int +SIV_Encrypt(SIV_Instance instance, + const unsigned char *nonce, int nonce_length, + const void *assoc, int assoc_length, + const void *plaintext, int plaintext_length, + unsigned char *ciphertext, int ciphertext_length) +{ + if (!instance->key_set) + return 0; + + if (nonce_length < SIV_MIN_NONCE_SIZE || assoc_length < 0 || + plaintext_length < 0 || plaintext_length > ciphertext_length || + plaintext_length + SIV_DIGEST_SIZE != ciphertext_length) + return 0; + + assert(assoc && plaintext); + + siv_cmac_aes128_encrypt_message(&instance->siv, nonce_length, nonce, + assoc_length, assoc, + ciphertext_length, ciphertext, plaintext); + return 1; +} + +/* ================================================== */ + +int +SIV_Decrypt(SIV_Instance instance, + const unsigned char *nonce, int nonce_length, + const void *assoc, int assoc_length, + const unsigned char *ciphertext, int ciphertext_length, + void *plaintext, int plaintext_length) +{ + if (!instance->key_set) + return 0; + + if (nonce_length < SIV_MIN_NONCE_SIZE || assoc_length < 0 || + plaintext_length < 0 || plaintext_length > ciphertext_length || + plaintext_length + SIV_DIGEST_SIZE != ciphertext_length) + return 0; + + assert(assoc && plaintext); + + if (!siv_cmac_aes128_decrypt_message(&instance->siv, nonce_length, nonce, + assoc_length, assoc, + plaintext_length, plaintext, ciphertext)) + return 0; + + return 1; +} diff -Nru chrony-3.5/siv_nettle_int.c chrony-4.1/siv_nettle_int.c --- chrony-3.5/siv_nettle_int.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/siv_nettle_int.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,452 @@ +/* This is a single-file implementation of AES-SIV-CMAC-256 based on + a patch for GNU Nettle by Nikos Mavrogiannopoulos */ + +/* + AES-CMAC-128 (rfc 4493) + Copyright (C) Stefan Metzmacher 2012 + Copyright (C) Jeremy Allison 2012 + Copyright (C) Michael Adam 2012 + Copyright (C) 2017, Red Hat Inc. + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle 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 copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ +/* siv-aes128.c, siv-cmac.c, siv.h + + AES-SIV, RFC5297 + SIV-CMAC, RFC5297 + + Copyright (C) 2017 Nikos Mavrogiannopoulos + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle 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 copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ +/* cmac.h, siv-cmac.h, cmac-aes128.c + + CMAC mode, as specified in RFC4493 + SIV-CMAC mode, as specified in RFC5297 + CMAC using AES128 as the underlying cipher. + + Copyright (C) 2017 Red Hat, Inc. + + Contributed by Nikos Mavrogiannopoulos + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle 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 copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +# include "config.h" + +#include +#include + +#include "nettle/aes.h" +#include "nettle/ctr.h" +#include "nettle/macros.h" +#include "nettle/memxor.h" +#include "nettle/memops.h" + +#include "nettle/nettle-types.h" + +/* For SIV, the block size of the block cipher shall be 128 bits. */ +#define SIV_BLOCK_SIZE 16 +#define SIV_DIGEST_SIZE 16 +#define SIV_MIN_NONCE_SIZE 1 + +/* + * SIV mode requires the aad and plaintext when building the IV, which + * prevents streaming processing and it incompatible with the AEAD API. + */ + +/* AES_SIV_CMAC_256 */ +struct siv_cmac_aes128_ctx { + struct aes128_ctx cipher; + uint8_t s2vk[AES128_KEY_SIZE]; +}; + +struct cmac128_ctx +{ + /* Key */ + union nettle_block16 K1; + union nettle_block16 K2; + + /* MAC state */ + union nettle_block16 X; + + /* Block buffer */ + union nettle_block16 block; + size_t index; +}; + +/* shift one and XOR with 0x87. */ +static void +_cmac128_block_mulx(union nettle_block16 *dst, + const union nettle_block16 *src) +{ + uint64_t b1 = READ_UINT64(src->b); + uint64_t b2 = READ_UINT64(src->b+8); + + b1 = (b1 << 1) | (b2 >> 63); + b2 <<= 1; + + if (src->b[0] & 0x80) + b2 ^= 0x87; + + WRITE_UINT64(dst->b, b1); + WRITE_UINT64(dst->b+8, b2); +} + +static void +cmac128_set_key(struct cmac128_ctx *ctx, const void *cipher, + nettle_cipher_func *encrypt) +{ + static const uint8_t const_zero[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + union nettle_block16 *L = &ctx->block; + memset(ctx, 0, sizeof(*ctx)); + + /* step 1 - generate subkeys k1 and k2 */ + encrypt(cipher, 16, L->b, const_zero); + + _cmac128_block_mulx(&ctx->K1, L); + _cmac128_block_mulx(&ctx->K2, &ctx->K1); +} + +#define MIN(x,y) ((x)<(y)?(x):(y)) + +static void +cmac128_update(struct cmac128_ctx *ctx, const void *cipher, + nettle_cipher_func *encrypt, + size_t msg_len, const uint8_t *msg) +{ + union nettle_block16 Y; + /* + * check if we expand the block + */ + if (ctx->index < 16) + { + size_t len = MIN(16 - ctx->index, msg_len); + memcpy(&ctx->block.b[ctx->index], msg, len); + msg += len; + msg_len -= len; + ctx->index += len; + } + + if (msg_len == 0) { + /* if it is still the last block, we are done */ + return; + } + + /* + * now checksum everything but the last block + */ + memxor3(Y.b, ctx->X.b, ctx->block.b, 16); + encrypt(cipher, 16, ctx->X.b, Y.b); + + while (msg_len > 16) + { + memxor3(Y.b, ctx->X.b, msg, 16); + encrypt(cipher, 16, ctx->X.b, Y.b); + msg += 16; + msg_len -= 16; + } + + /* + * copy the last block, it will be processed in + * cmac128_digest(). + */ + memcpy(ctx->block.b, msg, msg_len); + ctx->index = msg_len; +} + +static void +cmac128_digest(struct cmac128_ctx *ctx, const void *cipher, + nettle_cipher_func *encrypt, + unsigned length, + uint8_t *dst) +{ + union nettle_block16 Y; + + memset(ctx->block.b+ctx->index, 0, sizeof(ctx->block.b)-ctx->index); + + /* re-use ctx->block for memxor output */ + if (ctx->index < 16) + { + ctx->block.b[ctx->index] = 0x80; + memxor(ctx->block.b, ctx->K2.b, 16); + } + else + { + memxor(ctx->block.b, ctx->K1.b, 16); + } + + memxor3(Y.b, ctx->block.b, ctx->X.b, 16); + + assert(length <= 16); + if (length == 16) + { + encrypt(cipher, 16, dst, Y.b); + } + else + { + encrypt(cipher, 16, ctx->block.b, Y.b); + memcpy(dst, ctx->block.b, length); + } + + /* reset state for re-use */ + memset(&ctx->X, 0, sizeof(ctx->X)); + ctx->index = 0; +} + + +#define CMAC128_CTX(type) \ + { struct cmac128_ctx ctx; type cipher; } + +/* NOTE: Avoid using NULL, as we don't include anything defining it. */ +#define CMAC128_SET_KEY(self, set_key, encrypt, cmac_key) \ + do { \ + (set_key)(&(self)->cipher, (cmac_key)); \ + if (0) (encrypt)(&(self)->cipher, ~(size_t) 0, \ + (uint8_t *) 0, (const uint8_t *) 0); \ + cmac128_set_key(&(self)->ctx, &(self)->cipher, \ + (nettle_cipher_func *) (encrypt)); \ + } while (0) + +#define CMAC128_UPDATE(self, encrypt, length, src) \ + cmac128_update(&(self)->ctx, &(self)->cipher, \ + (nettle_cipher_func *)encrypt, (length), (src)) + +#define CMAC128_DIGEST(self, encrypt, length, digest) \ + (0 ? (encrypt)(&(self)->cipher, ~(size_t) 0, \ + (uint8_t *) 0, (const uint8_t *) 0) \ + : cmac128_digest(&(self)->ctx, &(self)->cipher, \ + (nettle_cipher_func *) (encrypt), \ + (length), (digest))) + +struct cmac_aes128_ctx CMAC128_CTX(struct aes128_ctx); + +static void +cmac_aes128_set_key(struct cmac_aes128_ctx *ctx, const uint8_t *key) +{ + CMAC128_SET_KEY(ctx, aes128_set_encrypt_key, aes128_encrypt, key); +} + +static void +cmac_aes128_update (struct cmac_aes128_ctx *ctx, + size_t length, const uint8_t *data) +{ + CMAC128_UPDATE (ctx, aes128_encrypt, length, data); +} + +static void +cmac_aes128_digest(struct cmac_aes128_ctx *ctx, + size_t length, uint8_t *digest) +{ + CMAC128_DIGEST(ctx, aes128_encrypt, length, digest); +} + +static const uint8_t const_one[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 +}; + +static const uint8_t const_zero[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static +void _siv_s2v(nettle_set_key_func *cmac_set_key, + nettle_hash_update_func *cmac_update, + nettle_hash_digest_func *cmac_digest, + size_t cmac_ctx_size, + const uint8_t *s2vk, size_t alength, const uint8_t *adata, + size_t nlength, const uint8_t *nonce, + size_t plength, const uint8_t *pdata, + uint8_t *v) +{ + uint8_t ctx[sizeof(struct cmac128_ctx)+sizeof(struct aes_ctx)]; + union nettle_block16 D, S, T; + + assert(cmac_ctx_size <= sizeof (ctx)); + + cmac_set_key(ctx, s2vk); + + if (nlength == 0 && alength == 0) { + cmac_update(ctx, 16, const_one); + cmac_digest(ctx, 16, v); + return; + } + + cmac_update(ctx, 16, const_zero); + cmac_digest(ctx, 16, D.b); + + if (1) { + _cmac128_block_mulx(&D, &D); + cmac_update(ctx, alength, adata); + cmac_digest(ctx, 16, S.b); + + memxor(D.b, S.b, 16); + } + + if (nlength > 0) { + _cmac128_block_mulx(&D, &D); + cmac_update(ctx, nlength, nonce); + cmac_digest(ctx, 16, S.b); + + memxor(D.b, S.b, 16); + } + + /* Sn */ + if (plength >= 16) { + cmac_update(ctx, plength-16, pdata); + + pdata += plength-16; + + memxor3(T.b, pdata, D.b, 16); + } else { + union nettle_block16 pad; + + _cmac128_block_mulx(&T, &D); + memcpy(pad.b, pdata, plength); + pad.b[plength] = 0x80; + if (plength+1 < 16) + memset(&pad.b[plength+1], 0, 16-plength-1); + + memxor(T.b, pad.b, 16); + } + + cmac_update(ctx, 16, T.b); + cmac_digest(ctx, 16, v); +} + +static void +siv_cmac_aes128_set_key(struct siv_cmac_aes128_ctx *ctx, const uint8_t *key) +{ + memcpy(ctx->s2vk, key, 16); + aes128_set_encrypt_key(&ctx->cipher, key+16); +} + +static void +siv_cmac_aes128_encrypt_message(struct siv_cmac_aes128_ctx *ctx, + size_t nlength, const uint8_t *nonce, + size_t alength, const uint8_t *adata, + size_t clength, uint8_t *dst, const uint8_t *src) +{ + union nettle_block16 siv; + size_t slength; + + assert (clength >= SIV_DIGEST_SIZE); + slength = clength - SIV_DIGEST_SIZE; + + /* create CTR nonce */ + _siv_s2v((nettle_set_key_func*)cmac_aes128_set_key, + (nettle_hash_update_func*)cmac_aes128_update, + (nettle_hash_digest_func*)cmac_aes128_digest, + sizeof(struct cmac_aes128_ctx), ctx->s2vk, alength, adata, + nlength, nonce, slength, src, siv.b); + memcpy(dst, siv.b, SIV_DIGEST_SIZE); + siv.b[8] &= ~0x80; + siv.b[12] &= ~0x80; + + ctr_crypt(&ctx->cipher, (nettle_cipher_func *)aes128_encrypt, AES_BLOCK_SIZE, + siv.b, slength, dst+SIV_DIGEST_SIZE, src); +} + +static int +siv_cmac_aes128_decrypt_message(struct siv_cmac_aes128_ctx *ctx, + size_t nlength, const uint8_t *nonce, + size_t alength, const uint8_t *adata, + size_t mlength, uint8_t *dst, const uint8_t *src) +{ + union nettle_block16 siv; + union nettle_block16 ctr; + + memcpy(ctr.b, src, SIV_DIGEST_SIZE); + ctr.b[8] &= ~0x80; + ctr.b[12] &= ~0x80; + + ctr_crypt(&ctx->cipher, (nettle_cipher_func *)aes128_encrypt, AES_BLOCK_SIZE, + ctr.b, mlength, dst, src+SIV_DIGEST_SIZE); + + /* create CTR nonce */ + _siv_s2v((nettle_set_key_func*)cmac_aes128_set_key, + (nettle_hash_update_func*)cmac_aes128_update, + (nettle_hash_digest_func*)cmac_aes128_digest, + sizeof(struct cmac_aes128_ctx), ctx->s2vk, alength, adata, + nlength, nonce, mlength, dst, siv.b); + + return memeql_sec(siv.b, src, SIV_DIGEST_SIZE); +} + diff -Nru chrony-3.5/smooth.c chrony-4.1/smooth.c --- chrony-3.5/smooth.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/smooth.c 2021-05-12 11:06:15.000000000 +0000 @@ -272,6 +272,10 @@ void SMT_Finalise(void) { + if (!enabled) + return; + + LCL_RemoveParameterChangeHandler(handle_slew, NULL); } int SMT_IsEnabled(void) diff -Nru chrony-3.5/socket.c chrony-4.1/socket.c --- chrony-3.5/socket.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/socket.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,1625 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Richard P. Curnow 1997-2003 + * Copyright (C) Timo Teras 2009 + * Copyright (C) Miroslav Lichvar 2009, 2013-2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + This file implements socket operations. + + */ + +#include "config.h" + +#include "sysincl.h" + +#ifdef HAVE_LINUX_TIMESTAMPING +#include +#include +#endif + +#include "socket.h" +#include "array.h" +#include "logging.h" +#include "privops.h" +#include "util.h" + +#define INVALID_SOCK_FD (-4) +#define CMSG_BUF_SIZE 256 + +union sockaddr_all { + struct sockaddr_in in4; +#ifdef FEAT_IPV6 + struct sockaddr_in6 in6; +#endif + struct sockaddr_un un; + struct sockaddr sa; +}; + +struct Message { + union sockaddr_all name; + struct iovec iov; + /* Buffer of sufficient length for all expected messages */ + union { + NTP_Packet ntp_msg; + CMD_Request cmd_request; + CMD_Reply cmd_reply; + } msg_buf; + /* Aligned buffer for control messages */ + struct cmsghdr cmsg_buf[CMSG_BUF_SIZE / sizeof (struct cmsghdr)]; +}; + +#ifdef HAVE_RECVMMSG +#define MAX_RECV_MESSAGES 16 +#define MessageHeader mmsghdr +#else +/* Compatible with mmsghdr */ +struct MessageHeader { + struct msghdr msg_hdr; + unsigned int msg_len; +}; + +#define MAX_RECV_MESSAGES 1 +#endif + +static int initialised; + +/* Flags indicating in which IP families sockets can be requested */ +static int ip4_enabled; +static int ip6_enabled; + +/* Flags supported by socket() */ +static int supported_socket_flags; + +/* Arrays of Message, MessageHeader, and SCK_Message */ +static ARR_Instance recv_messages; +static ARR_Instance recv_headers; +static ARR_Instance recv_sck_messages; + +static unsigned int received_messages; + +static int (*priv_bind_function)(int sock_fd, struct sockaddr *address, + socklen_t address_len); + +/* ================================================== */ + +static void +prepare_buffers(unsigned int n) +{ + struct MessageHeader *hdr; + struct Message *msg; + unsigned int i; + + for (i = 0; i < n; i++) { + msg = ARR_GetElement(recv_messages, i); + hdr = ARR_GetElement(recv_headers, i); + + msg->iov.iov_base = &msg->msg_buf; + msg->iov.iov_len = sizeof (msg->msg_buf); + hdr->msg_hdr.msg_name = &msg->name; + hdr->msg_hdr.msg_namelen = sizeof (msg->name); + hdr->msg_hdr.msg_iov = &msg->iov; + hdr->msg_hdr.msg_iovlen = 1; + hdr->msg_hdr.msg_control = msg->cmsg_buf; + hdr->msg_hdr.msg_controllen = sizeof (msg->cmsg_buf); + hdr->msg_hdr.msg_flags = 0; + hdr->msg_len = 0; + } +} + +/* ================================================== */ + +static const char * +domain_to_string(int domain) +{ + switch (domain) { + case AF_INET: + return "IPv4"; +#ifdef AF_INET6 + case AF_INET6: + return "IPv6"; +#endif + case AF_UNIX: + return "Unix"; + case AF_UNSPEC: + return "UNSPEC"; + default: + return "?"; + } +} + +/* ================================================== */ + +#if defined(SOCK_CLOEXEC) || defined(SOCK_NONBLOCK) +static int +check_socket_flag(int sock_flag, int fd_flag, int fs_flag) +{ + int sock_fd, fd_flags, fs_flags; + + sock_fd = socket(AF_INET, SOCK_DGRAM | sock_flag, 0); + if (sock_fd < 0) + return 0; + + fd_flags = fcntl(sock_fd, F_GETFD); + fs_flags = fcntl(sock_fd, F_GETFL); + + close(sock_fd); + + if (fd_flags == -1 || (fd_flags & fd_flag) != fd_flag || + fs_flags == -1 || (fs_flags & fs_flag) != fs_flag) + return 0; + + return 1; +} +#endif + +/* ================================================== */ + +static int +set_socket_nonblock(int sock_fd) +{ + if (fcntl(sock_fd, F_SETFL, O_NONBLOCK) < 0) { + DEBUG_LOG("Could not set O_NONBLOCK : %s", strerror(errno)); + return 0; + } + + return 1; +} + +/* ================================================== */ + +static int +get_open_flags(int flags) +{ + int r = supported_socket_flags; + +#ifdef SOCK_NONBLOCK + if (flags & SCK_FLAG_BLOCK) + r &= ~SOCK_NONBLOCK; +#endif + + return r; +} + +/* ================================================== */ + +static int +set_socket_flags(int sock_fd, int flags) +{ + /* Close the socket automatically on exec */ + if ( +#ifdef SOCK_CLOEXEC + (supported_socket_flags & SOCK_CLOEXEC) == 0 && +#endif + !UTI_FdSetCloexec(sock_fd)) + return 0; + + /* Enable non-blocking mode */ + if ((flags & SCK_FLAG_BLOCK) == 0 && +#ifdef SOCK_NONBLOCK + (supported_socket_flags & SOCK_NONBLOCK) == 0 && +#endif + !set_socket_nonblock(sock_fd)) + return 0; + + return 1; +} + +/* ================================================== */ + +static int +open_socket(int domain, int type, int flags) +{ + int sock_fd; + + sock_fd = socket(domain, type | get_open_flags(flags), 0); + + if (sock_fd < 0) { + DEBUG_LOG("Could not open %s socket : %s", + domain_to_string(domain), strerror(errno)); + return INVALID_SOCK_FD; + } + + if (!set_socket_flags(sock_fd, flags)) { + close(sock_fd); + return INVALID_SOCK_FD; + } + + return sock_fd; +} + +/* ================================================== */ + +static int +open_socket_pair(int domain, int type, int flags, int *other_fd) +{ + int sock_fds[2]; + + if (socketpair(domain, type | get_open_flags(flags), 0, sock_fds) < 0) { + DEBUG_LOG("Could not open %s socket : %s", + domain_to_string(domain), strerror(errno)); + return INVALID_SOCK_FD; + } + + if (!set_socket_flags(sock_fds[0], flags) || !set_socket_flags(sock_fds[1], flags)) { + close(sock_fds[0]); + close(sock_fds[1]); + return INVALID_SOCK_FD; + } + + *other_fd = sock_fds[1]; + + return sock_fds[0]; +} + +/* ================================================== */ + +static int +set_socket_options(int sock_fd, int flags) +{ + /* Make the socket capable of sending broadcast packets if requested */ + if (flags & SCK_FLAG_BROADCAST && !SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_BROADCAST, 1)) + ; + + return 1; +} + +/* ================================================== */ + +static int +set_ip_options(int sock_fd, int family, int flags) +{ +#if defined(FEAT_IPV6) && defined(IPV6_V6ONLY) + /* Receive only IPv6 packets on an IPv6 socket */ + if (family == IPADDR_INET6 && !SCK_SetIntOption(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, 1)) + return 0; +#endif + + /* Provide destination address of received packets if requested */ + if (flags & SCK_FLAG_RX_DEST_ADDR) { + if (family == IPADDR_INET4) { +#ifdef HAVE_IN_PKTINFO + if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_PKTINFO, 1)) + ; +#elif defined(IP_RECVDSTADDR) + if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_RECVDSTADDR, 1)) + ; +#endif + } +#ifdef FEAT_IPV6 + else if (family == IPADDR_INET6) { +#ifdef HAVE_IN6_PKTINFO +#ifdef IPV6_RECVPKTINFO + if (!SCK_SetIntOption(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, 1)) + ; +#else + if (!SCK_SetIntOption(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, 1)) + ; +#endif +#endif + } +#endif + } + + return 1; +} + +/* ================================================== */ + +static int +is_any_address(IPAddr *addr) +{ + IPAddr any_addr; + + SCK_GetAnyLocalIPAddress(addr->family, &any_addr); + + return UTI_CompareIPs(&any_addr, addr, NULL) == 0; +} + +/* ================================================== */ + +static int +bind_device(int sock_fd, const char *iface) +{ +#ifdef SO_BINDTODEVICE + if (setsockopt(sock_fd, SOL_SOCKET, SO_BINDTODEVICE, iface, strlen(iface) + 1) < 0) { + DEBUG_LOG("Could not bind socket to %s : %s", iface, strerror(errno)); + return 0; + } + return 1; +#else + DEBUG_LOG("Could not bind socket to %s : %s", iface, "Not supported"); + return 0; +#endif +} + +/* ================================================== */ + +static int +bind_ip_address(int sock_fd, IPSockAddr *addr, int flags) +{ + union sockaddr_all saddr; + socklen_t saddr_len; + int s; + + /* Make the socket capable of re-using an old address if binding to a specific port */ + if (addr->port > 0 && !SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_REUSEADDR, 1)) + ; + +#if defined(LINUX) && defined(SO_REUSEPORT) + /* Allow multiple instances to bind to the same port in order to enable load + balancing. Don't enable this option on non-Linux systems as it has + a slightly different meaning there (with some important implications). */ + if (addr->port > 0 && !SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_REUSEPORT, 1)) + ; +#endif + +#ifdef IP_FREEBIND + /* Allow binding to an address that doesn't exist yet */ + if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_FREEBIND, 1)) + ; +#endif + + saddr_len = SCK_IPSockAddrToSockaddr(addr, (struct sockaddr *)&saddr, sizeof (saddr)); + if (saddr_len == 0) + return 0; + + if (flags & SCK_FLAG_PRIV_BIND && priv_bind_function) + s = priv_bind_function(sock_fd, &saddr.sa, saddr_len); + else + s = bind(sock_fd, &saddr.sa, saddr_len); + + if (s < 0) { + DEBUG_LOG("Could not bind socket to %s : %s", + UTI_IPSockAddrToString(addr), strerror(errno)); + return 0; + } + + return 1; +} + +/* ================================================== */ + +static int +connect_ip_address(int sock_fd, IPSockAddr *addr) +{ + union sockaddr_all saddr; + socklen_t saddr_len; + + saddr_len = SCK_IPSockAddrToSockaddr(addr, (struct sockaddr *)&saddr, sizeof (saddr)); + if (saddr_len == 0) + return 0; + + if (connect(sock_fd, &saddr.sa, saddr_len) < 0 && errno != EINPROGRESS) { + DEBUG_LOG("Could not connect socket to %s : %s", + UTI_IPSockAddrToString(addr), strerror(errno)); + return 0; + } + + return 1; +} + +/* ================================================== */ + +static int +open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *iface, + int type, int flags) +{ + int domain, family, sock_fd; + + if (local_addr) + family = local_addr->ip_addr.family; + else if (remote_addr) + family = remote_addr->ip_addr.family; + else + family = IPADDR_INET4; + + switch (family) { + case IPADDR_INET4: + if (!ip4_enabled) + return INVALID_SOCK_FD; + domain = AF_INET; + break; +#ifdef FEAT_IPV6 + case IPADDR_INET6: + if (!ip6_enabled) + return INVALID_SOCK_FD; + domain = AF_INET6; + break; +#endif + default: + DEBUG_LOG("Unspecified family"); + return INVALID_SOCK_FD; + } + + sock_fd = open_socket(domain, type, flags); + if (sock_fd < 0) + return INVALID_SOCK_FD; + + if (!set_socket_options(sock_fd, flags)) + goto error; + + if (!set_ip_options(sock_fd, family, flags)) + goto error; + + if (iface && !bind_device(sock_fd, iface)) + goto error; + + /* Bind the socket if a non-any local address/port was specified */ + if (local_addr && local_addr->ip_addr.family != IPADDR_UNSPEC && + (local_addr->port != 0 || !is_any_address(&local_addr->ip_addr)) && + !bind_ip_address(sock_fd, local_addr, flags)) + goto error; + + /* Connect the socket if a remote address was specified */ + if (remote_addr && remote_addr->ip_addr.family != IPADDR_UNSPEC && + !connect_ip_address(sock_fd, remote_addr)) + goto error; + + if (remote_addr || local_addr) + DEBUG_LOG("Opened %s%s socket fd=%d%s%s%s%s", + type == SOCK_DGRAM ? "UDP" : type == SOCK_STREAM ? "TCP" : "?", + family == IPADDR_INET4 ? "v4" : "v6", + sock_fd, + remote_addr ? " remote=" : "", + remote_addr ? UTI_IPSockAddrToString(remote_addr) : "", + local_addr ? " local=" : "", + local_addr ? UTI_IPSockAddrToString(local_addr) : ""); + + return sock_fd; + +error: + SCK_CloseSocket(sock_fd); + return INVALID_SOCK_FD; +} + +/* ================================================== */ + +static int +bind_unix_address(int sock_fd, const char *addr, int flags) +{ + union sockaddr_all saddr; + + if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >= + sizeof (saddr.un.sun_path)) { + DEBUG_LOG("Unix socket path %s too long", addr); + return 0; + } + saddr.un.sun_family = AF_UNIX; + + if (unlink(addr) < 0) + DEBUG_LOG("Could not remove %s : %s", addr, strerror(errno)); + + /* PRV_BindSocket() doesn't support Unix sockets yet */ + if (bind(sock_fd, &saddr.sa, sizeof (saddr.un)) < 0) { + DEBUG_LOG("Could not bind Unix socket to %s : %s", addr, strerror(errno)); + 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; +} + +/* ================================================== */ + +static int +connect_unix_address(int sock_fd, const char *addr) +{ + union sockaddr_all saddr; + + if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >= + sizeof (saddr.un.sun_path)) { + DEBUG_LOG("Unix socket path %s too long", addr); + return 0; + } + saddr.un.sun_family = AF_UNIX; + + if (connect(sock_fd, &saddr.sa, sizeof (saddr.un)) < 0) { + DEBUG_LOG("Could not connect Unix socket to %s : %s", addr, strerror(errno)); + return 0; + } + + return 1; +} + +/* ================================================== */ + +static int +open_unix_socket(const char *remote_addr, const char *local_addr, int type, int flags) +{ + int sock_fd; + + sock_fd = open_socket(AF_UNIX, type, flags); + if (sock_fd < 0) + return INVALID_SOCK_FD; + + if (!set_socket_options(sock_fd, flags)) + goto error; + + /* Bind the socket if a local address was specified */ + if (local_addr && !bind_unix_address(sock_fd, local_addr, flags)) + goto error; + + /* Connect the socket if a remote address was specified */ + if (remote_addr && !connect_unix_address(sock_fd, remote_addr)) + goto error; + + DEBUG_LOG("Opened Unix socket fd=%d%s%s%s%s", + sock_fd, + remote_addr ? " remote=" : "", remote_addr ? remote_addr : "", + local_addr ? " local=" : "", local_addr ? local_addr : ""); + + return sock_fd; + +error: + SCK_RemoveSocket(sock_fd); + SCK_CloseSocket(sock_fd); + return INVALID_SOCK_FD; +} + +/* ================================================== */ + +static int +open_unix_socket_pair(int type, int flags, int *other_fd) +{ + int sock_fd; + + sock_fd = open_socket_pair(AF_UNIX, type, flags, other_fd); + if (sock_fd < 0) + return INVALID_SOCK_FD; + + DEBUG_LOG("Opened Unix socket pair fd1=%d fd2=%d", sock_fd, *other_fd); + + return sock_fd; +} + +/* ================================================== */ + +static int +get_recv_flags(int flags) +{ + int recv_flags = 0; + + if (flags & SCK_FLAG_MSG_ERRQUEUE) { +#ifdef MSG_ERRQUEUE + recv_flags |= MSG_ERRQUEUE; +#else + assert(0); +#endif + } + + return recv_flags; +} + +/* ================================================== */ + +static void +handle_recv_error(int sock_fd, int flags) +{ +#ifdef MSG_ERRQUEUE + /* If reading from the error queue failed, the select() exception should + be for a socket error. Clear the error to avoid a busy loop. */ + if (flags & SCK_FLAG_MSG_ERRQUEUE) { + int error = 0; + + if (SCK_GetIntOption(sock_fd, SOL_SOCKET, SO_ERROR, &error)) + errno = error; + } +#endif + + DEBUG_LOG("Could not receive message fd=%d : %s", sock_fd, strerror(errno)); +} + +/* ================================================== */ + +static void +log_message(int sock_fd, int direction, SCK_Message *message, const char *prefix, + const char *error) +{ + const char *local_addr, *remote_addr; + char if_index[20], tss[10], tsif[20], tslen[20]; + + if (DEBUG <= 0 || log_min_severity > LOGS_DEBUG) + return; + + remote_addr = NULL; + local_addr = NULL; + if_index[0] = '\0'; + tss[0] = '\0'; + tsif[0] = '\0'; + tslen[0] = '\0'; + + switch (message->addr_type) { + case SCK_ADDR_IP: + if (message->remote_addr.ip.ip_addr.family != IPADDR_UNSPEC) + remote_addr = UTI_IPSockAddrToString(&message->remote_addr.ip); + if (message->local_addr.ip.family != IPADDR_UNSPEC) + local_addr = UTI_IPToString(&message->local_addr.ip); + break; + case SCK_ADDR_UNIX: + remote_addr = message->remote_addr.path; + break; + default: + break; + } + + if (message->if_index != INVALID_IF_INDEX) + snprintf(if_index, sizeof (if_index), " if=%d", message->if_index); + + if (direction > 0) { + if (!UTI_IsZeroTimespec(&message->timestamp.kernel) || + !UTI_IsZeroTimespec(&message->timestamp.hw)) + snprintf(tss, sizeof (tss), " tss=%s%s", + !UTI_IsZeroTimespec(&message->timestamp.kernel) ? "K" : "", + !UTI_IsZeroTimespec(&message->timestamp.hw) ? "H" : ""); + + if (message->timestamp.if_index != INVALID_IF_INDEX) + snprintf(tsif, sizeof (tsif), " tsif=%d", message->timestamp.if_index); + + if (message->timestamp.l2_length != 0) + snprintf(tslen, sizeof (tslen), " tslen=%d", message->timestamp.l2_length); + } + + DEBUG_LOG("%s message%s%s%s%s fd=%d len=%d%s%s%s%s%s%s", + prefix, + remote_addr ? (direction > 0 ? " from " : " to ") : "", + remote_addr ? remote_addr : "", + local_addr ? (direction > 0 ? " to " : " from ") : "", + local_addr ? local_addr : "", + sock_fd, message->length, if_index, + tss, tsif, tslen, + error ? " : " : "", error ? error : ""); +} + +/* ================================================== */ + +static void +init_message_addresses(SCK_Message *message, SCK_AddressType addr_type) +{ + message->addr_type = addr_type; + + switch (addr_type) { + case SCK_ADDR_UNSPEC: + break; + case SCK_ADDR_IP: + message->remote_addr.ip.ip_addr.family = IPADDR_UNSPEC; + message->remote_addr.ip.port = 0; + message->local_addr.ip.family = IPADDR_UNSPEC; + break; + case SCK_ADDR_UNIX: + message->remote_addr.path = NULL; + break; + default: + assert(0); + } +} + +/* ================================================== */ + +static void +init_message_nonaddress(SCK_Message *message) +{ + message->data = NULL; + message->length = 0; + message->if_index = INVALID_IF_INDEX; + + UTI_ZeroTimespec(&message->timestamp.kernel); + UTI_ZeroTimespec(&message->timestamp.hw); + message->timestamp.if_index = INVALID_IF_INDEX; + message->timestamp.l2_length = 0; + message->timestamp.tx_flags = 0; + + message->descriptor = INVALID_SOCK_FD; +} + +/* ================================================== */ + +static int +match_cmsg(struct cmsghdr *cmsg, int level, int type, size_t length) +{ + if (cmsg->cmsg_type == type && cmsg->cmsg_level == level && + (length == 0 || cmsg->cmsg_len == CMSG_LEN(length))) + return 1; + return 0; +} + +/* ================================================== */ + +static int +process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags, + SCK_Message *message) +{ + struct cmsghdr *cmsg; + int r = 1; + + if (msg->msg_namelen <= sizeof (union sockaddr_all) && + msg->msg_namelen > sizeof (((struct sockaddr *)msg->msg_name)->sa_family)) { + switch (((struct sockaddr *)msg->msg_name)->sa_family) { + case AF_INET: +#ifdef FEAT_IPV6 + case AF_INET6: +#endif + init_message_addresses(message, SCK_ADDR_IP); + SCK_SockaddrToIPSockAddr(msg->msg_name, msg->msg_namelen, &message->remote_addr.ip); + break; + case AF_UNIX: + init_message_addresses(message, SCK_ADDR_UNIX); + message->remote_addr.path = ((struct sockaddr_un *)msg->msg_name)->sun_path; + break; + default: + init_message_addresses(message, SCK_ADDR_UNSPEC); + DEBUG_LOG("Unexpected address"); + r = 0; + break; + } + } else { + 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); + + if (msg->msg_iovlen == 1) { + 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) { + log_message(sock_fd, 1, message, "Truncated", NULL); + r = 0; + } + + if (msg->msg_flags & MSG_CTRUNC) { + log_message(sock_fd, 1, message, "Truncated cmsg in", NULL); + r = 0; + } + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + if (0) { + } +#ifdef HAVE_IN_PKTINFO + else if (match_cmsg(cmsg, IPPROTO_IP, IP_PKTINFO, sizeof (struct in_pktinfo))) { + struct in_pktinfo ipi; + + if (message->addr_type != SCK_ADDR_IP) + init_message_addresses(message, SCK_ADDR_IP); + + memcpy(&ipi, CMSG_DATA(cmsg), sizeof (ipi)); + message->local_addr.ip.addr.in4 = ntohl(ipi.ipi_addr.s_addr); + message->local_addr.ip.family = IPADDR_INET4; + message->if_index = ipi.ipi_ifindex; + } +#elif defined(IP_RECVDSTADDR) + else if (match_cmsg(cmsg, IPPROTO_IP, IP_RECVDSTADDR, sizeof (struct in_addr))) { + struct in_addr addr; + + if (message->addr_type != SCK_ADDR_IP) + init_message_addresses(message, SCK_ADDR_IP); + + memcpy(&addr, CMSG_DATA(cmsg), sizeof (addr)); + message->local_addr.ip.addr.in4 = ntohl(addr.s_addr); + message->local_addr.ip.family = IPADDR_INET4; + } +#endif +#ifdef HAVE_IN6_PKTINFO + else if (match_cmsg(cmsg, IPPROTO_IPV6, IPV6_PKTINFO, sizeof (struct in6_pktinfo))) { + struct in6_pktinfo ipi; + + if (message->addr_type != SCK_ADDR_IP) + init_message_addresses(message, SCK_ADDR_IP); + + memcpy(&ipi, CMSG_DATA(cmsg), sizeof (ipi)); + memcpy(&message->local_addr.ip.addr.in6, &ipi.ipi6_addr.s6_addr, + sizeof (message->local_addr.ip.addr.in6)); + message->local_addr.ip.family = IPADDR_INET6; + message->if_index = ipi.ipi6_ifindex; + } +#endif +#ifdef SCM_TIMESTAMP + else if (match_cmsg(cmsg, SOL_SOCKET, SCM_TIMESTAMP, sizeof (struct timeval))) { + struct timeval tv; + + memcpy(&tv, CMSG_DATA(cmsg), sizeof (tv)); + UTI_TimevalToTimespec(&tv, &message->timestamp.kernel); + } +#endif +#ifdef SCM_TIMESTAMPNS + else if (match_cmsg(cmsg, SOL_SOCKET, SCM_TIMESTAMPNS, sizeof (message->timestamp.kernel))) { + memcpy(&message->timestamp.kernel, CMSG_DATA(cmsg), sizeof (message->timestamp.kernel)); + } +#endif +#ifdef HAVE_LINUX_TIMESTAMPING +#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO + else if (match_cmsg(cmsg, SOL_SOCKET, SCM_TIMESTAMPING_PKTINFO, + sizeof (struct scm_ts_pktinfo))) { + struct scm_ts_pktinfo ts_pktinfo; + + memcpy(&ts_pktinfo, CMSG_DATA(cmsg), sizeof (ts_pktinfo)); + message->timestamp.if_index = ts_pktinfo.if_index; + message->timestamp.l2_length = ts_pktinfo.pkt_length; + } +#endif + else if (match_cmsg(cmsg, SOL_SOCKET, SCM_TIMESTAMPING, + sizeof (struct scm_timestamping))) { + struct scm_timestamping ts3; + + memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3)); + message->timestamp.kernel = ts3.ts[0]; + message->timestamp.hw = ts3.ts[2]; + } + else if ((match_cmsg(cmsg, SOL_IP, IP_RECVERR, 0) || + match_cmsg(cmsg, SOL_IPV6, IPV6_RECVERR, 0)) && + cmsg->cmsg_len >= CMSG_LEN(sizeof (struct sock_extended_err))) { + struct sock_extended_err err; + + memcpy(&err, CMSG_DATA(cmsg), sizeof (err)); + + if (err.ee_errno != ENOMSG || err.ee_info != SCM_TSTAMP_SND || + err.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) { + log_message(sock_fd, 1, message, "Unexpected extended error in", NULL); + r = 0; + } + } +#endif + else if (match_cmsg(cmsg, SOL_SOCKET, SCM_RIGHTS, 0)) { + if (!(flags & SCK_FLAG_MSG_DESCRIPTOR) || cmsg->cmsg_len != CMSG_LEN(sizeof (int))) { + int i, fd; + + DEBUG_LOG("Unexpected SCM_RIGHTS"); + for (i = 0; CMSG_LEN((i + 1) * sizeof (int)) <= cmsg->cmsg_len; i++) { + memcpy(&fd, (char *)CMSG_DATA(cmsg) + i * sizeof (int), sizeof (fd)); + close(fd); + } + r = 0; + } else { + memcpy(&message->descriptor, CMSG_DATA(cmsg), sizeof (message->descriptor)); + } + } + else { + DEBUG_LOG("Unexpected control message level=%d type=%d len=%d", + cmsg->cmsg_level, cmsg->cmsg_type, (int)cmsg->cmsg_len); + } + } + + if (!r && message->descriptor != INVALID_SOCK_FD) + close(message->descriptor); + + return r; +} + +/* ================================================== */ + +static SCK_Message * +receive_messages(int sock_fd, int flags, int max_messages, int *num_messages) +{ + struct MessageHeader *hdr; + SCK_Message *messages; + unsigned int i, n, n_ok; + int ret, recv_flags = 0; + + assert(initialised); + + *num_messages = 0; + + if (max_messages < 1) + return NULL; + + /* Prepare used buffers for new messages */ + prepare_buffers(received_messages); + received_messages = 0; + + messages = ARR_GetElements(recv_sck_messages); + + hdr = ARR_GetElements(recv_headers); + n = ARR_GetSize(recv_headers); + n = MIN(n, max_messages); + + if (n < 1 || n > MAX_RECV_MESSAGES || + n > ARR_GetSize(recv_messages) || n > ARR_GetSize(recv_sck_messages)) + assert(0); + + recv_flags = get_recv_flags(flags); + +#ifdef HAVE_RECVMMSG + ret = recvmmsg(sock_fd, hdr, n, recv_flags, NULL); + if (ret >= 0) + n = ret; +#else + n = 1; + ret = recvmsg(sock_fd, &hdr[0].msg_hdr, recv_flags); + if (ret >= 0) + hdr[0].msg_len = ret; +#endif + + if (ret < 0) { + handle_recv_error(sock_fd, flags); + return NULL; + } + + received_messages = n; + + for (i = n_ok = 0; i < n; i++) { + hdr = ARR_GetElement(recv_headers, i); + if (!process_header(&hdr->msg_hdr, hdr->msg_len, sock_fd, flags, &messages[n_ok])) + continue; + + log_message(sock_fd, 1, &messages[n_ok], + flags & SCK_FLAG_MSG_ERRQUEUE ? "Received error" : "Received", NULL); + + n_ok++; + } + + *num_messages = n_ok; + + return n_ok > 0 ? messages : NULL; +} + +/* ================================================== */ + +static void * +add_control_message(struct msghdr *msg, int level, int type, size_t length, size_t buf_length) +{ + struct cmsghdr *cmsg; + size_t cmsg_space; + + /* Avoid using CMSG_NXTHDR as the one in glibc does not support adding + control messages: https://sourceware.org/bugzilla/show_bug.cgi?id=13500 */ + + cmsg = msg->msg_control; + cmsg_space = CMSG_SPACE(length); + + if (!cmsg || length > buf_length || msg->msg_controllen + cmsg_space > buf_length) { + DEBUG_LOG("Could not add control message level=%d type=%d", level, type); + return NULL; + } + + cmsg = (struct cmsghdr *)((char *)cmsg + msg->msg_controllen); + + memset(cmsg, 0, cmsg_space); + + cmsg->cmsg_level = level; + cmsg->cmsg_type = type; + cmsg->cmsg_len = CMSG_LEN(length); + + msg->msg_controllen += cmsg_space; + + return CMSG_DATA(cmsg); +} + +/* ================================================== */ + +static int +send_message(int sock_fd, SCK_Message *message, int flags) +{ + struct cmsghdr cmsg_buf[CMSG_BUF_SIZE / sizeof (struct cmsghdr)]; + union sockaddr_all saddr; + socklen_t saddr_len; + struct msghdr msg; + struct iovec iov; + + switch (message->addr_type) { + case SCK_ADDR_UNSPEC: + saddr_len = 0; + break; + case SCK_ADDR_IP: + saddr_len = SCK_IPSockAddrToSockaddr(&message->remote_addr.ip, + (struct sockaddr *)&saddr, sizeof (saddr)); + break; + case SCK_ADDR_UNIX: + memset(&saddr, 0, sizeof (saddr)); + if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", + message->remote_addr.path) >= sizeof (saddr.un.sun_path)) { + DEBUG_LOG("Unix socket path %s too long", message->remote_addr.path); + return 0; + } + saddr.un.sun_family = AF_UNIX; + saddr_len = sizeof (saddr.un); + break; + default: + assert(0); + } + + if (saddr_len) { + msg.msg_name = &saddr.un; + msg.msg_namelen = saddr_len; + } else { + msg.msg_name = NULL; + msg.msg_namelen = 0; + } + + if (message->length < 0) { + DEBUG_LOG("Invalid length %d", message->length); + return 0; + } + + iov.iov_base = message->data; + iov.iov_len = message->length; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsg_buf; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + if (message->addr_type == SCK_ADDR_IP) { + if (message->local_addr.ip.family == IPADDR_INET4) { +#ifdef HAVE_IN_PKTINFO + struct in_pktinfo *ipi; + + ipi = add_control_message(&msg, IPPROTO_IP, IP_PKTINFO, sizeof (*ipi), + sizeof (cmsg_buf)); + if (!ipi) + return 0; + + ipi->ipi_spec_dst.s_addr = htonl(message->local_addr.ip.addr.in4); + if (message->if_index != INVALID_IF_INDEX) + ipi->ipi_ifindex = message->if_index; + +#elif defined(IP_SENDSRCADDR) + struct in_addr *addr; + + addr = add_control_message(&msg, IPPROTO_IP, IP_SENDSRCADDR, sizeof (*addr), + sizeof (cmsg_buf)); + if (!addr) + return 0; + + addr->s_addr = htonl(message->local_addr.ip.addr.in4); +#endif + } + +#ifdef HAVE_IN6_PKTINFO + if (message->local_addr.ip.family == IPADDR_INET6) { + struct in6_pktinfo *ipi; + + ipi = add_control_message(&msg, IPPROTO_IPV6, IPV6_PKTINFO, sizeof (*ipi), + sizeof (cmsg_buf)); + if (!ipi) + return 0; + + memcpy(&ipi->ipi6_addr.s6_addr, &message->local_addr.ip.addr.in6, + sizeof(ipi->ipi6_addr.s6_addr)); + if (message->if_index != INVALID_IF_INDEX) + ipi->ipi6_ifindex = message->if_index; + } +#endif + } + +#ifdef HAVE_LINUX_TIMESTAMPING + if (message->timestamp.tx_flags) { + int *ts_tx_flags; + + /* Set timestamping flags for this message */ + + ts_tx_flags = add_control_message(&msg, SOL_SOCKET, SO_TIMESTAMPING, + sizeof (*ts_tx_flags), sizeof (cmsg_buf)); + if (!ts_tx_flags) + return 0; + + *ts_tx_flags = message->timestamp.tx_flags; + } +#endif + + if (flags & SCK_FLAG_MSG_DESCRIPTOR) { + int *fd; + + fd = add_control_message(&msg, SOL_SOCKET, SCM_RIGHTS, sizeof (*fd), sizeof (cmsg_buf)); + if (!fd) + return 0; + + *fd = message->descriptor; + } + + /* This is apparently required on some systems */ + if (msg.msg_controllen == 0) + msg.msg_control = NULL; + + if (sendmsg(sock_fd, &msg, 0) < 0) { + log_message(sock_fd, -1, message, "Could not send", strerror(errno)); + return 0; + } + + log_message(sock_fd, -1, message, "Sent", NULL); + + return 1; +} + +/* ================================================== */ + +void +SCK_Initialise(int family) +{ + ip4_enabled = family == IPADDR_INET4 || family == IPADDR_UNSPEC; +#ifdef FEAT_IPV6 + ip6_enabled = family == IPADDR_INET6 || family == IPADDR_UNSPEC; +#else + ip6_enabled = 0; +#endif + + recv_messages = ARR_CreateInstance(sizeof (struct Message)); + ARR_SetSize(recv_messages, MAX_RECV_MESSAGES); + recv_headers = ARR_CreateInstance(sizeof (struct MessageHeader)); + ARR_SetSize(recv_headers, MAX_RECV_MESSAGES); + recv_sck_messages = ARR_CreateInstance(sizeof (SCK_Message)); + ARR_SetSize(recv_sck_messages, MAX_RECV_MESSAGES); + + received_messages = MAX_RECV_MESSAGES; + + priv_bind_function = NULL; + + supported_socket_flags = 0; +#ifdef SOCK_CLOEXEC + if (check_socket_flag(SOCK_CLOEXEC, FD_CLOEXEC, 0)) + supported_socket_flags |= SOCK_CLOEXEC; +#endif +#ifdef SOCK_NONBLOCK + if (check_socket_flag(SOCK_NONBLOCK, 0, O_NONBLOCK)) + supported_socket_flags |= SOCK_NONBLOCK; +#endif + + initialised = 1; +} + +/* ================================================== */ + +void +SCK_Finalise(void) +{ + ARR_DestroyInstance(recv_sck_messages); + ARR_DestroyInstance(recv_headers); + ARR_DestroyInstance(recv_messages); + + initialised = 0; +} + +/* ================================================== */ + +int +SCK_IsIpFamilyEnabled(int family) +{ + switch (family) { + case IPADDR_INET4: + return ip4_enabled; + case IPADDR_INET6: + return ip6_enabled; + default: + return 0; + } +} + +/* ================================================== */ + +void +SCK_GetAnyLocalIPAddress(int family, IPAddr *local_addr) +{ + local_addr->family = family; + + switch (family) { + case IPADDR_INET4: + local_addr->addr.in4 = INADDR_ANY; + break; + case IPADDR_INET6: +#ifdef FEAT_IPV6 + memcpy(&local_addr->addr.in6, &in6addr_any, sizeof (local_addr->addr.in6)); +#else + memset(&local_addr->addr.in6, 0, sizeof (local_addr->addr.in6)); +#endif + break; + } +} + +/* ================================================== */ + +void +SCK_GetLoopbackIPAddress(int family, IPAddr *local_addr) +{ + local_addr->family = family; + + switch (family) { + case IPADDR_INET4: + local_addr->addr.in4 = INADDR_LOOPBACK; + break; + case IPADDR_INET6: +#ifdef FEAT_IPV6 + memcpy(&local_addr->addr.in6, &in6addr_loopback, sizeof (local_addr->addr.in6)); +#else + memset(&local_addr->addr.in6, 0, sizeof (local_addr->addr.in6)); + local_addr->addr.in6[15] = 1; +#endif + break; + } +} + +/* ================================================== */ + +int +SCK_IsLinkLocalIPAddress(IPAddr *addr) +{ + switch (addr->family) { + case IPADDR_INET4: + /* 169.254.0.0/16 */ + return (addr->addr.in4 & 0xffff0000) == 0xa9fe0000; + case IPADDR_INET6: + /* fe80::/10 */ + return addr->addr.in6[0] == 0xfe && (addr->addr.in6[1] & 0xc0) == 0x80; + default: + return 0; + } +} + +/* ================================================== */ + +void +SCK_SetPrivBind(int (*function)(int sock_fd, struct sockaddr *address, + socklen_t address_len)) +{ + priv_bind_function = function; +} + +/* ================================================== */ + +int +SCK_OpenUdpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *iface, int flags) +{ + return open_ip_socket(remote_addr, local_addr, iface, SOCK_DGRAM, flags); +} + +/* ================================================== */ + +int +SCK_OpenTcpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *iface, int flags) +{ + return open_ip_socket(remote_addr, local_addr, iface, SOCK_STREAM, flags); +} + +/* ================================================== */ + +int +SCK_OpenUnixDatagramSocket(const char *remote_addr, const char *local_addr, int flags) +{ + return open_unix_socket(remote_addr, local_addr, SOCK_DGRAM, flags); +} + +/* ================================================== */ + +int +SCK_OpenUnixStreamSocket(const char *remote_addr, const char *local_addr, int flags) +{ + return open_unix_socket(remote_addr, local_addr, SOCK_STREAM, flags); +} + +/* ================================================== */ + +int +SCK_OpenUnixSocketPair(int flags, int *other_fd) +{ + int sock_fd; + + /* Prefer SEQPACKET sockets over DGRAM in order to receive a zero-length + message (end of file) when the other end is unexpectedly closed */ + if ( +#ifdef SOCK_SEQPACKET + (sock_fd = open_unix_socket_pair(SOCK_SEQPACKET, flags, other_fd)) < 0 && +#endif + (sock_fd = open_unix_socket_pair(SOCK_DGRAM, flags, other_fd)) < 0) + return INVALID_SOCK_FD; + + return sock_fd; +} + +/* ================================================== */ + +int +SCK_SetIntOption(int sock_fd, int level, int name, int value) +{ + if (setsockopt(sock_fd, level, name, &value, sizeof (value)) < 0) { + DEBUG_LOG("setsockopt() failed fd=%d level=%d name=%d value=%d : %s", + sock_fd, level, name, value, strerror(errno)); + return 0; + } + + return 1; +} + +/* ================================================== */ + +int +SCK_GetIntOption(int sock_fd, int level, int name, int *value) +{ + socklen_t len = sizeof (*value); + + if (getsockopt(sock_fd, level, name, value, &len) < 0) { + DEBUG_LOG("getsockopt() failed fd=%d level=%d name=%d : %s", + sock_fd, level, name, strerror(errno)); + return 0; + } + + return 1; +} + +/* ================================================== */ + +int +SCK_EnableKernelRxTimestamping(int sock_fd) +{ +#ifdef SO_TIMESTAMPNS + if (SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_TIMESTAMPNS, 1)) + return 1; +#endif +#ifdef SO_TIMESTAMP + if (SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_TIMESTAMP, 1)) + return 1; +#endif + + return 0; +} + +/* ================================================== */ + +int +SCK_ListenOnSocket(int sock_fd, int backlog) +{ + if (listen(sock_fd, backlog) < 0) { + DEBUG_LOG("listen() failed : %s", strerror(errno)); + return 0; + } + + return 1; +} + +/* ================================================== */ + +int +SCK_AcceptConnection(int sock_fd, IPSockAddr *remote_addr) +{ + union sockaddr_all saddr; + socklen_t saddr_len = sizeof (saddr); + int conn_fd; + + conn_fd = accept(sock_fd, &saddr.sa, &saddr_len); + if (conn_fd < 0) { + DEBUG_LOG("accept() failed : %s", strerror(errno)); + return INVALID_SOCK_FD; + } + + if (!UTI_FdSetCloexec(conn_fd) || !set_socket_nonblock(conn_fd)) { + close(conn_fd); + return INVALID_SOCK_FD; + } + + SCK_SockaddrToIPSockAddr(&saddr.sa, saddr_len, remote_addr); + + return conn_fd; +} + +/* ================================================== */ + +int +SCK_ShutdownConnection(int sock_fd) +{ + if (shutdown(sock_fd, SHUT_RDWR) < 0) { + DEBUG_LOG("shutdown() failed : %s", strerror(errno)); + return 0; + } + + return 1; +} + +/* ================================================== */ + +int +SCK_Receive(int sock_fd, void *buffer, int length, int flags) +{ + int r; + + if (length < 0) { + DEBUG_LOG("Invalid length %d", length); + return -1; + } + + r = recv(sock_fd, buffer, length, get_recv_flags(flags)); + + if (r < 0) { + handle_recv_error(sock_fd, flags); + return r; + } + + DEBUG_LOG("Received data fd=%d len=%d", sock_fd, r); + + return r; +} + +/* ================================================== */ + +int +SCK_Send(int sock_fd, const void *buffer, int length, int flags) +{ + int r; + + assert(flags == 0); + + if (length < 0) { + DEBUG_LOG("Invalid length %d", length); + return -1; + } + + r = send(sock_fd, buffer, length, 0); + + if (r < 0) { + DEBUG_LOG("Could not send data fd=%d len=%d : %s", sock_fd, length, strerror(errno)); + return r; + } + + DEBUG_LOG("Sent data fd=%d len=%d", sock_fd, r); + + return r; +} + +/* ================================================== */ + +SCK_Message * +SCK_ReceiveMessage(int sock_fd, int flags) +{ + int num_messages; + + return receive_messages(sock_fd, flags, 1, &num_messages); +} + +/* ================================================== */ + +SCK_Message * +SCK_ReceiveMessages(int sock_fd, int flags, int *num_messages) +{ + return receive_messages(sock_fd, flags, MAX_RECV_MESSAGES, num_messages); +} + +/* ================================================== */ + +void +SCK_InitMessage(SCK_Message *message, SCK_AddressType addr_type) +{ + init_message_addresses(message, addr_type); + init_message_nonaddress(message); +} + +/* ================================================== */ + +int +SCK_SendMessage(int sock_fd, SCK_Message *message, int flags) +{ + return send_message(sock_fd, message, flags); +} + +/* ================================================== */ + +int +SCK_RemoveSocket(int sock_fd) +{ + union sockaddr_all saddr; + socklen_t saddr_len; + + saddr_len = sizeof (saddr); + + if (getsockname(sock_fd, &saddr.sa, &saddr_len) < 0) { + DEBUG_LOG("getsockname() failed : %s", strerror(errno)); + return 0; + } + + if (saddr_len > sizeof (saddr) || saddr_len <= sizeof (saddr.sa.sa_family) || + saddr.sa.sa_family != AF_UNIX) + return 0; + + if (unlink(saddr.un.sun_path) < 0) { + DEBUG_LOG("Could not remove %s : %s", saddr.un.sun_path, strerror(errno)); + return 0; + } + + return 1; +} + +/* ================================================== */ + +void +SCK_CloseSocket(int sock_fd) +{ + close(sock_fd); +} + +/* ================================================== */ + +void +SCK_SockaddrToIPSockAddr(struct sockaddr *sa, int sa_length, IPSockAddr *ip_sa) +{ + ip_sa->ip_addr.family = IPADDR_UNSPEC; + ip_sa->port = 0; + + switch (sa->sa_family) { + case AF_INET: + if (sa_length < (int)sizeof (struct sockaddr_in)) + return; + ip_sa->ip_addr.family = IPADDR_INET4; + ip_sa->ip_addr.addr.in4 = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr); + ip_sa->port = ntohs(((struct sockaddr_in *)sa)->sin_port); + break; +#ifdef FEAT_IPV6 + case AF_INET6: + if (sa_length < (int)sizeof (struct sockaddr_in6)) + return; + ip_sa->ip_addr.family = IPADDR_INET6; + memcpy(&ip_sa->ip_addr.addr.in6, ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr, + sizeof (ip_sa->ip_addr.addr.in6)); + ip_sa->port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port); + break; +#endif + default: + break; + } +} + +/* ================================================== */ + +int +SCK_IPSockAddrToSockaddr(IPSockAddr *ip_sa, struct sockaddr *sa, int sa_length) +{ + switch (ip_sa->ip_addr.family) { + case IPADDR_INET4: + if (sa_length < (int)sizeof (struct sockaddr_in)) + return 0; + memset(sa, 0, sizeof (struct sockaddr_in)); + sa->sa_family = AF_INET; + ((struct sockaddr_in *)sa)->sin_addr.s_addr = htonl(ip_sa->ip_addr.addr.in4); + ((struct sockaddr_in *)sa)->sin_port = htons(ip_sa->port); +#ifdef SIN6_LEN + ((struct sockaddr_in *)sa)->sin_len = sizeof (struct sockaddr_in); +#endif + return sizeof (struct sockaddr_in); +#ifdef FEAT_IPV6 + case IPADDR_INET6: + if (sa_length < (int)sizeof (struct sockaddr_in6)) + return 0; + memset(sa, 0, sizeof (struct sockaddr_in6)); + sa->sa_family = AF_INET6; + memcpy(&((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr, ip_sa->ip_addr.addr.in6, + sizeof (((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr)); + ((struct sockaddr_in6 *)sa)->sin6_port = htons(ip_sa->port); +#ifdef SIN6_LEN + ((struct sockaddr_in6 *)sa)->sin6_len = sizeof (struct sockaddr_in6); +#endif + return sizeof (struct sockaddr_in6); +#endif + default: + if (sa_length < (int)sizeof (struct sockaddr)) + return 0; + memset(sa, 0, sizeof (struct sockaddr)); + sa->sa_family = AF_UNSPEC; + return 0; + } +} diff -Nru chrony-3.5/socket.h chrony-4.1/socket.h --- chrony-3.5/socket.h 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/socket.h 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,147 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + This is the header file for socket operations. + + */ + +#ifndef GOT_SOCKET_H +#define GOT_SOCKET_H + +#include "addressing.h" + +/* Flags for opening sockets */ +#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 + +/* Flags for receiving and sending messages */ +#define SCK_FLAG_MSG_ERRQUEUE 1 +#define SCK_FLAG_MSG_DESCRIPTOR 2 + +typedef enum { + SCK_ADDR_UNSPEC = 0, + SCK_ADDR_IP, + SCK_ADDR_UNIX +} SCK_AddressType; + +typedef struct { + void *data; + int length; + SCK_AddressType addr_type; + int if_index; + + union { + IPSockAddr ip; + const char *path; + } remote_addr; + + union { + IPAddr ip; + } local_addr; + + struct { + struct timespec kernel; + struct timespec hw; + int if_index; + int l2_length; + int tx_flags; + } timestamp; + + int descriptor; +} SCK_Message; + +/* Initialisation function (the specified IP family is enabled, + or all if IPADDR_UNSPEC) */ +extern void SCK_Initialise(int family); + +/* Finalisation function */ +extern void SCK_Finalise(void); + +/* Check if support for the IP family is enabled */ +extern int SCK_IsIpFamilyEnabled(int family); + +/* Get the 0.0.0.0/::0 or 127.0.0.1/::1 address */ +extern void SCK_GetAnyLocalIPAddress(int family, IPAddr *local_addr); +extern void SCK_GetLoopbackIPAddress(int family, IPAddr *local_addr); + +/* Check if an IP address is a link-local address */ +extern int SCK_IsLinkLocalIPAddress(IPAddr *addr); + +/* Specify a bind()-like function for binding sockets to privileged ports when + running in a restricted process (e.g. after dropping root privileges) */ +extern void SCK_SetPrivBind(int (*function)(int sock_fd, struct sockaddr *address, + socklen_t address_len)); + +/* Open a socket (addresses and iface may be NULL) */ +extern int SCK_OpenUdpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, + const char *iface, int flags); +extern int SCK_OpenTcpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, + const char *iface, int flags); +extern int SCK_OpenUnixDatagramSocket(const char *remote_addr, const char *local_addr, + int flags); +extern int SCK_OpenUnixStreamSocket(const char *remote_addr, const char *local_addr, + int flags); +extern int SCK_OpenUnixSocketPair(int flags, int *other_fd); + +/* Set and get a socket option of int size */ +extern int SCK_SetIntOption(int sock_fd, int level, int name, int value); +extern int SCK_GetIntOption(int sock_fd, int level, int name, int *value); + +/* Enable RX timestamping socket option */ +extern int SCK_EnableKernelRxTimestamping(int sock_fd); + +/* Operate on a stream socket - listen()/accept()/shutdown() wrappers */ +extern int SCK_ListenOnSocket(int sock_fd, int backlog); +extern int SCK_AcceptConnection(int sock_fd, IPSockAddr *remote_addr); +extern int SCK_ShutdownConnection(int sock_fd); + +/* Receive and send data on connected sockets - recv()/send() wrappers */ +extern int SCK_Receive(int sock_fd, void *buffer, int length, int flags); +extern int SCK_Send(int sock_fd, const void *buffer, int length, int flags); + +/* Receive a single message or multiple messages. The functions return + a pointer to static buffers, or NULL on error. The buffers are valid until + another call of the functions and can be reused for sending messages. */ +extern SCK_Message *SCK_ReceiveMessage(int sock_fd, int flags); +extern SCK_Message *SCK_ReceiveMessages(int sock_fd, int flags, int *num_messages); + +/* Initialise a new message (e.g. before sending) */ +extern void SCK_InitMessage(SCK_Message *message, SCK_AddressType addr_type); + +/* Send a message */ +extern int SCK_SendMessage(int sock_fd, SCK_Message *message, int flags); + +/* Remove bound Unix socket */ +extern int SCK_RemoveSocket(int sock_fd); + +/* Close the socket */ +extern void SCK_CloseSocket(int sock_fd); + +/* Convert between IPSockAddr and sockaddr_in/in6 */ +extern void SCK_SockaddrToIPSockAddr(struct sockaddr *sa, int sa_length, IPSockAddr *ip_sa); +extern int SCK_IPSockAddrToSockaddr(IPSockAddr *ip_sa, struct sockaddr *sa, int sa_length); + +#endif diff -Nru chrony-3.5/sources.c chrony-4.1/sources.c --- chrony-3.5/sources.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sources.c 2021-05-12 11:06:15.000000000 +0000 @@ -3,7 +3,7 @@ ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 - * Copyright (C) Miroslav Lichvar 2011-2016, 2018 + * Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020 * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -54,8 +54,6 @@ /* ================================================== */ /* Structure used to hold info for selecting between sources */ struct SelectInfo { - NTP_Leap leap; - int stratum; int select_ok; double std_dev; double root_distance; @@ -119,13 +117,28 @@ /* Type of the source */ SRC_Type type; - /* Options used when selecting sources */ + /* Flag indicating that the source is authenticated */ + int authenticated; + + /* Configured selection options */ + int conf_sel_options; + + /* Effective selection options */ int sel_options; /* Score against currently selected source */ double sel_score; struct SelectInfo sel_info; + + /* Current stratum */ + int stratum; + + /* Current leap status */ + NTP_Leap leap; + + /* Flag indicating the source has a leap second vote */ + int leap_vote; }; /* ================================================== */ @@ -164,16 +177,17 @@ static double stratum_weight; static double combine_limit; +/* Identifier of the dump file */ +#define DUMP_IDENTIFIER "SRC0\n" + /* ================================================== */ /* Forward prototype */ -static void -slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq, - double doffset, LCL_ChangeType change_type, void *anything); -static void -add_dispersion(double dispersion, void *anything); -static char * -source_to_string(SRC_Instance inst); +static void update_sel_options(void); +static void slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq, + double doffset, LCL_ChangeType change_type, void *anything); +static void add_dispersion(double dispersion, void *anything); +static char *source_to_string(SRC_Instance inst); /* ================================================== */ /* Initialisation function */ @@ -213,9 +227,9 @@ /* Function to create a new instance. This would be called by one of the individual source-type instance creation routines. */ -SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options, - IPAddr *addr, int min_samples, int max_samples, - double min_delay, double asymmetry) +SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int authenticated, + int sel_options, IPAddr *addr, int min_samples, + int max_samples, double min_delay, double asymmetry) { SRC_Instance result; @@ -248,13 +262,18 @@ result->index = n_sources; result->type = type; + result->authenticated = authenticated; + result->conf_sel_options = sel_options; result->sel_options = sel_options; + result->active = 0; SRC_SetRefid(result, ref_id, addr); SRC_ResetInstance(result); n_sources++; + update_sel_options(); + return result; } @@ -279,6 +298,8 @@ --n_sources; Free(instance); + update_sel_options(); + /* If this was the previous reference source, we have to reselect! */ if (selected_source_index == dead_index) SRC_ReselectSource(); @@ -291,13 +312,17 @@ void SRC_ResetInstance(SRC_Instance instance) { - instance->active = 0; instance->updates = 0; instance->reachability = 0; instance->reachability_size = 0; instance->distant = 0; instance->status = SRC_BAD_STATS; instance->sel_score = 1.0; + instance->stratum = 0; + instance->leap = LEAP_Unsynchronised; + instance->leap_vote = 0; + + memset(&instance->sel_info, 0, sizeof (instance->sel_info)); SST_ResetInstance(instance->stats); } @@ -323,6 +348,50 @@ /* ================================================== */ +static NTP_Leap +get_leap_status(void) +{ + int i, leap_votes, leap_ins, leap_del; + + /* Accept a leap second if more than half of the sources with a vote agree */ + + for (i = leap_ins = leap_del = leap_votes = 0; i < n_sources; i++) { + if (!sources[i]->leap_vote) + continue; + + leap_votes++; + if (sources[i]->leap == LEAP_InsertSecond) + leap_ins++; + else if (sources[i]->leap == LEAP_DeleteSecond) + leap_del++; + } + + if (leap_ins > leap_votes / 2) + return LEAP_InsertSecond; + else if (leap_del > leap_votes / 2) + return LEAP_DeleteSecond; + else + return LEAP_Normal; +} + +/* ================================================== */ + +void +SRC_UpdateStatus(SRC_Instance inst, int stratum, NTP_Leap leap) +{ + inst->stratum = stratum; + + if (REF_IsLeapSecondClose(NULL, 0.0)) + return; + + inst->leap = leap; + + if (inst->leap_vote) + REF_UpdateLeapStatus(get_leap_status()); +} + +/* ================================================== */ + /* This function is called by one of the source drivers when it has a new sample that is to be accumulated. @@ -337,11 +406,11 @@ assert(initialised); - DEBUG_LOG("ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d", + DEBUG_LOG("src=%s ts=%s offset=%e delay=%e disp=%e", source_to_string(inst), UTI_TimespecToString(&sample->time), -sample->offset, - sample->root_delay, sample->root_dispersion, sample->stratum); + sample->root_delay, sample->root_dispersion); - if (REF_IsLeapSecondClose()) { + if (REF_IsLeapSecondClose(&sample->time, sample->offset)) { LOG(LOGS_INFO, "Dropping sample around leap second"); return; } @@ -378,7 +447,7 @@ if (!sources[i]->active) continue; - /* Don't expect more updates than from an offline iburst NTP source */ + /* Don't expect more updates than the initial burst of an NTP source */ if (sources[i]->reachability_size >= SOURCE_REACH_BITS - 1) continue; @@ -434,7 +503,76 @@ /* ================================================== */ static void -log_selection_message(char *format, char *arg) +update_sel_options(void) +{ + int options, auth_ntp_options, unauth_ntp_options, refclk_options; + int i, auth_ntp_sources, unauth_ntp_sources; + + auth_ntp_sources = unauth_ntp_sources = 0; + + for (i = 0; i < n_sources; i++) { + if (sources[i]->conf_sel_options & SRC_SELECT_NOSELECT) + continue; + if (sources[i]->type != SRC_NTP) + continue; + if (sources[i]->authenticated) + auth_ntp_sources++; + else + unauth_ntp_sources++; + } + + auth_ntp_options = unauth_ntp_options = refclk_options = 0; + + /* Determine which selection options need to be added to authenticated NTP + sources, unauthenticated NTP sources, and refclocks, to follow the + configured selection mode */ + switch (CNF_GetAuthSelectMode()) { + case SRC_AUTHSELECT_IGNORE: + break; + case SRC_AUTHSELECT_MIX: + if (auth_ntp_sources > 0 && unauth_ntp_sources > 0) + auth_ntp_options = refclk_options = SRC_SELECT_REQUIRE | SRC_SELECT_TRUST; + break; + case SRC_AUTHSELECT_PREFER: + if (auth_ntp_sources > 0) + unauth_ntp_options = SRC_SELECT_NOSELECT; + break; + case SRC_AUTHSELECT_REQUIRE: + unauth_ntp_options = SRC_SELECT_NOSELECT; + break; + default: + assert(0); + } + + for (i = 0; i < n_sources; i++) { + options = sources[i]->conf_sel_options; + + if (options & SRC_SELECT_NOSELECT) + continue; + + switch (sources[i]->type) { + case SRC_NTP: + options |= sources[i]->authenticated ? auth_ntp_options : unauth_ntp_options; + break; + case SRC_REFCLOCK: + options |= refclk_options; + break; + default: + assert(0); + } + + if (sources[i]->sel_options != options) { + DEBUG_LOG("changing %s from %x to %x", source_to_string(sources[i]), + (unsigned int)sources[i]->sel_options, (unsigned int)options); + sources[i]->sel_options = options; + } + } +} + +/* ================================================== */ + +static void +log_selection_message(const char *format, const char *arg) { if (REF_GetMode() != REF_ModeNormal) return; @@ -443,6 +581,24 @@ /* ================================================== */ +static void +log_selection_source(const char *format, SRC_Instance inst) +{ + char buf[320], *name, *ntp_name; + + name = source_to_string(inst); + ntp_name = inst->type == SRC_NTP ? NSR_GetName(inst->ip_addr) : NULL; + + if (ntp_name && strcmp(name, ntp_name) != 0) + snprintf(buf, sizeof (buf), "%s (%s)", name, ntp_name); + else + snprintf(buf, sizeof (buf), "%s", name); + + log_selection_message(format, buf); +} + +/* ================================================== */ + static int compare_sort_elements(const void *a, const void *b) { @@ -481,6 +637,20 @@ /* ================================================== */ static void +mark_source(SRC_Instance inst, SRC_Status status) +{ + inst->status = status; + + DEBUG_LOG("%s status=%d options=%x reach=%o/%d updates=%d distant=%d leap=%d vote=%d lo=%f hi=%f", + source_to_string(inst), (int)inst->status, (unsigned int)inst->sel_options, + (unsigned int)inst->reachability, inst->reachability_size, inst->updates, + inst->distant, (int)inst->leap, inst->leap_vote, + inst->sel_info.lo_limit, inst->sel_info.hi_limit); +} + +/* ================================================== */ + +static void mark_ok_sources(SRC_Status status) { int i; @@ -488,7 +658,7 @@ for (i = 0; i < n_sources; i++) { if (sources[i]->status != SRC_OK) continue; - sources[i]->status = status; + mark_source(sources[i], status); } } @@ -539,12 +709,12 @@ } if (sources[index]->distant) { - sources[index]->status = SRC_DISTANT; + mark_source(sources[index], SRC_DISTANT); continue; } if (sources[index]->status == SRC_OK) - sources[index]->status = SRC_UNSELECTED; + mark_source(sources[index], SRC_UNSELECTED); elapsed = UTI_DiffTimespecsToDouble(ref_time, &src_ref_time); src_offset += elapsed * src_frequency; @@ -593,12 +763,13 @@ struct timespec now, ref_time; int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources, sel_req_source; int n_badstats_sources, max_sel_reach, max_sel_reach_size, max_badstat_reach; - int depth, best_depth, trust_depth, best_trust_depth; + int depth, best_depth, trust_depth, best_trust_depth, n_sel_trust_sources; int combined, stratum, min_stratum, max_score_index; - int orphan_stratum, orphan_source, leap_votes, leap_ins, leap_del; + int orphan_stratum, orphan_source; double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew; double src_root_delay, src_root_dispersion; double best_lo, best_hi, distance, sel_src_distance, max_score; + double best_trust_lo, best_trust_hi; double first_sample_ago, max_reach_sample_ago; NTP_Leap leap_status; @@ -620,7 +791,7 @@ /* Step 1 - build intervals about each source */ n_endpoints = 0; - n_sel_sources = 0; + n_sel_sources = n_sel_trust_sources = 0; n_badstats_sources = 0; sel_req_source = 0; max_sel_reach = max_badstat_reach = 0; @@ -630,6 +801,9 @@ for (i = 0; i < n_sources; i++) { assert(sources[i]->status != SRC_OK); + /* Don't allow the source to vote on leap seconds unless it's selectable */ + sources[i]->leap_vote = 0; + /* If some sources are specified with the require option, at least one of them will have to be selectable in order to update the clock */ if (sources[i]->sel_options & SRC_SELECT_REQUIRE) @@ -637,19 +811,19 @@ /* Ignore sources which were added with the noselect option */ if (sources[i]->sel_options & SRC_SELECT_NOSELECT) { - sources[i]->status = SRC_UNSELECTABLE; + mark_source(sources[i], SRC_UNSELECTABLE); continue; } si = &sources[i]->sel_info; - SST_GetSelectionData(sources[i]->stats, &now, &si->stratum, &si->leap, + SST_GetSelectionData(sources[i]->stats, &now, &si->lo_limit, &si->hi_limit, &si->root_distance, &si->std_dev, &first_sample_ago, &si->last_sample_ago, &si->select_ok); if (!si->select_ok) { ++n_badstats_sources; - sources[i]->status = SRC_BAD_STATS; + mark_source(sources[i], SRC_BAD_STATS); if (max_badstat_reach < sources[i]->reachability) max_badstat_reach = sources[i]->reachability; continue; @@ -665,15 +839,16 @@ si->hi_limit += extra_disp; } - /* Require the root distance to be below the allowed maximum */ - if (si->root_distance > max_distance) { - sources[i]->status = SRC_BAD_DISTANCE; + /* Require the root distance to be below the allowed maximum and the + endpoints to be in the right order (i.e. a non-negative distance) */ + if (!(si->root_distance <= max_distance && si->lo_limit <= si->hi_limit)) { + mark_source(sources[i], SRC_BAD_DISTANCE); continue; } /* And the same applies for the estimated standard deviation */ if (si->std_dev > max_jitter) { - sources[i]->status = SRC_JITTERY; + mark_source(sources[i], SRC_JITTERY); continue; } @@ -702,7 +877,7 @@ can still be selected if its newest sample is not older than the oldest sample from reachable sources. */ if (!sources[i]->reachability && max_reach_sample_ago < si->last_sample_ago) { - sources[i]->status = SRC_STALE; + mark_source(sources[i], SRC_STALE); continue; } @@ -723,10 +898,10 @@ source can settle down to a state where only one server is serving its local unsychronised time and others are synchronised to it. */ - if (si->stratum >= orphan_stratum && sources[i]->type == SRC_NTP) { - sources[i]->status = SRC_ORPHAN; + if (sources[i]->stratum >= orphan_stratum && sources[i]->type == SRC_NTP) { + mark_source(sources[i], SRC_ORPHAN); - if (si->stratum == orphan_stratum && sources[i]->reachability && + if (sources[i]->stratum == orphan_stratum && sources[i]->reachability && (orphan_source == INVALID_SOURCE || sources[i]->ref_id < sources[orphan_source]->ref_id)) orphan_source = i; @@ -754,6 +929,9 @@ if (sources[i]->status != SRC_OK) continue; + if (sources[i]->sel_options & SRC_SELECT_TRUST) + n_sel_trust_sources++; + si = &sources[i]->sel_info; j1 = n_endpoints; @@ -770,7 +948,7 @@ n_endpoints += 2; } - DEBUG_LOG("badstat=%d sel=%d badstat_reach=%x sel_reach=%x size=%d max_reach_ago=%f", + DEBUG_LOG("badstat=%d sel=%d badstat_reach=%o sel_reach=%o size=%d max_reach_ago=%f", n_badstats_sources, n_sel_sources, (unsigned int)max_badstat_reach, (unsigned int)max_sel_reach, max_sel_reach_size, max_reach_sample_ago); @@ -826,7 +1004,7 @@ trust_depth = best_trust_depth = 0; depth = best_depth = 0; - best_lo = best_hi = 0.0; + best_lo = best_hi = best_trust_lo = best_trust_hi = 0.0; for (i = 0; i < n_endpoints; i++) { switch (sort_list[i].tag) { @@ -836,14 +1014,20 @@ trust_depth++; if (trust_depth > best_trust_depth || (trust_depth == best_trust_depth && depth > best_depth)) { - best_trust_depth = trust_depth; + if (trust_depth > best_trust_depth) { + best_trust_depth = trust_depth; + best_trust_lo = sort_list[i].offset; + } best_depth = depth; best_lo = sort_list[i].offset; } break; case HIGH: - if (trust_depth == best_trust_depth && depth == best_depth) - best_hi = sort_list[i].offset; + if (trust_depth == best_trust_depth) { + if (depth == best_depth) + best_hi = sort_list[i].offset; + best_trust_hi = sort_list[i].offset; + } if (sources[sort_list[i].index]->sel_options & SRC_SELECT_TRUST) trust_depth--; depth--; @@ -851,11 +1035,16 @@ default: assert(0); } + assert(trust_depth <= depth); + assert(trust_depth >= 0); } - if (best_depth <= n_sel_sources / 2 && !best_trust_depth) { - /* Could not even get half the reachable sources to agree and there - are no trusted sources - clearly we can't synchronise */ + assert(depth == 0 && trust_depth == 0); + assert(2 * n_sel_sources == n_endpoints); + + if ((best_trust_depth == 0 && best_depth <= n_sel_sources / 2) || + (best_trust_depth > 0 && best_trust_depth <= n_sel_trust_sources / 2)) { + /* Could not even get half the reachable (trusted) sources to agree */ if (selected_source_index != INVALID_SOURCE) { log_selection_message("Can't synchronise: no majority", NULL); @@ -882,24 +1071,28 @@ continue; /* Check if source's interval contains the best interval, or is wholly - contained within it. If there are any trusted sources the first - condition is applied only to them to not allow non-trusted sources to - move the final offset outside the interval. */ - if (((!best_trust_depth || sources[i]->sel_options & SRC_SELECT_TRUST) && - sources[i]->sel_info.lo_limit <= best_lo && + contained within it. If there are any trusted sources, other sources + are required to be wholly contained within the best interval of the + trusted sources to not allow non-trusted sources to move the final + offset outside the trusted interval. */ + if ((sources[i]->sel_info.lo_limit <= best_lo && sources[i]->sel_info.hi_limit >= best_hi) || (sources[i]->sel_info.lo_limit >= best_lo && sources[i]->sel_info.hi_limit <= best_hi)) { + if (!(best_trust_depth == 0 || (sources[i]->sel_options & SRC_SELECT_TRUST) || + (sources[i]->sel_info.lo_limit >= best_trust_lo && + sources[i]->sel_info.hi_limit <= best_trust_hi))) { + mark_source(sources[i], SRC_UNTRUSTED); + continue; + } + sel_sources[n_sel_sources++] = i; if (sources[i]->sel_options & SRC_SELECT_REQUIRE) sel_req_source = 0; - } else if (sources[i]->sel_info.lo_limit <= best_lo && - sources[i]->sel_info.hi_limit >= best_hi) { - sources[i]->status = SRC_UNTRUSTED; } else { - sources[i]->status = SRC_FALSETICKER; + mark_source(sources[i], SRC_FALSETICKER); } } @@ -914,26 +1107,15 @@ return; } - /* Accept leap second status if more than half of selectable (and trusted - if there are any) sources agree */ - for (i = leap_ins = leap_del = leap_votes = 0; i < n_sel_sources; i++) { + /* Enable the selectable sources (and trusted if there are any) to + vote on leap seconds */ + for (i = 0; i < n_sel_sources; i++) { index = sel_sources[i]; if (best_trust_depth && !(sources[index]->sel_options & SRC_SELECT_TRUST)) continue; - leap_votes++; - if (sources[index]->sel_info.leap == LEAP_InsertSecond) - leap_ins++; - else if (sources[index]->sel_info.leap == LEAP_DeleteSecond) - leap_del++; + sources[index]->leap_vote = 1; } - if (leap_ins > leap_votes / 2) - leap_status = LEAP_InsertSecond; - else if (leap_del > leap_votes / 2) - leap_status = LEAP_DeleteSecond; - else - leap_status = LEAP_Normal; - /* If there are any sources with prefer option, reduce the list again only to the preferred sources */ for (i = 0; i < n_sel_sources; i++) { @@ -943,7 +1125,7 @@ if (i < n_sel_sources) { for (i = j = 0; i < n_sel_sources; i++) { if (!(sources[sel_sources[i]]->sel_options & SRC_SELECT_PREFER)) - sources[sel_sources[i]]->status = SRC_NONPREFERRED; + mark_source(sources[sel_sources[i]], SRC_NONPREFERRED); else sel_sources[j++] = sel_sources[i]; } @@ -957,10 +1139,10 @@ /* Find minimum stratum */ index = sel_sources[0]; - min_stratum = sources[index]->sel_info.stratum; + min_stratum = sources[index]->stratum; for (i = 1; i < n_sel_sources; i++) { index = sel_sources[i]; - stratum = sources[index]->sel_info.stratum; + stratum = sources[index]->stratum; if (stratum < min_stratum) min_stratum = stratum; } @@ -973,7 +1155,7 @@ if (selected_source_index != INVALID_SOURCE) sel_src_distance = sources[selected_source_index]->sel_info.root_distance + - (sources[selected_source_index]->sel_info.stratum - min_stratum) * stratum_weight; + (sources[selected_source_index]->stratum - min_stratum) * stratum_weight; for (i = 0; i < n_sources; i++) { /* Reset score for non-selectable sources */ @@ -985,7 +1167,7 @@ } distance = sources[i]->sel_info.root_distance + - (sources[i]->sel_info.stratum - min_stratum) * stratum_weight; + (sources[i]->stratum - min_stratum) * stratum_weight; if (sources[i]->type == SRC_NTP) distance += reselect_distance; @@ -1007,10 +1189,8 @@ sources[i]->sel_score = 1.0 / distance; } - DEBUG_LOG("select score=%f refid=%"PRIx32" match_refid=%"PRIx32" status=%u dist=%f", - sources[i]->sel_score, sources[i]->ref_id, - updated_inst ? updated_inst->ref_id : 0, - sources[i]->status, distance); + DEBUG_LOG("%s score=%f dist=%f", + source_to_string(sources[i]), sources[i]->sel_score, distance); if (max_score < sources[i]->sel_score) { max_score = sources[i]->sel_score; @@ -1031,13 +1211,11 @@ if (sources[max_score_index]->updates == 0) { selected_source_index = INVALID_SOURCE; mark_ok_sources(SRC_WAITS_UPDATE); - DEBUG_LOG("best source has no updates"); return; } selected_source_index = max_score_index; - log_selection_message("Selected source %s", - source_to_string(sources[selected_source_index])); + log_selection_source("Selected source %s", sources[selected_source_index]); /* New source has been selected, reset all scores */ for (i = 0; i < n_sources; i++) { @@ -1046,7 +1224,7 @@ } } - sources[selected_source_index]->status = SRC_SELECTED; + mark_source(sources[selected_source_index], SRC_SELECTED); /* Don't update reference when the selected source has no new samples */ @@ -1056,8 +1234,7 @@ for (i = 0; i < n_sel_sources; i++) { index = sel_sources[i]; if (sources[index]->status == SRC_OK) - sources[index]->status = sources[index]->distant ? - SRC_DISTANT : SRC_UNSELECTED; + mark_source(sources[index], sources[index]->distant ? SRC_DISTANT : SRC_UNSELECTED); } return; } @@ -1065,6 +1242,8 @@ for (i = 0; i < n_sources; i++) sources[i]->updates = 0; + leap_status = get_leap_status(); + /* Now just use the statistics of the selected source combined with the other selectable sources for trimming the local clock */ @@ -1076,7 +1255,7 @@ combined = combine_sources(n_sel_sources, &ref_time, &src_offset, &src_offset_sd, &src_frequency, &src_frequency_sd, &src_skew); - REF_SetReference(sources[selected_source_index]->sel_info.stratum, + REF_SetReference(sources[selected_source_index]->stratum, leap_status, combined, sources[selected_source_index]->ref_id, sources[selected_source_index]->ip_addr, @@ -1128,7 +1307,7 @@ } if (change_type == LCL_ChangeUnknownStep) { - /* After resetting no source is selectable, set reference unsynchronised */ + /* Update selection status */ SRC_SelectSource(NULL); } } @@ -1149,35 +1328,60 @@ /* ================================================== */ -static -FILE *open_dumpfile(SRC_Instance inst, const char *mode) +static int +get_dumpfile(SRC_Instance inst, char *filename, size_t len) +{ + /* Use the IP address, or reference ID with reference clocks */ + switch (inst->type) { + case SRC_NTP: + if (!UTI_IsIPReal(inst->ip_addr) || + snprintf(filename, len, "%s", source_to_string(inst)) >= len) + return 0; + break; + case SRC_REFCLOCK: + if (snprintf(filename, len, "refid:%08"PRIx32, inst->ref_id) >= len) + return 0; + break; + default: + assert(0); + } + + return 1; +} + +/* ================================================== */ + +static void +save_source(SRC_Instance inst) { + char filename[64], *dumpdir, *ntp_name; FILE *f; - char filename[1024], *dumpdir; dumpdir = CNF_GetDumpDir(); - if (dumpdir[0] == '\0') { - LOG(LOGS_WARN, "dumpdir not specified"); - return NULL; - } - - /* Include IP address in the name for NTP sources, or reference ID in hex */ - if ((inst->type == SRC_NTP && - snprintf(filename, sizeof (filename), "%s/%s.dat", dumpdir, - source_to_string(inst)) >= sizeof (filename)) || - (inst->type != SRC_NTP && - snprintf(filename, sizeof (filename), "%s/refid:%08"PRIx32".dat", - dumpdir, inst->ref_id) >= sizeof (filename))) { - LOG(LOGS_WARN, "dumpdir too long"); - return NULL; - } - - f = fopen(filename, mode); - if (!f && mode[0] != 'r') - LOG(LOGS_WARN, "Could not open dump file for %s", - source_to_string(inst)); + if (!dumpdir) + return; + + if (!get_dumpfile(inst, filename, sizeof (filename))) + return; + + f = UTI_OpenFile(dumpdir, filename, ".dat", 'w', 0644); + if (!f) + return; + + ntp_name = inst->type == SRC_NTP ? NSR_GetName(inst->ip_addr) : "."; - return f; + if (fprintf(f, "%s%s\n%d %o %d %d %d\n", + DUMP_IDENTIFIER, ntp_name, inst->authenticated, + (unsigned int)inst->reachability, inst->reachability_size, + inst->stratum, (int)inst->leap) < 0 || + !SST_SaveToFile(inst->stats, f)) { + fclose(f); + if (!UTI_RemoveFile(dumpdir, filename, ".dat")) + ; + return; + } + + fclose(f); } /* ================================================== */ @@ -1186,16 +1390,60 @@ void SRC_DumpSources(void) { - FILE *out; int i; - for (i = 0; i < n_sources; i++) { - out = open_dumpfile(sources[i], "w"); - if (!out) - continue; - SST_SaveToFile(sources[i]->stats, out); - fclose(out); + for (i = 0; i < n_sources; i++) + save_source(sources[i]); +} + +/* ================================================== */ + +#define MAX_WORDS 1 + +static void +load_source(SRC_Instance inst) +{ + char filename[64], line[256], *dumpdir, *ntp_name, *words[MAX_WORDS]; + int auth, leap, reach_size, stratum; + unsigned int reach; + FILE *f; + + dumpdir = CNF_GetDumpDir(); + if (!dumpdir) + return; + + if (!get_dumpfile(inst, filename, sizeof (filename))) + return; + + f = UTI_OpenFile(dumpdir, filename, ".dat", 'r', 0); + if (!f) + return; + + ntp_name = inst->type == SRC_NTP ? NSR_GetName(inst->ip_addr) : NULL; + + if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 || + !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 || + (inst->type == SRC_NTP && (!ntp_name || strcmp(words[0], ntp_name) != 0)) || + !fgets(line, sizeof (line), f) || + sscanf(words[0], "%d %o %d %d %d", + &auth, &reach, &reach_size, &stratum, &leap) != 5 || + (!auth && inst->authenticated) || + stratum < 0 || stratum >= NTP_MAX_STRATUM || + leap < LEAP_Normal || leap >= LEAP_Unsynchronised || + !SST_LoadFromFile(inst->stats, f)) { + LOG(LOGS_WARN, "Could not load dump file for %s", source_to_string(inst)); + fclose(f); + return; } + + inst->reachability = reach & ((1U << SOURCE_REACH_BITS) - 1); + inst->reachability_size = CLAMP(0, reach_size, SOURCE_REACH_BITS); + inst->stratum = stratum; + inst->leap = leap; + + LOG(LOGS_INFO, "Loaded dump file for %s", source_to_string(inst)); + + fclose(f); } /* ================================================== */ @@ -1203,21 +1451,17 @@ void SRC_ReloadSources(void) { - FILE *in; int i; for (i = 0; i < n_sources; i++) { - in = open_dumpfile(sources[i], "r"); - if (!in) - continue; - if (!SST_LoadFromFile(sources[i]->stats, in)) - LOG(LOGS_WARN, "Could not load dump file for %s", - source_to_string(sources[i])); - else - LOG(LOGS_INFO, "Loaded dump file for %s", - source_to_string(sources[i])); - fclose(in); + load_source(sources[i]); + + /* Allow an immediate update of the reference */ + sources[i]->updates++; } + + /* Select sources and set the reference */ + SRC_SelectSource(NULL); } /* ================================================== */ @@ -1225,13 +1469,13 @@ void SRC_RemoveDumpFiles(void) { - char pattern[1024], name[64], *dumpdir, *s; + char pattern[PATH_MAX], name[64], *dumpdir, *s; IPAddr ip_addr; glob_t gl; size_t i; dumpdir = CNF_GetDumpDir(); - if (dumpdir[0] == '\0' || + if (!dumpdir || snprintf(pattern, sizeof (pattern), "%s/*.dat", dumpdir) >= sizeof (pattern)) return; @@ -1252,8 +1496,8 @@ if (strncmp(name, "refid:", 6) && !UTI_StringToIP(name, &ip_addr)) continue; - DEBUG_LOG("Removing %s", gl.gl_pathv[i]); - unlink(gl.gl_pathv[i]); + if (!UTI_RemoveFile(NULL, gl.gl_pathv[i], NULL)) + ; } globfree(&gl); @@ -1261,6 +1505,17 @@ /* ================================================== */ +void +SRC_ResetSources(void) +{ + int i; + + for (i = 0; i < n_sources; i++) + SRC_ResetInstance(sources[i]); +} + +/* ================================================== */ + int SRC_IsSyncPeer(SRC_Instance inst) { @@ -1321,6 +1576,8 @@ report->ip_addr.family = IPADDR_INET4; } + report->stratum = src->stratum; + switch (src->status) { case SRC_FALSETICKER: report->state = RPT_FALSETICKER; @@ -1328,26 +1585,24 @@ case SRC_JITTERY: report->state = RPT_JITTERY; break; - case SRC_UNTRUSTED: case SRC_WAITS_SOURCES: case SRC_NONPREFERRED: case SRC_WAITS_UPDATE: case SRC_DISTANT: case SRC_OUTLIER: - report->state = RPT_OUTLIER; + report->state = RPT_SELECTABLE; break; case SRC_UNSELECTED: - report->state = RPT_CANDIDATE; + report->state = RPT_UNSELECTED; break; case SRC_SELECTED: - report->state = RPT_SYNC; + report->state = RPT_SELECTED; break; default: - report->state = RPT_UNREACH; + report->state = RPT_NONSELECTABLE; break; } - report->sel_options = src->sel_options; report->reachability = src->reachability; /* Call stats module to fill out estimates */ @@ -1380,6 +1635,79 @@ } /* ================================================== */ + +static char +get_status_char(SRC_Status status) +{ + switch (status) { + case SRC_UNSELECTABLE: + return 'N'; + case SRC_BAD_STATS: + return 'M'; + case SRC_BAD_DISTANCE: + return 'd'; + case SRC_JITTERY: + return '~'; + case SRC_WAITS_STATS: + return 'w'; + case SRC_STALE: + return 'S'; + case SRC_ORPHAN: + return 'O'; + case SRC_UNTRUSTED: + return 'T'; + case SRC_FALSETICKER: + return 'x'; + case SRC_WAITS_SOURCES: + return 'W'; + case SRC_NONPREFERRED: + return 'P'; + case SRC_WAITS_UPDATE: + return 'U'; + case SRC_DISTANT: + return 'D'; + case SRC_OUTLIER: + return 'L'; + case SRC_UNSELECTED: + return '+'; + case SRC_SELECTED: + return '*'; + default: + return '?'; + } +} + +/* ================================================== */ + +int +SRC_GetSelectReport(int index, RPT_SelectReport *report) +{ + SRC_Instance inst; + + if (index >= n_sources || index < 0) + return 0; + + inst = sources[index]; + + report->ref_id = inst->ref_id; + if (inst->ip_addr) + report->ip_addr = *inst->ip_addr; + else + report->ip_addr.family = IPADDR_UNSPEC; + report->state_char = get_status_char(inst->status); + report->authentication = inst->authenticated; + report->leap = inst->leap; + report->conf_options = inst->conf_sel_options; + report->eff_options = inst->sel_options; + report->last_sample_ago = inst->sel_info.last_sample_ago; + report->score = inst->sel_score; + report->lo_limit = inst->sel_info.lo_limit; + report->hi_limit = inst->sel_info.hi_limit; + + return 1; +} + +/* ================================================== */ SRC_Type SRC_GetType(int index) diff -Nru chrony-3.5/sources.h chrony-4.1/sources.h --- chrony-3.5/sources.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sources.h 2021-05-12 11:06:15.000000000 +0000 @@ -51,6 +51,14 @@ /* Finalisation function */ extern void SRC_Finalise(void); +/* Modes for selecting NTP sources based on their authentication status */ +typedef enum { + SRC_AUTHSELECT_IGNORE, + SRC_AUTHSELECT_MIX, + SRC_AUTHSELECT_PREFER, + SRC_AUTHSELECT_REQUIRE, +} SRC_AuthSelectMode; + typedef enum { SRC_NTP, /* NTP client/peer */ SRC_REFCLOCK /* Rerefence clock */ @@ -59,9 +67,9 @@ /* Function to create a new instance. This would be called by one of the individual source-type instance creation routines. */ -extern SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options, - IPAddr *addr, int min_samples, int max_samples, - double min_delay, double asymmetry); +extern SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int authenticated, + int sel_options, IPAddr *addr, int min_samples, + int max_samples, double min_delay, double asymmetry); /* Function to get rid of a source when it is being unconfigured. This may cause the current reference source to be reselected, if this @@ -79,8 +87,10 @@ /* Function to get access to the sourcestats instance */ extern SST_Stats SRC_GetSourcestats(SRC_Instance instance); -/* This function is called by one of the source drivers when it has - a new sample that is to be accumulated */ +/* Function to update the stratum and leap status of the source */ +extern void SRC_UpdateStatus(SRC_Instance instance, int stratum, NTP_Leap leap); + +/* Function to accumulate a new sample from the source */ extern void SRC_AccumulateSample(SRC_Instance instance, NTP_Sample *sample); /* This routine sets the source as receiving reachability updates */ @@ -114,13 +124,16 @@ extern void SRC_ReloadSources(void); extern void SRC_RemoveDumpFiles(void); +extern void SRC_ResetSources(void); + extern int SRC_IsSyncPeer(SRC_Instance inst); extern int SRC_IsReachable(SRC_Instance inst); extern int SRC_ReadNumberOfSources(void); extern int SRC_ActiveSources(void); -extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now); +extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now); extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timespec *now); +extern int SRC_GetSelectReport(int index, RPT_SelectReport *report); extern SRC_Type SRC_GetType(int index); diff -Nru chrony-3.5/sourcestats.c chrony-4.1/sourcestats.c --- chrony-3.5/sourcestats.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sourcestats.c 2021-05-12 11:06:15.000000000 +0000 @@ -3,7 +3,7 @@ ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 - * Copyright (C) Miroslav Lichvar 2011-2014, 2016-2018 + * Copyright (C) Miroslav Lichvar 2011-2014, 2016-2018, 2021 * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -54,6 +54,9 @@ /* The minimum standard deviation */ #define MIN_STDDEV 1.0e-9 +/* The worst case bound on an unknown standard deviation of the offset */ +#define WORST_CASE_STDDEV_BOUND 4.0 + /* The asymmetry of network jitter when all jitter is in one direction */ #define MAX_ASYMMETRY 0.5 @@ -174,12 +177,6 @@ /* This array contains the root dispersions of each sample at the time of the measurements */ double root_dispersions[MAX_SAMPLES]; - - /* The stratum from the last accumulated sample */ - int stratum; - - /* The leap status from the last accumulated sample */ - NTP_Leap leap; }; /* ================================================== */ @@ -249,13 +246,12 @@ inst->estimated_frequency_sd = WORST_CASE_FREQ_BOUND; inst->skew = WORST_CASE_FREQ_BOUND; inst->estimated_offset = 0.0; - inst->estimated_offset_sd = 86400.0; /* Assume it's at least within a day! */ + inst->estimated_offset_sd = WORST_CASE_STDDEV_BOUND; UTI_ZeroTimespec(&inst->offset_time); - inst->std_dev = 4.0; + inst->std_dev = WORST_CASE_STDDEV_BOUND; inst->nruns = 0; inst->asymmetry_run = 0; inst->asymmetry = 0.0; - inst->leap = LEAP_Unsynchronised; } /* ================================================== */ @@ -322,8 +318,6 @@ inst->peer_dispersions[m] = sample->peer_dispersion; inst->root_delays[m] = sample->root_delay; inst->root_dispersions[m] = sample->root_dispersion; - inst->stratum = sample->stratum; - inst->leap = sample->leap; if (inst->peer_delays[n] < inst->fixed_min_delay) inst->peer_delays[n] = 2.0 * inst->fixed_min_delay - inst->peer_delays[n]; @@ -603,9 +597,20 @@ times_back_start = inst->runs_samples + best_start; prune_register(inst, best_start); } else { - inst->estimated_frequency = 0.0; inst->estimated_frequency_sd = WORST_CASE_FREQ_BOUND; inst->skew = WORST_CASE_FREQ_BOUND; + inst->estimated_offset_sd = WORST_CASE_STDDEV_BOUND; + inst->std_dev = WORST_CASE_STDDEV_BOUND; + inst->nruns = 0; + + if (inst->n_samples > 0) { + inst->estimated_offset = inst->offsets[inst->last_sample]; + inst->offset_time = inst->sample_times[inst->last_sample]; + } else { + inst->estimated_offset = 0.0; + UTI_ZeroTimespec(&inst->offset_time); + } + times_back_start = 0; } @@ -641,7 +646,6 @@ void SST_GetSelectionData(SST_Stats inst, struct timespec *now, - int *stratum, NTP_Leap *leap, double *offset_lo_limit, double *offset_hi_limit, double *root_distance, @@ -661,8 +665,6 @@ i = get_runsbuf_index(inst, inst->best_single_sample); j = get_buf_index(inst, inst->best_single_sample); - *stratum = inst->stratum; - *leap = inst->leap; *std_dev = inst->std_dev; sample_elapsed = fabs(UTI_DiffTimespecsToDouble(now, &inst->sample_times[i])); @@ -694,6 +696,13 @@ *select_ok = inst->regression_ok; + /* If maxsamples is too small to have a successful regression, enable the + selection as a special case for a fast update/print-once reference mode */ + if (!*select_ok && inst->n_samples < 3 && inst->n_samples == inst->max_samples) { + *std_dev = CNF_GetMaxJitter(); + *select_ok = 1; + } + DEBUG_LOG("n=%d off=%f dist=%f sd=%f first_ago=%f last_ago=%f selok=%d", inst->n_samples, offset, *root_distance, *std_dev, *first_sample_ago, *last_sample_ago, *select_ok); @@ -843,38 +852,30 @@ /* This is used to save the register to a file, so that we can reload it after restarting the daemon */ -void +int SST_SaveToFile(SST_Stats inst, FILE *out) { int m, i, j; - fprintf(out, "%d\n", inst->n_samples); + if (inst->n_samples < 1) + return 0; + + if (fprintf(out, "%d %d\n", inst->n_samples, inst->asymmetry_run) < 0) + return 0; for(m = 0; m < inst->n_samples; m++) { i = get_runsbuf_index(inst, m); j = get_buf_index(inst, m); - fprintf(out, -#ifdef HAVE_LONG_TIME_T - "%08"PRIx64" %08lx %.6e %.6e %.6e %.6e %.6e %.6e %.6e %d\n", - (uint64_t)inst->sample_times[i].tv_sec, -#else - "%08lx %08lx %.6e %.6e %.6e %.6e %.6e %.6e %.6e %d\n", - (unsigned long)inst->sample_times[i].tv_sec, -#endif - (unsigned long)inst->sample_times[i].tv_nsec / 1000, - inst->offsets[i], - inst->orig_offsets[j], - inst->peer_delays[i], - inst->peer_dispersions[j], - inst->root_delays[j], - inst->root_dispersions[j], - 1.0, /* used to be inst->weights[i] */ - inst->stratum /* used to be an array */); - + if (fprintf(out, "%s %.6e %.6e %.6e %.6e %.6e %.6e\n", + UTI_TimespecToString(&inst->sample_times[i]), + inst->offsets[i], inst->orig_offsets[j], + inst->peer_delays[i], inst->peer_dispersions[j], + inst->root_delays[j], inst->root_dispersions[j]) < 0) + return 0; } - fprintf(out, "%d\n", inst->asymmetry_run); + return 1; } /* ================================================== */ @@ -883,65 +884,46 @@ int SST_LoadFromFile(SST_Stats inst, FILE *in) { -#ifdef HAVE_LONG_TIME_T - uint64_t sec; -#else - unsigned long sec; -#endif - unsigned long usec; - int i; - char line[1024]; - double weight; + int i, n_samples, arun; + struct timespec now; + double sample_time; + char line[256]; + + if (!fgets(line, sizeof (line), in) || + sscanf(line, "%d %d", &n_samples, &arun) != 2 || + n_samples < 1 || n_samples > MAX_SAMPLES) + return 0; SST_ResetInstance(inst); - if (fgets(line, sizeof(line), in) && - sscanf(line, "%d", &inst->n_samples) == 1 && - inst->n_samples >= 0 && inst->n_samples <= MAX_SAMPLES) { - - for (i=0; in_samples; i++) { - if (!fgets(line, sizeof(line), in) || - (sscanf(line, -#ifdef HAVE_LONG_TIME_T - "%"SCNx64"%lx%lf%lf%lf%lf%lf%lf%lf%d\n", -#else - "%lx%lx%lf%lf%lf%lf%lf%lf%lf%d\n", -#endif - &(sec), &(usec), - &(inst->offsets[i]), - &(inst->orig_offsets[i]), - &(inst->peer_delays[i]), - &(inst->peer_dispersions[i]), - &(inst->root_delays[i]), - &(inst->root_dispersions[i]), - &weight, /* not used anymore */ - &inst->stratum) != 10)) { - - /* This is the branch taken if the read FAILED */ - - inst->n_samples = 0; /* Load abandoned if any sign of corruption */ - return 0; - } else { - - /* This is the branch taken if the read is SUCCESSFUL */ - inst->sample_times[i].tv_sec = sec; - inst->sample_times[i].tv_nsec = 1000 * usec; - UTI_NormaliseTimespec(&inst->sample_times[i]); - } - } + LCL_ReadCookedTime(&now, NULL); - /* This field was not saved in older versions */ - if (!fgets(line, sizeof(line), in) || sscanf(line, "%d\n", &inst->asymmetry_run) != 1) - inst->asymmetry_run = 0; - } else { - inst->n_samples = 0; /* Load abandoned if any sign of corruption */ - return 0; + for (i = 0; i < n_samples; i++) { + if (!fgets(line, sizeof (line), in) || + sscanf(line, "%lf %lf %lf %lf %lf %lf %lf", + &sample_time, &inst->offsets[i], &inst->orig_offsets[i], + &inst->peer_delays[i], &inst->peer_dispersions[i], + &inst->root_delays[i], &inst->root_dispersions[i]) != 7) + return 0; + + if (!UTI_IsTimeOffsetSane(&now, sample_time - UTI_TimespecToDouble(&now))) + return 0; + + /* Some resolution is lost in the double format, but that's ok */ + UTI_DoubleToTimespec(sample_time, &inst->sample_times[i]); + + /* Make sure the samples are sane and they are in order */ + if (!UTI_IsTimeOffsetSane(&inst->sample_times[i], -inst->offsets[i]) || + !(fabs(inst->peer_delays[i]) < 1.0e6 && fabs(inst->peer_dispersions[i]) < 1.0e6 && + fabs(inst->root_delays[i]) < 1.0e6 && fabs(inst->root_dispersions[i]) < 1.0e6) || + (i > 0 && UTI_CompareTimespecs(&inst->sample_times[i], + &inst->sample_times[i - 1]) <= 0)) + return 0; } - if (!inst->n_samples) - return 1; - + inst->n_samples = n_samples; inst->last_sample = inst->n_samples - 1; + inst->asymmetry_run = CLAMP(-MAX_ASYMMETRY_RUN, arun, MAX_ASYMMETRY_RUN); find_min_delay_sample(inst); SST_DoNewRegression(inst); @@ -963,7 +945,6 @@ report->orig_latest_meas = inst->orig_offsets[j]; report->latest_meas = inst->offsets[i]; report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j]; - report->stratum = inst->stratum; /* Align the sample time to reduce the leak of the receive timestamp */ last_sample_time = inst->sample_times[i]; @@ -993,31 +974,24 @@ { double dspan; double elapsed, sample_elapsed; - int li, lj, bi, bj; + int bi, bj; report->n_samples = inst->n_samples; report->n_runs = inst->nruns; - if (inst->n_samples > 1) { - li = get_runsbuf_index(inst, inst->n_samples - 1); - lj = get_buf_index(inst, inst->n_samples - 1); - dspan = UTI_DiffTimespecsToDouble(&inst->sample_times[li], - &inst->sample_times[get_runsbuf_index(inst, 0)]); - report->span_seconds = (unsigned long) (dspan + 0.5); - - if (inst->n_samples > 3) { - elapsed = UTI_DiffTimespecsToDouble(now, &inst->offset_time); - bi = get_runsbuf_index(inst, inst->best_single_sample); - bj = get_buf_index(inst, inst->best_single_sample); - sample_elapsed = UTI_DiffTimespecsToDouble(now, &inst->sample_times[bi]); - report->est_offset = inst->estimated_offset + elapsed * inst->estimated_frequency; - report->est_offset_err = (inst->estimated_offset_sd + - sample_elapsed * inst->skew + - (0.5*inst->root_delays[bj] + inst->root_dispersions[bj])); - } else { - report->est_offset = inst->offsets[li]; - report->est_offset_err = 0.5*inst->root_delays[lj] + inst->root_dispersions[lj]; - } + if (inst->n_samples > 0) { + bi = get_runsbuf_index(inst, inst->best_single_sample); + bj = get_buf_index(inst, inst->best_single_sample); + + dspan = UTI_DiffTimespecsToDouble(&inst->sample_times[inst->last_sample], + &inst->sample_times[get_runsbuf_index(inst, 0)]); + elapsed = UTI_DiffTimespecsToDouble(now, &inst->offset_time); + sample_elapsed = UTI_DiffTimespecsToDouble(now, &inst->sample_times[bi]); + + report->span_seconds = round(dspan); + report->est_offset = inst->estimated_offset + elapsed * inst->estimated_frequency; + report->est_offset_err = inst->estimated_offset_sd + sample_elapsed * inst->skew + + (0.5 * inst->root_delays[bj] + inst->root_dispersions[bj]); } else { report->span_seconds = 0; report->est_offset = 0; diff -Nru chrony-3.5/sourcestats.h chrony-4.1/sourcestats.h --- chrony-3.5/sourcestats.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sourcestats.h 2021-05-12 11:06:15.000000000 +0000 @@ -69,7 +69,6 @@ /* Get data needed for selection */ extern void SST_GetSelectionData(SST_Stats inst, struct timespec *now, - int *stratum, NTP_Leap *leap, double *offset_lo_limit, double *offset_hi_limit, double *root_distance, @@ -120,7 +119,7 @@ double *last_sample_ago, double *predicted_offset, double *min_delay, double *skew, double *std_dev); -extern void SST_SaveToFile(SST_Stats inst, FILE *out); +extern int SST_SaveToFile(SST_Stats inst, FILE *out); extern int SST_LoadFromFile(SST_Stats inst, FILE *in); diff -Nru chrony-3.5/srcparams.h chrony-4.1/srcparams.h --- chrony-3.5/srcparams.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/srcparams.h 2021-05-12 11:06:15.000000000 +0000 @@ -52,7 +52,11 @@ int filter_length; int interleaved; int sel_options; + int nts; + int nts_port; + int copy; uint32_t authkey; + uint32_t cert_set; double max_delay; double max_delay_ratio; double max_delay_dev_ratio; @@ -74,6 +78,8 @@ #define SRC_DEFAULT_MINSAMPLES (-1) #define SRC_DEFAULT_MAXSAMPLES (-1) #define SRC_DEFAULT_ASYMMETRY 1.0 +#define SRC_DEFAULT_NTSPORT 4460 +#define SRC_DEFAULT_CERTSET 0 #define INACTIVE_AUTHKEY 0 /* Flags for source selection */ diff -Nru chrony-3.5/stubs.c chrony-4.1/stubs.c --- chrony-3.5/stubs.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/stubs.c 2021-05-12 11:06:15.000000000 +0000 @@ -28,6 +28,7 @@ #include "config.h" #include "clientlog.h" +#include "cmac.h" #include "cmdmon.h" #include "keys.h" #include "logging.h" @@ -39,12 +40,16 @@ #include "ntp_io.h" #include "ntp_sources.h" #include "ntp_signd.h" +#include "nts_ke_client.h" +#include "nts_ke_server.h" +#include "nts_ntp_client.h" +#include "nts_ntp_server.h" #include "privops.h" #include "refclock.h" #include "sched.h" #include "util.h" -#ifndef FEAT_ASYNCDNS +#if defined(FEAT_NTP) && !defined(FEAT_ASYNCDNS) /* This is a blocking implementation used when asynchronous resolving is not available */ @@ -107,7 +112,7 @@ #ifndef FEAT_CMDMON void -CAM_Initialise(int family) +CAM_Initialise(void) { } @@ -142,7 +147,7 @@ #ifndef FEAT_NTP void -NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval) +NCR_AddBroadcastDestination(NTP_Remote_Address *addr, int interval) { } @@ -169,7 +174,7 @@ } void -NIO_Initialise(int family) +NIO_Initialise(void) { } @@ -189,23 +194,31 @@ } NSR_Status -NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params) +NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, + SourceParameters *params, uint32_t *conf_id) { return NSR_TooManySources; } -void -NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, SourceParameters *params) +NSR_Status +NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, + SourceParameters *params, uint32_t *conf_id) { + return NSR_TooManySources; } NSR_Status -NSR_RemoveSource(NTP_Remote_Address *remote_addr) +NSR_RemoveSource(IPAddr *address) { return NSR_NoSuchSource; } void +NSR_RemoveSourcesById(uint32_t conf_id) +{ +} + +void NSR_RemoveAllSources(void) { } @@ -220,6 +233,12 @@ { } +char * +NSR_GetName(IPAddr *address) +{ + return NULL; +} + void NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler) { @@ -308,6 +327,12 @@ } int +NSR_GetAuthReport(IPAddr *address, RPT_AuthReport *report) +{ + return 0; +} + +int NSR_GetNTPReport(RPT_NTPReport *report) { return 0; @@ -319,6 +344,11 @@ memset(report, 0, sizeof (*report)); } +void +NSR_DumpAuthData(void) +{ +} + #ifndef FEAT_CMDMON void @@ -398,15 +428,140 @@ } int -NSD_GetAuthDelay(uint32_t key_id) +NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info, + NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr) { return 0; } +#endif /* !FEAT_SIGND */ + +#ifndef HAVE_CMAC + int -NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length) +CMC_GetKeyLength(CMC_Algorithm algorithm) { return 0; } -#endif /* !FEAT_SIGND */ +CMC_Instance +CMC_CreateInstance(CMC_Algorithm algorithm, const unsigned char *key, int length) +{ + return NULL; +} + +int +CMC_Hash(CMC_Instance inst, const void *in, int in_len, unsigned char *out, int out_len) +{ + return 0; +} + +void +CMC_DestroyInstance(CMC_Instance inst) +{ +} + +#endif /* !HAVE_CMAC */ + +#ifndef FEAT_NTS + +void +NNS_Initialise(void) +{ +} + +void +NNS_Finalise(void) +{ +} + +int +NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod) +{ + *kod = 0; + return 0; +} + +int +NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info, + NTP_Packet *response, NTP_PacketInfo *res_info, + uint32_t kod) +{ + return 0; +} + +NNC_Instance +NNC_CreateInstance(IPSockAddr *nts_address, const char *name, uint32_t cert_set, + uint16_t ntp_port) +{ + return NULL; +} + +void +NNC_DestroyInstance(NNC_Instance inst) +{ +} + +int +NNC_PrepareForAuth(NNC_Instance inst) +{ + return 1; +} + +int +NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info) +{ + static int logged = 0; + + LOG(logged ? LOGS_DEBUG : LOGS_WARN, "Missing NTS support"); + logged = 1; + return 0; +} + +int +NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info) +{ + return 0; +} + +void +NNC_ChangeAddress(NNC_Instance inst, IPAddr *address) +{ +} + +void +NNC_DumpData(NNC_Instance inst) +{ +} + +void +NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report) +{ +} + +void +NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level) +{ +} + +void +NKS_Initialise(void) +{ +} + +void +NKS_Finalise(void) +{ +} + +void +NKS_DumpKeys(void) +{ +} + +void +NKS_ReloadKeys(void) +{ +} + +#endif /* !FEAT_NTS */ diff -Nru chrony-3.5/sys.c chrony-4.1/sys.c --- chrony-3.5/sys.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sys.c 2021-05-12 11:06:15.000000000 +0000 @@ -97,16 +97,16 @@ /* ================================================== */ -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) - SYS_Linux_DropRoot(uid, gid, !null_driver); + SYS_Linux_DropRoot(uid, gid, context, !null_driver); #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) - SYS_NetBSD_DropRoot(uid, gid); + SYS_NetBSD_DropRoot(uid, gid, context, !null_driver); #elif defined(MACOSX) && defined(FEAT_PRIVDROP) - SYS_MacOSX_DropRoot(uid, gid); + SYS_MacOSX_DropRoot(uid, gid, context); #else LOG_FATAL("dropping root privileges not supported"); #endif @@ -114,10 +114,10 @@ /* ================================================== */ -void SYS_EnableSystemCallFilter(int level) +void SYS_EnableSystemCallFilter(int level, SYS_ProcessContext context) { #if defined(LINUX) && defined(FEAT_SCFILTER) - SYS_Linux_EnableSystemCallFilter(level); + SYS_Linux_EnableSystemCallFilter(level, context); #else LOG_FATAL("system call filter not supported"); #endif diff -Nru chrony-3.5/sys_generic.c chrony-4.1/sys_generic.c --- chrony-3.5/sys_generic.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sys_generic.c 2021-05-12 11:06:15.000000000 +0000 @@ -417,6 +417,8 @@ LCL_ReadRawTime(&now); stop_fastslew(&now); + + LCL_RemoveParameterChangeHandler(handle_step, NULL); } /* ================================================== */ diff -Nru chrony-3.5/sys.h chrony-4.1/sys.h --- chrony-3.5/sys.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sys.h 2021-05-12 11:06:15.000000000 +0000 @@ -35,12 +35,17 @@ /* Called at the end of the run to do final clean-up */ 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 { + SYS_MAIN_PROCESS, + SYS_NTSKE_HELPER, +} 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 which chronyd normally needs after initialization */ -extern void SYS_EnableSystemCallFilter(int level); +extern void SYS_EnableSystemCallFilter(int level, SYS_ProcessContext context); extern void SYS_SetScheduler(int SchedPriority); extern void SYS_LockMemory(void); diff -Nru chrony-3.5/sysincl.h chrony-4.1/sysincl.h --- chrony-3.5/sysincl.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sysincl.h 2021-05-12 11:06:15.000000000 +0000 @@ -21,14 +21,14 @@ ======================================================================= - This file includes all system header files that the software - requires. This allows us to isolate system dependencies to this file - alone. + This file includes most system header files that the software + requires to better isolate system dependencies. */ #ifndef GOT_SYSINCL_H #define GOT_SYSINCL_H +#include #include #include #include @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -54,7 +55,6 @@ #include #include #include -#include #include #include @@ -62,11 +62,6 @@ #include #endif -#ifdef FEAT_IPV6 -/* For inet_ntop() */ -#include -#endif - #ifdef HAVE_GETRANDOM #include #endif diff -Nru chrony-3.5/sys_linux.c chrony-4.1/sys_linux.c --- chrony-3.5/sys_linux.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sys_linux.c 2021-05-12 11:06:15.000000000 +0000 @@ -426,7 +426,7 @@ #ifdef FEAT_PRIVDROP 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]; cap_t cap; @@ -437,13 +437,23 @@ UTI_DropRoot(uid, gid); - /* Keep CAP_NET_BIND_SERVICE only if a server NTP port can be opened - and keep CAP_SYS_TIME only if the clock control is enabled */ - if (snprintf(cap_text, sizeof (cap_text), "%s %s", - CNF_GetNTPPort() ? "cap_net_bind_service=ep" : "", + /* Keep CAP_NET_BIND_SERVICE if the NTP server sockets may need to be bound + to a privileged port. + Keep CAP_NET_RAW if an NTP socket may need to be bound to a device on + kernels before 5.7. + Keep CAP_SYS_TIME if the clock control is enabled. */ + if (snprintf(cap_text, sizeof (cap_text), "%s %s %s", + (CNF_GetNTPPort() > 0 && CNF_GetNTPPort() < 1024) ? + "cap_net_bind_service=ep" : "", + (CNF_GetBindNtpInterface() || CNF_GetBindAcquisitionInterface()) && + !SYS_Linux_CheckKernelVersion(5, 7) ? "cap_net_raw=ep" : "", clock_control ? "cap_sys_time=ep" : "") >= sizeof (cap_text)) 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) { LOG_FATAL("cap_from_text() failed"); } @@ -474,39 +484,150 @@ /* ================================================== */ void -SYS_Linux_EnableSystemCallFilter(int level) +SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context) { - const int syscalls[] = { + const int allowed[] = { /* Clock */ - SCMP_SYS(adjtimex), SCMP_SYS(clock_gettime), SCMP_SYS(gettimeofday), - SCMP_SYS(settimeofday), SCMP_SYS(time), + SCMP_SYS(adjtimex), + SCMP_SYS(clock_adjtime), +#ifdef __NR_clock_adjtime64 + SCMP_SYS(clock_adjtime64), +#endif + SCMP_SYS(clock_gettime), +#ifdef __NR_clock_gettime64 + SCMP_SYS(clock_gettime64), +#endif + SCMP_SYS(gettimeofday), + SCMP_SYS(settimeofday), + SCMP_SYS(time), + /* Process */ - SCMP_SYS(clone), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(getpid), - SCMP_SYS(getrlimit), SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigreturn), - SCMP_SYS(rt_sigprocmask), SCMP_SYS(set_tid_address), SCMP_SYS(sigreturn), - SCMP_SYS(wait4), SCMP_SYS(waitpid), + SCMP_SYS(clone), + SCMP_SYS(exit), + SCMP_SYS(exit_group), + SCMP_SYS(getpid), + SCMP_SYS(getrlimit), + SCMP_SYS(getuid), + SCMP_SYS(getuid32), + SCMP_SYS(rt_sigaction), + SCMP_SYS(rt_sigreturn), + SCMP_SYS(rt_sigprocmask), + SCMP_SYS(set_tid_address), + SCMP_SYS(sigreturn), + SCMP_SYS(wait4), + SCMP_SYS(waitpid), + /* Memory */ - SCMP_SYS(brk), SCMP_SYS(madvise), SCMP_SYS(mmap), SCMP_SYS(mmap2), - SCMP_SYS(mprotect), SCMP_SYS(mremap), SCMP_SYS(munmap), SCMP_SYS(shmdt), + SCMP_SYS(brk), + SCMP_SYS(madvise), + SCMP_SYS(mmap), + SCMP_SYS(mmap2), + SCMP_SYS(mprotect), + SCMP_SYS(mremap), + SCMP_SYS(munmap), + SCMP_SYS(shmdt), + /* Filesystem */ - SCMP_SYS(_llseek), SCMP_SYS(access), SCMP_SYS(chmod), SCMP_SYS(chown), - SCMP_SYS(chown32), SCMP_SYS(faccessat), SCMP_SYS(fchmodat), SCMP_SYS(fchownat), - SCMP_SYS(fstat), SCMP_SYS(fstat64), SCMP_SYS(getdents), SCMP_SYS(getdents64), - SCMP_SYS(lseek), SCMP_SYS(newfstatat), SCMP_SYS(rename), SCMP_SYS(renameat), - SCMP_SYS(stat), SCMP_SYS(stat64), SCMP_SYS(statfs), SCMP_SYS(statfs64), - SCMP_SYS(unlink), SCMP_SYS(unlinkat), + SCMP_SYS(_llseek), + SCMP_SYS(access), + SCMP_SYS(chmod), + SCMP_SYS(chown), + SCMP_SYS(chown32), + SCMP_SYS(faccessat), + SCMP_SYS(fchmodat), + SCMP_SYS(fchownat), + SCMP_SYS(fstat), + SCMP_SYS(fstat64), + SCMP_SYS(fstatat64), + SCMP_SYS(getdents), + SCMP_SYS(getdents64), + SCMP_SYS(lseek), + SCMP_SYS(lstat), + SCMP_SYS(lstat64), + SCMP_SYS(newfstatat), + SCMP_SYS(readlink), + SCMP_SYS(readlinkat), + SCMP_SYS(rename), + SCMP_SYS(renameat), +#ifdef __NR_renameat2 + SCMP_SYS(renameat2), +#endif + SCMP_SYS(stat), + SCMP_SYS(stat64), + SCMP_SYS(statfs), + SCMP_SYS(statfs64), +#ifdef __NR_statx + SCMP_SYS(statx), +#endif + SCMP_SYS(unlink), + SCMP_SYS(unlinkat), + /* Socket */ - SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname), SCMP_SYS(getsockopt), - SCMP_SYS(recv), SCMP_SYS(recvfrom), SCMP_SYS(recvmmsg), SCMP_SYS(recvmsg), - SCMP_SYS(send), SCMP_SYS(sendmmsg), SCMP_SYS(sendmsg), SCMP_SYS(sendto), + SCMP_SYS(accept), + SCMP_SYS(bind), + SCMP_SYS(connect), + SCMP_SYS(getsockname), + SCMP_SYS(getsockopt), + SCMP_SYS(recv), + SCMP_SYS(recvfrom), + SCMP_SYS(recvmmsg), +#ifdef __NR_recvmmsg_time64 + SCMP_SYS(recvmmsg_time64), +#endif + SCMP_SYS(recvmsg), + SCMP_SYS(send), + SCMP_SYS(sendmmsg), + SCMP_SYS(sendmsg), + SCMP_SYS(sendto), + SCMP_SYS(shutdown), /* TODO: check socketcall arguments */ SCMP_SYS(socketcall), + /* General I/O */ - SCMP_SYS(_newselect), SCMP_SYS(close), SCMP_SYS(open), SCMP_SYS(openat), SCMP_SYS(pipe), - SCMP_SYS(pipe2), SCMP_SYS(poll), SCMP_SYS(ppoll), SCMP_SYS(pselect6), SCMP_SYS(read), - SCMP_SYS(futex), SCMP_SYS(select), SCMP_SYS(set_robust_list), SCMP_SYS(write), + SCMP_SYS(_newselect), + SCMP_SYS(close), + SCMP_SYS(open), + SCMP_SYS(openat), + SCMP_SYS(pipe), + SCMP_SYS(pipe2), + SCMP_SYS(poll), + SCMP_SYS(ppoll), +#ifdef __NR_ppoll_time64 + SCMP_SYS(ppoll_time64), +#endif + SCMP_SYS(pselect6), +#ifdef __NR_pselect6_time64 + SCMP_SYS(pselect6_time64), +#endif + SCMP_SYS(read), + SCMP_SYS(futex), +#ifdef __NR_futex_time64 + SCMP_SYS(futex_time64), +#endif + SCMP_SYS(select), + SCMP_SYS(set_robust_list), + SCMP_SYS(write), + /* Miscellaneous */ - SCMP_SYS(getrandom), SCMP_SYS(sysinfo), SCMP_SYS(uname), + SCMP_SYS(getrandom), + SCMP_SYS(sysinfo), + SCMP_SYS(uname), + }; + + const int denied_any[] = { + SCMP_SYS(execve), +#ifdef __NR_execveat + SCMP_SYS(execveat), +#endif + SCMP_SYS(fork), + SCMP_SYS(ptrace), + SCMP_SYS(vfork), + }; + + const int denied_ntske[] = { + SCMP_SYS(ioctl), + SCMP_SYS(setsockopt), + SCMP_SYS(socket), }; const int socket_domains[] = { @@ -517,18 +638,24 @@ }; const static int socket_options[][2] = { - { SOL_IP, IP_PKTINFO }, { SOL_IP, IP_FREEBIND }, + { SOL_IP, IP_PKTINFO }, { SOL_IP, IP_FREEBIND }, { SOL_IP, IP_TOS }, #ifdef FEAT_IPV6 { SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO }, #endif +#ifdef SO_BINDTODEVICE + { SOL_SOCKET, SO_BINDTODEVICE }, +#endif { SOL_SOCKET, SO_BROADCAST }, { SOL_SOCKET, SO_REUSEADDR }, +#ifdef SO_REUSEPORT + { SOL_SOCKET, SO_REUSEPORT }, +#endif { SOL_SOCKET, SO_TIMESTAMP }, { SOL_SOCKET, SO_TIMESTAMPNS }, #ifdef HAVE_LINUX_TIMESTAMPING { SOL_SOCKET, SO_SELECT_ERR_QUEUE }, { SOL_SOCKET, SO_TIMESTAMPING }, #endif }; - const static int fcntls[] = { F_GETFD, F_SETFD, F_SETFL }; + const static int fcntls[] = { F_GETFD, F_SETFD, F_GETFL, F_SETFL }; const static unsigned long ioctls[] = { FIONREAD, TCGETS, @@ -555,64 +682,103 @@ #endif }; + unsigned int default_action, deny_action; scmp_filter_ctx *ctx; int i; - /* Check if the chronyd configuration is supported */ - check_seccomp_applicability(); + /* Sign of the level determines the deny action (kill or SIGSYS). + At level 1, selected syscalls are allowed, others are denied. + At level 2, selected syscalls are denied, others are allowed. */ + + deny_action = level > 0 ? SCMP_ACT_KILL : SCMP_ACT_TRAP; + if (level < 0) + level = -level; + + switch (level) { + case 1: + default_action = deny_action; + break; + case 2: + default_action = SCMP_ACT_ALLOW; + break; + default: + LOG_FATAL("Unsupported filter level"); + } - /* Start the helper process, which will run without any seccomp filter. It - will be used for getaddrinfo(), for which it's difficult to maintain a - list of required system calls (with glibc it depends on what NSS modules - are installed and enabled on the system). */ - PRV_StartHelper(); + if (context == SYS_MAIN_PROCESS) { + /* Check if the chronyd configuration is supported */ + check_seccomp_applicability(); + + /* At level 1, start a helper process which will not have a seccomp filter. + It will be used for getaddrinfo(), for which it is difficult to maintain + a list of required system calls (with glibc it depends on what NSS + modules are installed and enabled on the system). */ + if (default_action != SCMP_ACT_ALLOW) + PRV_StartHelper(); + } - ctx = seccomp_init(level > 0 ? SCMP_ACT_KILL : SCMP_ACT_TRAP); + ctx = seccomp_init(default_action); if (ctx == NULL) LOG_FATAL("Failed to initialize seccomp"); - /* Add system calls that are always allowed */ - for (i = 0; i < (sizeof (syscalls) / sizeof (*syscalls)); i++) { - if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls[i], 0) < 0) - goto add_failed; - } - - /* Allow sockets to be created only in selected domains */ - for (i = 0; i < sizeof (socket_domains) / sizeof (*socket_domains); i++) { - if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1, - SCMP_A0(SCMP_CMP_EQ, socket_domains[i])) < 0) - goto add_failed; - } - - /* Allow setting only selected sockets options */ - for (i = 0; i < sizeof (socket_options) / sizeof (*socket_options); i++) { - if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 3, - SCMP_A1(SCMP_CMP_EQ, socket_options[i][0]), - SCMP_A2(SCMP_CMP_EQ, socket_options[i][1]), - SCMP_A4(SCMP_CMP_LE, sizeof (int))) < 0) - goto add_failed; - } - - /* Allow only selected fcntl calls */ - for (i = 0; i < sizeof (fcntls) / sizeof (*fcntls); i++) { - if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 1, - SCMP_A1(SCMP_CMP_EQ, fcntls[i])) < 0 || - seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), 1, - SCMP_A1(SCMP_CMP_EQ, fcntls[i])) < 0) - goto add_failed; - } - - /* Allow only selected ioctls */ - for (i = 0; i < sizeof (ioctls) / sizeof (*ioctls); i++) { - if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1, - SCMP_A1(SCMP_CMP_EQ, ioctls[i])) < 0) - goto add_failed; + if (default_action != SCMP_ACT_ALLOW) { + for (i = 0; i < sizeof (allowed) / sizeof (*allowed); i++) { + if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, allowed[i], 0) < 0) + goto add_failed; + } + } else { + for (i = 0; i < sizeof (denied_any) / sizeof (*denied_any); i++) { + if (seccomp_rule_add(ctx, deny_action, denied_any[i], 0) < 0) + goto add_failed; + } + + if (context == SYS_NTSKE_HELPER) { + for (i = 0; i < sizeof (denied_ntske) / sizeof (*denied_ntske); i++) { + if (seccomp_rule_add(ctx, deny_action, denied_ntske[i], 0) < 0) + goto add_failed; + } + } + } + + if (default_action != SCMP_ACT_ALLOW && context == SYS_MAIN_PROCESS) { + /* Allow opening sockets in selected domains */ + for (i = 0; i < sizeof (socket_domains) / sizeof (*socket_domains); i++) { + if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1, + SCMP_A0(SCMP_CMP_EQ, socket_domains[i])) < 0) + goto add_failed; + } + + /* Allow selected socket options */ + for (i = 0; i < sizeof (socket_options) / sizeof (*socket_options); i++) { + if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 3, + SCMP_A1(SCMP_CMP_EQ, socket_options[i][0]), + SCMP_A2(SCMP_CMP_EQ, socket_options[i][1]), + SCMP_A4(SCMP_CMP_LE, sizeof (int))) < 0) + goto add_failed; + } + + /* Allow selected fcntl calls */ + for (i = 0; i < sizeof (fcntls) / sizeof (*fcntls); i++) { + if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 1, + SCMP_A1(SCMP_CMP_EQ, fcntls[i])) < 0 || + seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), 1, + SCMP_A1(SCMP_CMP_EQ, fcntls[i])) < 0) + goto add_failed; + } + + /* Allow selected ioctls */ + for (i = 0; i < sizeof (ioctls) / sizeof (*ioctls); i++) { + if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1, + SCMP_A1(SCMP_CMP_EQ, ioctls[i])) < 0) + goto add_failed; + } } if (seccomp_load(ctx) < 0) LOG_FATAL("Failed to load seccomp rules"); - LOG(LOGS_INFO, "Loaded seccomp filter"); + LOG(context == SYS_MAIN_PROCESS ? LOGS_INFO : LOGS_DEBUG, + "Loaded seccomp filter (level %d)", level); seccomp_release(ctx); return; diff -Nru chrony-3.5/sys_linux.h chrony-4.1/sys_linux.h --- chrony-3.5/sys_linux.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sys_linux.h 2021-05-12 11:06:15.000000000 +0000 @@ -27,13 +27,15 @@ #ifndef GOT_SYS_LINUX_H #define GOT_SYS_LINUX_H +#include "sys.h" + extern void SYS_Linux_Initialise(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); +extern void SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context); extern int SYS_Linux_CheckKernelVersion(int req_major, int req_minor); diff -Nru chrony-3.5/sys_macosx.c chrony-4.1/sys_macosx.c --- chrony-3.5/sys_macosx.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sys_macosx.c 2021-05-12 11:06:15.000000000 +0000 @@ -4,7 +4,7 @@ ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2001 * Copyright (C) J. Hannken-Illjes 2001 - * Copyright (C) Bryan Christianson 2015, 2017 + * Copyright (C) Bryan Christianson 2015, 2017, 2020 * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -46,8 +46,9 @@ #include "privops.h" #include "util.h" -#ifdef HAVE_MACOS_SYS_TIMEX #include + +#ifdef HAVE_MACOS_SYS_TIMEX #include "sys_netbsd.h" static int have_ntp_adjtime = 0; @@ -414,9 +415,10 @@ /* ================================================== */ #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); } @@ -451,6 +453,39 @@ /* ================================================== */ +#if HAVE_CLOCK_GETTIME +int +clock_gettime(clockid_t clock_id, struct timespec *ts) +{ + /* Check that the system clock_gettime symbol is actually present before + attempting to call it. The symbol is available in macOS 10.12 + and later. */ + + static int init = 0; + static int (*sys_clock_gettime)(clockid_t, struct timespec *) = NULL; + int ret = 0; + + if (!init) { + sys_clock_gettime = dlsym(RTLD_NEXT, "clock_gettime"); + init = 1; + } + + if (sys_clock_gettime != NULL) { + ret = sys_clock_gettime(clock_id, ts); + } else { + struct timeval tv; + + if (gettimeofday(&tv, NULL) < 0) + LOG_FATAL("gettimeofday() failed : %s", strerror(errno)); + + UTI_TimevalToTimespec(&tv, ts); + } + return ret; +} +#endif + +/* ================================================== */ + void SYS_MacOSX_Initialise(void) { diff -Nru chrony-3.5/sys_macosx.h chrony-4.1/sys_macosx.h --- chrony-3.5/sys_macosx.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sys_macosx.h 2021-05-12 11:06:15.000000000 +0000 @@ -30,8 +30,10 @@ #ifndef GOT_SYS_MACOSX_H #define GOT_SYS_MACOSX_H +#include "sys.h" + 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_Finalise(void); diff -Nru chrony-3.5/sys_netbsd.c chrony-4.1/sys_netbsd.c --- chrony-3.5/sys_netbsd.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sys_netbsd.c 2021-05-12 11:06:15.000000000 +0000 @@ -131,7 +131,7 @@ #ifdef FEAT_PRIVDROP 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 int fd; @@ -139,11 +139,15 @@ /* On NetBSD the helper is used only for socket binding, but on FreeBSD it's used also for setting and adjusting the system clock */ - PRV_StartHelper(); + if (context == SYS_MAIN_PROCESS) + PRV_StartHelper(); UTI_DropRoot(uid, gid); #ifdef NETBSD + if (!clock_control) + return; + /* Check if we have write access to /dev/clockctl */ fd = open("/dev/clockctl", O_WRONLY); if (fd < 0) diff -Nru chrony-3.5/sys_netbsd.h chrony-4.1/sys_netbsd.h --- chrony-3.5/sys_netbsd.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sys_netbsd.h 2021-05-12 11:06:15.000000000 +0000 @@ -28,10 +28,12 @@ #ifndef GOT_SYS_NETBSD_H #define GOT_SYS_NETBSD_H +#include "sys.h" + void SYS_NetBSD_Initialise(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 diff -Nru chrony-3.5/sys_solaris.c chrony-4.1/sys_solaris.c --- chrony-3.5/sys_solaris.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sys_solaris.c 2021-05-12 11:06:15.000000000 +0000 @@ -55,9 +55,10 @@ #ifdef FEAT_PRIVDROP 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); } #endif diff -Nru chrony-3.5/sys_solaris.h chrony-4.1/sys_solaris.h --- chrony-3.5/sys_solaris.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sys_solaris.h 2021-05-12 11:06:15.000000000 +0000 @@ -27,10 +27,12 @@ #ifndef GOT_SYS_SOLARIS_H #define GOT_SYS_SOLARIS_H +#include "sys.h" + void SYS_Solaris_Initialise(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 diff -Nru chrony-3.5/sys_timex.c chrony-4.1/sys_timex.c --- chrony-3.5/sys_timex.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/sys_timex.c 2021-05-12 11:06:15.000000000 +0000 @@ -69,6 +69,18 @@ /* ================================================== */ static double +convert_timex_frequency(const struct timex *txc) +{ + double freq_ppm; + + freq_ppm = txc->freq / FREQ_SCALE; + + return -freq_ppm; +} + +/* ================================================== */ + +static double read_frequency(void) { struct timex txc; @@ -77,7 +89,7 @@ SYS_Timex_Adjust(&txc, 0); - return txc.freq / -FREQ_SCALE; + return convert_timex_frequency(&txc); } /* ================================================== */ @@ -92,7 +104,7 @@ SYS_Timex_Adjust(&txc, 0); - return txc.freq / -FREQ_SCALE; + return convert_timex_frequency(&txc); } /* ================================================== */ @@ -256,10 +268,8 @@ state = NTP_ADJTIME(txc); if (state < 0) { - if (!ignore_error) - LOG_FATAL(NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, strerror(errno)); - else - DEBUG_LOG(NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, strerror(errno)); + LOG(ignore_error ? LOGS_DEBUG : LOGS_FATAL, + NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, strerror(errno)); } return state; diff -Nru chrony-3.5/tempcomp.c chrony-4.1/tempcomp.c --- chrony-3.5/tempcomp.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/tempcomp.c 2021-05-12 11:06:15.000000000 +0000 @@ -84,7 +84,7 @@ FILE *f; double temp, comp; - f = fopen(filename, "r"); + f = UTI_OpenFile(NULL, filename, NULL, 'r', 0); if (f && fscanf(f, "%lf", &temp) == 1) { comp = get_tempcomp(temp); @@ -122,11 +122,7 @@ char line[256]; struct Point *p; - f = fopen(filename, "r"); - if (!f) { - LOG_FATAL("Could not open tempcomp point file %s", filename); - return; - } + f = UTI_OpenFile(NULL, filename, NULL, 'R', 0); points = ARR_CreateInstance(sizeof (struct Point)); diff -Nru chrony-3.5/test/compilation/001-features chrony-4.1/test/compilation/001-features --- chrony-3.5/test/compilation/001-features 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/compilation/001-features 2021-05-12 11:06:15.000000000 +0000 @@ -17,14 +17,20 @@ "--disable-rtc" \ "--disable-sechash" \ "--disable-cmdmon" \ + "--disable-cmdmon --enable-scfilter" \ "--disable-ntp" \ + "--disable-ntp --enable-scfilter" \ + "--disable-nts" \ "--disable-refclock" \ "--disable-timestamping" \ "--disable-timestamping --disable-ntp" \ "--disable-cmdmon --disable-ntp" \ + "--disable-cmdmon --disable-ntp --enable-scfilter" \ "--disable-cmdmon --disable-refclock" \ "--disable-cmdmon --disable-ntp --disable-refclock" do ./configure $opts || exit 1 + make clean make "$@" || exit 1 + make -C test/unit check || exit 1 done diff -Nru chrony-3.5/test/compilation/003-sanitizers chrony-4.1/test/compilation/003-sanitizers --- chrony-3.5/test/compilation/003-sanitizers 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/compilation/003-sanitizers 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Run the unit and simulation tests with different compiler sanitizers # and under valgrind @@ -14,7 +14,7 @@ if [ "$ID" = "fedora" ]; then echo Checking test dependencies: rpm -q {valgrind,gcc,clang}.x86_64 {libgcc,clang-libs}.{x86_64,i686} || exit 1 - rpm -q {libseccomp,nettle,nss-softokn-freebl,libtomcrypt}-devel.{x86_64,i686} || exit 1 + rpm -q {libseccomp,nettle,nss-softokn-freebl,libtomcrypt,gnutls}-devel.{x86_64,i686} || exit 1 echo fi @@ -34,6 +34,7 @@ for extra_config_opts in \ "--all-privops" \ "--disable-scfilter" \ + "--without-gnutls" \ "--without-nettle" \ "--without-nettle --without-nss" \ "--without-nettle --without-nss --without-tomcrypt"; \ @@ -79,7 +80,12 @@ echo pushd test/simulation || exit 1 - CLKNETSIM_RANDOM_SEED=101 ./run -i 1 || exit 1 + export CLKNETSIM_RANDOM_SEED=101 + if [ "$arch_opts" = "" -a "$san_options" = "" ]; then + CLKNETSIM_CLIENT_WRAPPER=valgrind ./run -i 1 || exit 1 + else + ./run -i 1 || exit 1 + fi popd done done diff -Nru chrony-3.5/test/kernel/ntpadjtime.c chrony-4.1/test/kernel/ntpadjtime.c --- chrony-3.5/test/kernel/ntpadjtime.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/kernel/ntpadjtime.c 2021-05-12 11:06:15.000000000 +0000 @@ -52,15 +52,15 @@ printf("freq range:\n"); - for (i = 0; i <= 1000; i += 50) { + for (i = -1000; i <= 1000; i += 50) { t.modes = MOD_FREQUENCY; - t.freq = i << 16; + t.freq = i * (1 << 16); printf("%4d ppm => ", i); if (try_ntpadjtime(&t) < 0) continue; printf("%4ld ppm : ", t.freq / (1 << 16)); - printf("%s\n", t.freq == i << 16 ? "ok" : "fail"); + printf("%s\n", t.freq == i * (1 << 16) ? "ok" : "fail"); } } diff -Nru chrony-3.5/test/simulation/001-defaults chrony-4.1/test/simulation/001-defaults --- chrony-3.5/test/simulation/001-defaults 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/001-defaults 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/002-largenetwork chrony-4.1/test/simulation/002-largenetwork --- chrony-3.5/test/simulation/002-largenetwork 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/002-largenetwork 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/003-largefreqoffset chrony-4.1/test/simulation/003-largefreqoffset --- chrony-3.5/test/simulation/003-largefreqoffset 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/003-largefreqoffset 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/004-largetimeoffset chrony-4.1/test/simulation/004-largetimeoffset --- chrony-3.5/test/simulation/004-largetimeoffset 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/004-largetimeoffset 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/005-externalstep chrony-4.1/test/simulation/005-externalstep --- chrony-3.5/test/simulation/005-externalstep 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/005-externalstep 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/006-largejitter chrony-4.1/test/simulation/006-largejitter --- chrony-3.5/test/simulation/006-largejitter 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/006-largejitter 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/007-largewander chrony-4.1/test/simulation/007-largewander --- chrony-3.5/test/simulation/007-largewander 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/007-largewander 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/008-ntpera chrony-4.1/test/simulation/008-ntpera --- chrony-3.5/test/simulation/008-ntpera 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/008-ntpera 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common test_start "NTP eras" diff -Nru chrony-3.5/test/simulation/009-sourceselection chrony-4.1/test/simulation/009-sourceselection --- chrony-3.5/test/simulation/009-sourceselection 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/009-sourceselection 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/010-multrecv chrony-4.1/test/simulation/010-multrecv --- chrony-3.5/test/simulation/010-multrecv 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/010-multrecv 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/011-asymjitter chrony-4.1/test/simulation/011-asymjitter --- chrony-3.5/test/simulation/011-asymjitter 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/011-asymjitter 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/012-daemonts chrony-4.1/test/simulation/012-daemonts --- chrony-3.5/test/simulation/012-daemonts 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/012-daemonts 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/013-nameserv chrony-4.1/test/simulation/013-nameserv --- chrony-3.5/test/simulation/013-nameserv 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/simulation/013-nameserv 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +. ./test.common + +test_start "name resolving" + +dns=1 + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection || test_fail +check_packet_interval || test_fail +check_sync || test_fail + +test_pass diff -Nru chrony-3.5/test/simulation/101-poll chrony-4.1/test/simulation/101-poll --- chrony-3.5/test/simulation/101-poll 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/101-poll 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common test_start "minpoll/maxpoll options" diff -Nru chrony-3.5/test/simulation/102-iburst chrony-4.1/test/simulation/102-iburst --- chrony-3.5/test/simulation/102-iburst 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/102-iburst 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common test_start "iburst option" diff -Nru chrony-3.5/test/simulation/103-initstepslew chrony-4.1/test/simulation/103-initstepslew --- chrony-3.5/test/simulation/103-initstepslew 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/103-initstepslew 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common test_start "initstepslew directive" @@ -9,6 +9,7 @@ limit=100 client_conf="initstepslew 5 192.168.123.1" +client_server_conf="#" min_sync_time=6 max_sync_time=35 @@ -18,6 +19,7 @@ check_chronyd_exit || test_fail check_packet_interval || test_fail check_sync || test_fail + check_log_messages "00:00:0.Z System's initial.*slew" 1 1 || test_fail done min_sync_time=5 @@ -27,6 +29,35 @@ run_test || test_fail check_packet_interval || test_fail check_sync || test_fail + check_log_messages "System's initial.*step" 1 1 || test_fail done +time_offset=3 +limit=500 +servers=2 +falsetickers=1 +client_conf="initstepslew 5 192.168.123.1 192.168.123.2" +client_server_conf="server 192.168.123.2" + +min_sync_time=360 +max_sync_time=450 + +run_test || test_fail +check_chronyd_exit || test_fail +check_packet_interval || test_fail +check_sync || test_fail +check_log_messages "00:03:2.Z No suitable source for initstepslew" 1 1 || test_fail + +client_conf="initstepslew 5 192.168.123.1 192.168.123.2" + +min_sync_time=1 +max_sync_time=500 +server_conf="deny all" + +run_test || test_fail +check_chronyd_exit || test_fail +check_packet_interval || test_fail +check_sync && test_fail +check_log_messages "00:00:1.Z No suitable source for initstepslew" 1 1 || test_fail + test_pass diff -Nru chrony-3.5/test/simulation/104-driftfile chrony-4.1/test/simulation/104-driftfile --- chrony-3.5/test/simulation/104-driftfile 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/104-driftfile 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common test_start "driftfile directive" diff -Nru chrony-3.5/test/simulation/105-ntpauth chrony-4.1/test/simulation/105-ntpauth --- chrony-3.5/test/simulation/105-ntpauth 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/105-ntpauth 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common @@ -23,16 +23,20 @@ keys=4 -if check_config_h 'FEAT_SECHASH 1'; then - hashes="MD5 SHA1 SHA256 SHA384 SHA512" -else - hashes="MD5" -fi +types="MD5" +check_config_h 'FEAT_SECHASH 1' && types="$types SHA1 SHA256 SHA384 SHA512" +check_config_h 'HAVE_CMAC 1' && types="$types AES128 AES256" -for hash in $hashes; do +for type in $types; do keys=$[$keys + 1] - key=$(echo $keys $hash HEX:$(tr -c -d '0-9A-F' < /dev/urandom 2> /dev/null | \ - head -c $[$RANDOM % 64 * 2 + 2])) + case $type in + AES128) length=16;; + AES256) length=32;; + *) length=$[$RANDOM % 32 + 1];; + esac + + key=$(echo $keys $type HEX:$(tr -c -d '0-9A-F' < /dev/urandom 2> /dev/null | \ + head -c $[$length * 2])) echo "$key" >> tmp/server.keys echo "$key" >> tmp/client.keys done @@ -70,13 +74,18 @@ peers=2 max_sync_time=500 base_delay="$default_base_delay (* -1 (equal 0.1 from 3) (equal 0.1 to 1))" -client_lpeer_options="key 1" -client_rpeer_options="key 1" -run_test || test_fail -check_chronyd_exit || test_fail -check_sync || test_fail +for versions in "3 3" "3 4" "4 3" "4 4"; do + for key in 1 $keys; do + client_lpeer_options="version ${versions% *} key $key" + client_rpeer_options="version ${versions#* } key $key" + run_test || test_fail + check_chronyd_exit || test_fail + check_sync || test_fail + done +done +client_lpeer_options="key 1" client_rpeer_options="key 2" run_test || test_fail diff -Nru chrony-3.5/test/simulation/106-refclock chrony-4.1/test/simulation/106-refclock --- chrony-3.5/test/simulation/106-refclock 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/106-refclock 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common test_start "SHM refclock" @@ -16,7 +16,9 @@ chronyc_conf="tracking" for refclock in "SHM 0" "PHC /dev/ptp0"; do - client_conf="refclock $refclock stratum 3 delay 1e-3 refid GPS" + client_conf="refclock $refclock stratum 3 delay 1e-3 refid GPS +logdir tmp +log refclocks" run_test || test_fail check_chronyd_exit || test_fail @@ -29,6 +31,79 @@ .* Update interval : 16\.. seconds .*$" || test_fail + + check_file_messages "20.* GPS.*[0-9] N " 997 1001 refclocks.log || test_fail + check_file_messages "20.* GPS.*- N " 61 63 refclocks.log || test_fail + rm -f tmp/refclocks.log done +if check_config_h 'FEAT_PPS 1'; then + refclock_offset=0.35 + refclock_jitter=0.05 + + client_conf=" +refclock SHM 0 refid NMEA noselect +refclock PPS /dev/pps0 lock NMEA +logdir tmp +log refclocks" + + run_test || test_fail + check_chronyd_exit || test_fail + check_source_selection || test_fail + check_sync || test_fail + check_chronyc_output "^Reference ID.*50505331 \(PPS1\) +Stratum.*: 1 +.* +Root delay : 0\.000000001 seconds +.*$" || test_fail + + check_file_messages "20.* PPS1.*[0-9] N " 620 740 refclocks.log || test_fail + check_file_messages "20.* PPS1.*- N " 60 63 refclocks.log || test_fail + rm -f tmp/refclocks.log + + client_conf=" +refclock SHM 0 noselect +refclock PPS /dev/pps0 +local +logdir tmp +log refclocks" + + run_test || test_fail + check_chronyd_exit || test_fail + check_source_selection || test_fail + check_sync || test_fail + check_chronyc_output "^Reference ID.*50505331 \(PPS1\) +Stratum.*: 10 +.* +Root delay : 0\.000000001 seconds +.*$" || test_fail + + check_file_messages "20.* PPS1.*[0-9] N " 997 1001 refclocks.log || test_fail + check_file_messages "20.* PPS1.*- N " 60 63 refclocks.log || test_fail + rm -f tmp/refclocks.log + + min_sync_time=100 + max_sync_time=220 + chronyc_start=220 + client_conf=" +refclock SHM 0 refid NMEA offset 0.35 delay 0.1 +refclock PPS /dev/pps0 +logdir tmp +log refclocks" + + run_test || test_fail + check_chronyd_exit || test_fail + check_source_selection || test_fail + check_sync || test_fail + check_chronyc_output "^Reference ID.*50505331 \(PPS1\) +Stratum.*: 1 +.* +Root delay : 0\.000000001 seconds +.*$" || test_fail + + check_file_messages "20.* PPS1.*[0-9] N " 800 940 refclocks.log || test_fail + check_file_messages "20.* PPS1.*- N " 50 63 refclocks.log || test_fail + rm -f tmp/refclocks.log +fi + test_pass diff -Nru chrony-3.5/test/simulation/107-allowdeny chrony-4.1/test/simulation/107-allowdeny --- chrony-3.5/test/simulation/107-allowdeny 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/107-allowdeny 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/108-peer chrony-4.1/test/simulation/108-peer --- chrony-3.5/test/simulation/108-peer 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/108-peer 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/109-makestep chrony-4.1/test/simulation/109-makestep --- chrony-3.5/test/simulation/109-makestep 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/109-makestep 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common test_start "makestep directive" diff -Nru chrony-3.5/test/simulation/110-chronyc chrony-4.1/test/simulation/110-chronyc --- chrony-3.5/test/simulation/110-chronyc 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/110-chronyc 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common @@ -8,12 +8,17 @@ check_config_h 'FEAT_CMDMON 1' || test_skip refclock_jitter=$jitter +client_server_conf=" +server node1.net1.clk +server 192.168.123.2" client_conf=" refclock SHM 0 noselect smoothtime 400 0.001 leaponly" chronyc_conf="activity tracking +sourcename 192.168.123.1 +sourcename 192.168.123.2 sources sourcestats manual list @@ -25,7 +30,7 @@ check_chronyd_exit || test_fail check_chronyc_output "^200 OK -1 sources online +2 sources online 0 sources offline 0 sources doing burst \(return to online\) 0 sources doing burst \(return to offline\) @@ -43,16 +48,18 @@ Root dispersion : 0\.000...... seconds Update interval : [0-9]+\.. seconds Leap status : Normal -210 Number of sources = 2 +node1\.net1\.clk +192\.168\.123\.2 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== #\? SHM0 0 4 377 [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s \^\* 192\.168\.123\.1 1 [67] 377 [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s -210 Number of sources = 2 +\^\? 192\.168\.123\.2 0 [0-9]+ 0 - \+0ns\[ \+0ns\] \+/- 0ns Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev ============================================================================== SHM0 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s 192\.168\.123\.1 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s +192\.168\.123\.2 0 0 0 \+0\.000 2000\.000 \+0ns 4000ms 210 n_samples = 0 # Date Time\(UTC\) Slewed Original Residual ======================================================= @@ -66,15 +73,36 @@ 513 RTC driver not running$" \ || test_fail +chronyc_conf="tracking" +dns=1 + +run_test || test_fail +check_chronyd_exit || test_fail + +check_chronyc_output "^Reference ID : C0A87B01 \(node1\.net1\.clk\)" \ + || test_fail + +chronyc_options="-c" + +run_test || test_fail +check_chronyd_exit || test_fail + +check_chronyc_output "^C0A87B01,192\.168\.123\.1,2,12623049..\..........,-?0\.0000.....,-?0\.000......,0\.000......,(99|100)\....,-?[0-9]\....,[0-9]\....,0\.000......,0\.000......,[0-9]+\..,Normal$" \ + || test_fail + +chronyc_options="" server_strata=0 chronyc_start=0 +client_server_conf="" client_conf="" +server_conf="server 192.168.123.1" limit=1 for chronyc_conf in \ "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 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 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" \ + "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 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" \ + "add server node1.net1.clk" \ "allow 1.2.3.4" \ "allow 1.2" \ "allow 3.4.5" \ @@ -85,10 +113,14 @@ "allow ::/0" \ "allow" \ "allow all 10/24" \ + "authdata" \ "burst 5/10" \ "burst 3/5 255.255.255.0/1.2.3.0" \ "burst 1/2 1.2.3.0/24" \ "clients" \ + "clients -k" \ + "clients -p 100" \ + "clients -r" \ "cmdaccheck 1.2.3.4" \ "cmdallow 1.2.3.4" \ "cmdallow all 1.2.3.0/24" \ @@ -96,6 +128,7 @@ "cmddeny all 1.2.3.0/24" \ "cyclelogs" \ "delete 10.0.0.0" \ + "delete ID#0000000001" \ "deny 1.2.3.4" \ "deny all 1.2.3.0/24" \ "dfreq 1.0e-3" \ @@ -116,6 +149,7 @@ "maxupdateskew 1.2.3.4 10.0" \ "minpoll 1.2.3.4 3" \ "minstratum 1.2.3.4 1" \ + "minstratum ID#0000000001 1" \ "ntpdata 1.2.3.4" \ "offline" \ "offline 255.255.255.0/1.2.3.0" \ @@ -126,8 +160,11 @@ "polltarget 1.2.3.4 10" \ "refresh" \ "rekey" \ + "reload sources" \ "reselect" \ "reselectdist 1e-3" \ + "reset sources" \ + "selectdata" \ "settime 16:30" \ "settime 16:30:05" \ "settime Nov 21, 2015 16:30:05" \ @@ -140,7 +177,7 @@ do run_test || test_fail check_chronyd_exit || test_fail - check_chronyc_output "501 Not authorised" || test_fail + check_chronyc_output "501 Not authorised$" || test_fail done chronyc_conf="dns -n @@ -152,6 +189,7 @@ retries 1 keygen keygen 10 MD5 128 +keygen 11 MD5 40 help quit nosuchcommand" @@ -160,7 +198,48 @@ check_chronyc_output "^1 (MD5|SHA1) HEX:........................................ 10 MD5 HEX:................................ +11 MD5 HEX:.................... System clock:.*this help *$" || test_fail +chronyc_conf="keygen 10 NOSUCHTYPE 128 +help" +run_test || test_fail +check_chronyc_output "^Unknown hash function or cipher NOSUCHTYPE\$" || test_fail + +if check_config_h 'FEAT_SECHASH 1'; then + for hash in MD5 SHA1 SHA256 SHA384 SHA512; do + chronyc_conf="keygen 5 $hash" + run_test || test_fail + check_chronyc_output "^5 $hash HEX:........................................\$" || test_fail + done +fi + +if check_config_h 'HAVE_CMAC 1'; then + chronyc_conf="keygen 6 AES128 +keygen 7 AES256" + run_test || test_fail + check_chronyc_output "^6 AES128 HEX:................................ +7 AES256 HEX:................................................................\$" || test_fail +fi + +# Pass every fourth request +base_delay=$(cat <<-EOF | tr -d '\n' + (+ 1e-4 + (* -1 + (equal 0.1 from 2) + (equal 0.1 (min (% (sum 1) 4) 1) 1))) +EOF +) +limit=15 + +chronyc_conf="sources" +run_test || test_fail +check_chronyc_output "^506 Cannot talk to daemon$" || test_fail + +chronyc_conf="retries 3 +sources" +run_test || test_fail +check_chronyc_output "^MS.*0ns$" || test_fail + test_pass diff -Nru chrony-3.5/test/simulation/111-knownclient chrony-4.1/test/simulation/111-knownclient --- chrony-3.5/test/simulation/111-knownclient 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/111-knownclient 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/112-port chrony-4.1/test/simulation/112-port --- chrony-3.5/test/simulation/112-port 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/112-port 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/113-leapsecond chrony-4.1/test/simulation/113-leapsecond --- chrony-3.5/test/simulation/113-leapsecond 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/113-leapsecond 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/114-presend chrony-4.1/test/simulation/114-presend --- chrony-3.5/test/simulation/114-presend 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/114-presend 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common test_start "presend option" diff -Nru chrony-3.5/test/simulation/115-cmdmontime chrony-4.1/test/simulation/115-cmdmontime --- chrony-3.5/test/simulation/115-cmdmontime 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/115-cmdmontime 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common @@ -15,11 +15,10 @@ chronyc_conf="tracking" for year in `seq 1850 100 2300`; do - date="Jan 01 00:00:00 $year" - export CLKNETSIM_START_DATE=$(date -d "$date UTC" +'%s') + export CLKNETSIM_START_DATE=$(date -d "Jan 01 00:00:05 $year UTC" +'%s') run_test || test_fail check_chronyd_exit || test_fail - check_chronyc_output "^.*Ref time \(UTC\).*$date.*$" || test_fail + check_chronyc_output "^.*Ref time \(UTC\).*Jan 01 00:00:0. $year.*$" || test_fail done test_pass diff -Nru chrony-3.5/test/simulation/116-minsources chrony-4.1/test/simulation/116-minsources --- chrony-3.5/test/simulation/116-minsources 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/116-minsources 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/117-fallbackdrift chrony-4.1/test/simulation/117-fallbackdrift --- chrony-3.5/test/simulation/117-fallbackdrift 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/117-fallbackdrift 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common test_start "fallback drift" diff -Nru chrony-3.5/test/simulation/118-maxdelay chrony-4.1/test/simulation/118-maxdelay --- chrony-3.5/test/simulation/118-maxdelay 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/118-maxdelay 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common test_start "maxdelay options" diff -Nru chrony-3.5/test/simulation/119-smoothtime chrony-4.1/test/simulation/119-smoothtime --- chrony-3.5/test/simulation/119-smoothtime 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/119-smoothtime 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/120-selectoptions chrony-4.1/test/simulation/120-selectoptions --- chrony-3.5/test/simulation/120-selectoptions 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/120-selectoptions 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common @@ -16,7 +16,6 @@ run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail -check_packet_interval || test_fail check_sync || test_fail client_server_conf=" @@ -27,7 +26,6 @@ run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail -check_packet_interval || test_fail # This check is expected to fail check_sync && test_fail @@ -36,10 +34,9 @@ run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail -check_packet_interval || test_fail check_sync || test_fail -base_delay=$default_base_delay +base_delay=1e-3 falsetickers=1 client_server_conf=" @@ -65,4 +62,28 @@ check_source_selection && test_fail check_sync && test_fail +cat > tmp/keys <<-EOF +1 MD5 HEX:1B81CBF88D4A73F2E8CE59647F6E5C1719B6CAF5 +EOF + +server_conf="keyfile tmp/keys" +client_server_conf=" +server 192.168.123.1 key 1 +server 192.168.123.2 +server 192.168.123.3" + +for authselectmode in require prefer mix ignore; do + client_conf="keyfile tmp/keys + authselectmode $authselectmode" + run_test || test_fail + check_chronyd_exit || test_fail + check_source_selection || test_fail + check_packet_interval || test_fail + if [ $authselectmode = ignore ]; then + check_sync || test_fail + else + check_sync && test_fail + fi +done + test_pass diff -Nru chrony-3.5/test/simulation/121-orphan chrony-4.1/test/simulation/121-orphan --- chrony-3.5/test/simulation/121-orphan 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/121-orphan 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common @@ -11,9 +11,9 @@ server 192.168.123.1 server 192.168.123.2 server 192.168.123.3" -max_sync_time=500 +max_sync_time=900 client_start=140 -chronyc_start=300 +chronyc_start=700 chronyc_conf="tracking" time_rms_limit=5e-4 diff -Nru chrony-3.5/test/simulation/122-xleave chrony-4.1/test/simulation/122-xleave --- chrony-3.5/test/simulation/122-xleave 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/122-xleave 2021-05-12 11:06:15.000000000 +0000 @@ -1,9 +1,12 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common test_start "interleaved mode" client_server_options="xleave" +client_conf=" +logdir tmp +log rawmeasurements" run_test || test_fail check_chronyd_exit || test_fail @@ -11,6 +14,10 @@ check_packet_interval || test_fail check_sync || test_fail +check_file_messages "111 111 1111.* 4B [DKH] [DKH]\$" 2 2 measurements.log || test_fail +check_file_messages "111 111 1111.* 4I [DKH] [DKH]\$" 30 200 measurements.log || test_fail +rm -f tmp/measurements.log + clients=2 peers=2 max_sync_time=500 @@ -25,6 +32,8 @@ check_source_selection && test_fail check_sync && test_fail +rm -f tmp/measurements.log + for rpoll in 4 5 6; do client_rpeer_options="xleave minpoll $rpoll maxpoll $rpoll" @@ -32,6 +41,15 @@ check_chronyd_exit || test_fail check_source_selection || test_fail check_sync || test_fail + + if [ $rpoll -le 5 ]; then + check_file_messages "111 111 1111.* 1B [DKH] [DKH]\$" 0 0 measurements.log || test_fail + check_file_messages "111 111 1111.* 1I [DKH] [DKH]\$" 200 310 measurements.log || test_fail + else + check_file_messages "111 111 1111.* 1B [DKH] [DKH]\$" 125 135 measurements.log || test_fail + check_file_messages "111 111 1111.* 1I [DKH] [DKH]\$" 20 30 measurements.log || test_fail + fi + rm -f tmp/measurements.log done test_pass diff -Nru chrony-3.5/test/simulation/123-mindelay chrony-4.1/test/simulation/123-mindelay --- chrony-3.5/test/simulation/123-mindelay 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/123-mindelay 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/124-tai chrony-4.1/test/simulation/124-tai --- chrony-3.5/test/simulation/124-tai 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/124-tai 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/125-packetloss chrony-4.1/test/simulation/125-packetloss --- chrony-3.5/test/simulation/125-packetloss 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/125-packetloss 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/126-burst chrony-4.1/test/simulation/126-burst --- chrony-3.5/test/simulation/126-burst 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/126-burst 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/127-filter chrony-4.1/test/simulation/127-filter --- chrony-3.5/test/simulation/127-filter 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/127-filter 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/128-nocontrol chrony-4.1/test/simulation/128-nocontrol --- chrony-3.5/test/simulation/128-nocontrol 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/128-nocontrol 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/129-reload chrony-4.1/test/simulation/129-reload --- chrony-3.5/test/simulation/129-reload 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/129-reload 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common @@ -13,9 +13,45 @@ maxupdateskew 10000" run_test || test_fail +check_chronyd_exit || test_fail + +check_log_messages "Loaded dump file" 0 0 || test_fail +check_file_messages "." 6 6 192.168.123.1.dat || test_fail + +client_start=$limit +limit=1000 + +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 "Loaded dump file" 1 1 || test_fail +check_file_messages "." 10 30 192.168.123.1.dat || test_fail + +rm -f tmp/*.dat + +client_start=0 +limit=200 +jitter=1e-6 +client_conf="dumpdir tmp +maxupdateskew 1e-6 +maxslewrate 1e-6" + +run_test || test_fail +check_chronyd_exit || test_fail + +check_log_messages "Loaded dump file" 0 0 || test_fail +check_file_messages "." 8 8 192.168.123.1.dat || test_fail +cp tmp/192.168.123.1.dat tmp/backup.dat client_start=$limit limit=1000 +min_sync_time=201 +max_sync_time=203 +client_server_options="offline" +client_conf="dumpdir tmp" run_test || test_fail check_chronyd_exit || test_fail @@ -23,4 +59,51 @@ check_packet_interval || test_fail check_sync || test_fail +check_log_messages "Loaded dump file" 1 1 || test_fail +check_file_messages "." 8 8 192.168.123.1.dat || test_fail + +cp -f tmp/backup.dat tmp/192.168.123.1.dat + +client_server_options="key 1" + +run_test || test_fail +check_chronyd_exit || test_fail +check_sync && test_fail + +check_log_messages "Could not load dump file" 1 1 || test_fail +check_log_messages "Loaded dump file" 0 0 || test_fail + +client_server_options="" + +if check_config_h 'FEAT_REFCLOCK 1'; then + refclock_jitter=1e-6 + servers=0 + client_start=0 + limit=40 + min_sync_time=56 + max_sync_time=58 + client_chronyd_options="-r" + client_conf="dumpdir tmp + refclock SHM 0" + + run_test || test_fail + check_chronyd_exit || test_fail + + check_log_messages "Loaded dump file" 0 0 || test_fail + check_file_messages "." 6 6 refid:53484d30.dat || test_fail + + client_start=$limit + limit=300 + + run_test || test_fail + check_chronyd_exit || test_fail + check_source_selection || test_fail + check_sync || test_fail + + check_log_messages "Loaded dump file" 1 1 || test_fail + check_file_messages "." 6 23 refid:53484d30.dat || test_fail + + rm -f tmp/*.dat +fi + test_pass diff -Nru chrony-3.5/test/simulation/130-quit chrony-4.1/test/simulation/130-quit --- chrony-3.5/test/simulation/130-quit 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/130-quit 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common @@ -16,6 +16,14 @@ check_packet_interval || test_fail check_sync || test_fail +min_sync_time=1 +max_sync_time=1 +client_server_options="iburst maxsamples 1" + +run_test || test_fail +check_chronyd_exit || test_fail +check_sync || test_fail + client_chronyd_options="-Q" run_test || test_fail check_sync && test_fail diff -Nru chrony-3.5/test/simulation/131-maxchange chrony-4.1/test/simulation/131-maxchange --- chrony-3.5/test/simulation/131-maxchange 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/131-maxchange 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/132-logchange chrony-4.1/test/simulation/132-logchange --- chrony-3.5/test/simulation/132-logchange 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/132-logchange 2021-05-12 11:06:15.000000000 +0000 @@ -1,11 +1,11 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common test_start "logchange directive" time_offset=2 -min_sync_time=600 +min_sync_time=590 max_sync_time=700 client_server_options="maxsamples 6" client_conf="logchange 0.1" diff -Nru chrony-3.5/test/simulation/133-hwtimestamp chrony-4.1/test/simulation/133-hwtimestamp --- chrony-3.5/test/simulation/133-hwtimestamp 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/133-hwtimestamp 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common @@ -27,8 +27,10 @@ if check_config_h 'FEAT_DEBUG 1'; then check_log_messages "HW clock samples" 190 200 || test_fail check_log_messages "HW clock reset" 0 0 || test_fail - check_log_messages "Received.*tss=1" 1 1 || test_fail - check_log_messages "Received.*tss=2" 390 400 || test_fail + check_log_messages "Received message.*tss=KH" 195 200 || test_fail + check_log_messages "Received error.*message.*tss=KH" 195 200 || test_fail + check_log_messages "Updated RX timestamp.*tss=1" 1 1 || test_fail + check_log_messages "Updated RX timestamp.*tss=2" 195 200 || test_fail check_log_messages "update_tx_timestamp.*Updated" 50 140 || test_fail check_log_messages "update_tx_timestamp.*Unacceptable" 50 140 || test_fail fi diff -Nru chrony-3.5/test/simulation/134-log chrony-4.1/test/simulation/134-log --- chrony-3.5/test/simulation/134-log 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/134-log 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/135-ratelimit chrony-4.1/test/simulation/135-ratelimit --- chrony-3.5/test/simulation/135-ratelimit 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/135-ratelimit 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common test_start "ratelimit directive" diff -Nru chrony-3.5/test/simulation/136-broadcast chrony-4.1/test/simulation/136-broadcast --- chrony-3.5/test/simulation/136-broadcast 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/136-broadcast 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/137-pool chrony-4.1/test/simulation/137-pool --- chrony-3.5/test/simulation/137-pool 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/simulation/137-pool 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +. ./test.common + +test_start "pool directive" + +limit=500 +client_conf="logdir tmp +log measurements" + +servers=3 +client_server_conf="pool nodes-1-2-3.net1.clk" + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection || test_fail +check_packet_interval || test_fail +check_sync || test_fail + +check_file_messages "20.*192.168.123.1" 5 10 measurements.log || test_fail +check_file_messages "20.*192.168.123.2" 5 10 measurements.log || test_fail +check_file_messages "20.*192.168.123.3" 5 10 measurements.log || test_fail +rm -f tmp/measurements.log + +servers=6 +client_server_conf="pool nodes-1-2-3-4-5-6.net1.clk minpoll 6 maxpoll 6" + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection || test_fail +check_packet_interval || test_fail +check_sync || test_fail + +check_file_messages "20.*192.168.123.*" 30 35 measurements.log || test_fail +rm -f tmp/measurements.log + +servers=6 +client_server_conf="pool nodes-1-2-3-4-5-6.net1.clk maxsources 2 minpoll 6 maxpoll 6" + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection || test_fail +check_packet_interval || test_fail +check_sync || test_fail + +check_file_messages "20.*192.168.123.*" 15 17 measurements.log || test_fail +rm -f tmp/measurements.log + +test_pass diff -Nru chrony-3.5/test/simulation/138-syncloop chrony-4.1/test/simulation/138-syncloop --- chrony-3.5/test/simulation/138-syncloop 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/simulation/138-syncloop 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +. ./test.common + +test_start "loop prevention" + +mkdir tmp/logdir1 tmp/logdir2 + +server_conf=" +server 192.168.123.1 +server 192.168.123.2 +logdir tmp/logdir1 +log measurements" +client_server_conf=" +server 192.168.123.1 +server 192.168.123.2 +logdir tmp/logdir2 +log measurements +allow" + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection || test_fail +check_sync || test_fail + +check_file_messages "20.*123\.1.* 111 111 1110" 30 200 logdir1/measurements.log || test_fail +check_file_messages "20.*123\.2.* 111 111 1110" 30 200 logdir1/measurements.log || test_fail +check_file_messages "20.*123\...* 111 111 1111" 0 0 logdir1/measurements.log || test_fail +check_file_messages "20.*123\.1.* 111 111 1111" 30 200 logdir2/measurements.log || test_fail +check_file_messages "20.*123\.1.* 111 111 1110" 0 0 logdir2/measurements.log || test_fail +check_file_messages "20.*123\.2.* 111 111 1110" 30 200 logdir2/measurements.log || test_fail +check_file_messages "20.*123\.2.* 111 111 1111" 0 0 logdir1/measurements.log || test_fail + +test_pass diff -Nru chrony-3.5/test/simulation/139-nts chrony-4.1/test/simulation/139-nts --- chrony-3.5/test/simulation/139-nts 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/simulation/139-nts 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,312 @@ +#!/usr/bin/env bash + +. ./test.common + +test_start "NTP authentication with NTS" + +check_config_h 'FEAT_NTS 1' || test_skip +certtool --help &> /dev/null || test_skip + +export CLKNETSIM_START_DATE=$(date -d 'Jan 1 00:00:00 UTC 2010' +'%s') + +for i in 1 2; do + cat > tmp/cert$i.cfg <<-EOF + cn = "node$i.net1.clk" + dns_name = "node$i.net1.clk" + ip_address = "192.168.123.$i" + serial = 001 + activation_date = "2010-01-01 00:00:00 UTC" + expiration_date = "2010-01-02 00:00:00 UTC" + signing_key + encryption_key + EOF + + certtool --generate-privkey --key-type=ed25519 --outfile tmp/server$i.key &> \ + tmp/log.certtool$i + certtool --generate-self-signed --load-privkey tmp/server$i.key \ + --template tmp/cert$i.cfg --outfile tmp/server$i.crt &>> tmp/log.certtool$i +done + +max_sync_time=400 +dns=1 +server_conf=" +ntsserverkey tmp/server1.key +ntsservercert tmp/server1.crt +ntsprocesses 0 +ntsrotate 66 +ntsdumpdir tmp +" +client_server_options="minpoll 6 maxpoll 6 nts" +client_conf=" +nosystemcert +ntstrustedcerts /dev/null +ntstrustedcerts tmp/server1.crt +ntstrustedcerts /dev/null +logdir tmp +log rawmeasurements" + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection || test_fail +check_sync || test_fail + +check_file_messages "20.*123\.1.* 111 111 1111" 75 80 measurements.log || test_fail +check_file_messages "20.*123\.1.* 111 001 0000" 37 39 measurements.log || test_fail +check_file_messages " 2 1 .* 4460 " 260 300 log.packets || test_fail +check_file_messages "." 6 6 ntskeys || test_fail +rm -f tmp/measurements.log + +client_conf+=" +ntsrefresh 120 +ntsdumpdir tmp" + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection || test_fail +check_sync || test_fail + +check_file_messages "20.*123\.1.* 111 111 1111" 99 103 measurements.log || test_fail +check_file_messages "20.*123\.1.* 111 001 0000" 0 0 measurements.log || test_fail +check_file_messages " 2 1 .* 4460 " 350 390 log.packets || test_fail +check_file_messages "." 6 6 ntskeys || test_fail +check_file_messages "." 12 13 192.168.123.1.nts || test_fail +rm -f tmp/measurements.log + +export CLKNETSIM_START_DATE=$(date -d 'Jan 1 00:00:00 UTC 2010 + 40000 sec' +'%s') + +server_conf+=" +ntsrotate 100000" +client_conf+=" +ntsrefresh 39500" + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection || test_fail +check_sync || test_fail + +check_file_messages "20.*123\.1.* 111 111 1111" 150 160 measurements.log || test_fail +check_file_messages "20.*123\.1.* 111 001 0000" 0 0 measurements.log || test_fail +check_file_messages " 2 1 .* 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 "." 12 13 192.168.123.1.nts || test_fail +rm -f tmp/measurements.log + +client_conf=" +nosystemcert" + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection && test_fail +check_sync && test_fail + +check_file_messages " 2 1 .* 123 " 0 0 log.packets || test_fail +check_file_messages " 2 1 .* 4460 " 10 20 log.packets || test_fail + +export CLKNETSIM_START_DATE=$(date -d 'Jan 2 00:00:01 UTC 2010' +'%s') + +client_conf=" +nosystemcert +ntstrustedcerts tmp/server1.crt" + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection && test_fail +check_sync && test_fail + +check_file_messages " 2 1 .* 123 " 0 0 log.packets || test_fail +check_file_messages " 2 1 .* 4460 " 10 20 log.packets || test_fail +check_log_messages "expired certificate" 4 4 || test_fail + +client_conf+=" +nocerttimecheck 1" + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection || test_fail +check_sync || test_fail + +export CLKNETSIM_START_DATE=$(date -d 'Jan 1 00:00:00 UTC 2010' +'%s') + +client_conf=" +nosystemcert +ntstrustedcerts tmp/server1.crt +ntsrefresh 500" + +for dns in 1 0; do + server_conf=" + ntsserverkey tmp/server1.key + ntsservercert tmp/server1.crt + ntsprocesses 0 + ntsrotate 0 + ntsdumpdir tmp" + + if [ $dns != 0 ]; then + server_conf+=" + ntsntpserver node2.net1.clk" + client_server_conf="server node1.net1.clk $client_server_options" + else + server_conf+=" + ntsntpserver 192.168.123.2" + client_server_conf="server 192.168.123.1 $client_server_options" + fi + + servers=1 + + run_test || test_fail + check_chronyd_exit || test_fail + check_source_selection && test_fail + check_sync && test_fail + + check_file_messages " 2 1 .* 4460 " 50 100 log.packets || test_fail + check_file_messages " 2 2 .* 4460 " 0 0 log.packets || test_fail + check_log_messages "Source 192.168.123.1 changed to 192.168.123.2" 6 8 || test_fail + check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 6 8 || test_fail + + servers=2 + + run_test || test_fail + check_chronyd_exit || test_fail + check_source_selection || test_fail + check_sync || test_fail + + check_file_messages " 3 1 .* 4460 " 100 150 log.packets || test_fail + check_file_messages " 3 2 .* 4460 " 0 0 log.packets || test_fail + check_log_messages "Source 192.168.123.1 changed to 192.168.123.2" 1 1 || test_fail + check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 0 0 || test_fail + + server_conf+=" + ntsratelimit interval 12 burst 1 leak 4" + + run_test || test_fail + check_chronyd_exit || test_fail + check_source_selection && test_fail + + check_file_messages " 3 1 .* 4460 1 0 2" 25 50 log.packets || test_fail + check_file_messages " 3 2 .* 4460 " 0 0 log.packets || test_fail + check_log_messages "Source 192.168.123.1 changed to 192.168.123.2" 2 6 || test_fail + check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 1 6 || test_fail +done + +servers=2 +server_conf=" +ntsserverkey tmp/server1.key +ntsservercert tmp/server1.crt +ntsprocesses 0 +ntsrotate 0 +ntsntpserver node2.net1.clk +port 11123 +ntsdumpdir tmp" +client_conf=" +nosystemcert +ntstrustedcerts tmp/server1.crt +ntsdumpdir tmp" +client_server_conf="server 192.168.123.1 $client_server_options" + +rm -f tmp/*.nts + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection || test_fail +check_sync || test_fail + +check_log_messages "Could not change" 0 0 || test_fail +check_file_messages " 3 1 .* 4460 1 0 2" 1 1 log.packets || test_fail +check_file_messages " 3 2 .* 4460 " 0 0 log.packets || test_fail + +for dns in 1 0; do + run_test || test_fail + check_chronyd_exit || test_fail + check_source_selection || test_fail + check_sync || test_fail + + check_log_messages "Could not change" 0 0 || test_fail + check_file_messages " 3 1 .* 4460 1 0 2" 0 0 log.packets || test_fail + check_file_messages " 3 2 .* 4460 " 0 0 log.packets || test_fail +done + +server_conf=" +ntsserverkey tmp/server1.key +ntsservercert tmp/server1.crt +ntsprocesses 0 +ntsrotate 0 +ntsdumpdir tmp" + +head -n 8 tmp/192.168.123.1.nts > tmp/192.168.123.1.nts_ +mv tmp/192.168.123.1.nts_ tmp/192.168.123.1.nts + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection || test_fail +check_sync || test_fail + +check_log_messages "Could not change" 0 0 || test_fail +check_file_messages " 3 1 .* 4460 1 0 2" 1 1 log.packets || test_fail +check_file_messages " 3 2 .* 4460 " 0 0 log.packets || test_fail +check_file_messages " 3 1 .* 11123 " 0 0 log.packets || test_fail +check_file_messages " 3 2 .* 123 " 0 0 log.packets || test_fail +check_file_messages " 3 2 .* 11123 " 3 3 log.packets || test_fail + +dns=1 +server_conf=" +ntsserverkey tmp/server1.key +ntsservercert tmp/server1.crt +ntsserverkey tmp/server2.key +ntsservercert tmp/server2.crt +ntsprocesses 0" +client_conf=" +nosystemcert +ntstrustedcerts tmp/server1.crt +ntstrustedcerts tmp/server2.crt +minsources 2" +client_server_conf="" + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection || test_fail +check_sync || test_fail + +client_conf=" +nosystemcert +ntstrustedcerts tmp/server1.crt +ntstrustedcerts 1 tmp/server1.crt +ntstrustedcerts 2 tmp/server2.crt +ntstrustedcerts 3 tmp/server2.crt" +client_server_conf=" +server node1.net1.clk $client_server_options certset 0 +server node2.net1.clk $client_server_options certset 2" + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection || test_fail +check_sync || test_fail + +check_file_messages " 3 1 .* 123 " 100 200 log.packets || test_fail +check_file_messages " 3 2 .* 123 " 100 200 log.packets || test_fail + +client_server_conf=" +server node1.net1.clk $client_server_options certset 2 +server node2.net1.clk $client_server_options" + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection && test_fail +check_sync && test_fail + +check_file_messages " 3 1 .* 123 " 0 0 log.packets || test_fail +check_file_messages " 3 2 .* 123 " 0 0 log.packets || test_fail + +client_conf=" +nosystemcert +ntstrustedcerts tmp/nosuch.crt +ntstrustedcerts 2 tmp/nosuch.crt" + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection && test_fail +check_sync && test_fail + +check_file_messages " 3 1 .* 123 " 0 0 log.packets || test_fail +check_file_messages " 3 2 .* 123 " 0 0 log.packets || test_fail + +test_pass diff -Nru chrony-3.5/test/simulation/140-noclientlog chrony-4.1/test/simulation/140-noclientlog --- chrony-3.5/test/simulation/140-noclientlog 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/simulation/140-noclientlog 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +. ./test.common +test_start "noclientlog option" + +server_conf="noclientlog" +client_server_options="xleave" +client_conf=" +logdir tmp +log rawmeasurements" + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection || test_fail +check_packet_interval || test_fail +check_sync || test_fail + +check_file_messages "111 111 1111.* 4B " 30 200 measurements.log || test_fail +check_file_messages "111 111 1111.* 4I " 0 0 measurements.log || test_fail + +test_pass diff -Nru chrony-3.5/test/simulation/141-copy chrony-4.1/test/simulation/141-copy --- chrony-3.5/test/simulation/141-copy 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/simulation/141-copy 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +. ./test.common + +test_start "copy option" + +check_config_h 'FEAT_CMDMON 1' || test_skip + +client_server_options="copy" +chronyc_conf="tracking" + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection || test_fail +check_sync || test_fail +check_chronyc_output "^Reference ID *: 7F7F0101 \(192\.168\.123\.1\) +Stratum *: 1" || test_fail + +test_pass diff -Nru chrony-3.5/test/simulation/201-freqaccumulation chrony-4.1/test/simulation/201-freqaccumulation --- chrony-3.5/test/simulation/201-freqaccumulation 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/201-freqaccumulation 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/202-prefer chrony-4.1/test/simulation/202-prefer --- chrony-3.5/test/simulation/202-prefer 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/202-prefer 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./test.common diff -Nru chrony-3.5/test/simulation/run chrony-4.1/test/simulation/run --- chrony-3.5/test/simulation/run 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/run 2021-05-12 11:06:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash print_help() { echo "$1 [-a] [-i ITERATIONS] [-m MAXFAILS] [-s SEED] [TEST]..." diff -Nru chrony-3.5/test/simulation/test.common chrony-4.1/test/simulation/test.common --- chrony-3.5/test/simulation/test.common 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/simulation/test.common 2021-05-12 11:06:15.000000000 +0000 @@ -64,6 +64,7 @@ default_chronyc_conf="" default_server_chronyd_options="" default_client_chronyd_options="" +default_chronyc_options="" default_time_max_limit=1e-3 default_freq_max_limit=5e-4 @@ -75,6 +76,8 @@ default_client_min_mean_out_interval=0.0 default_client_max_min_out_interval=inf +default_dns=0 + # Initialize test settings from their defaults for defoptname in ${!default_*}; do optname=${defoptname#default_} @@ -82,7 +85,7 @@ done test_start() { - rm -f tmp/* + rm -rf tmp/* echo "Testing $@:" check_config_h 'FEAT_NTP 1' || test_skip @@ -180,6 +183,16 @@ echo $[$servers * $server_strata + $clients] } +get_node_name() { + local index=$1 + + if [ $dns -ne 0 ]; then + echo "node$index.net1.clk" + else + echo "192.168.123.$index" + fi +} + get_chronyd_conf() { local i stratum=$1 peer=$2 @@ -188,25 +201,26 @@ echo "$server_conf" elif [ $stratum -le $server_strata ]; then for i in $(seq 1 $servers); do - echo "server 192.168.123.$[$servers * ($stratum - 2) + $i] $server_server_options" + echo "server $(get_node_name $[$servers * ($stratum - 2) + $i]) $server_server_options" done for i in $(seq 1 $peers); do [ $i -eq $peer -o $i -gt $servers ] && continue - echo -n "peer 192.168.123.$[$servers * ($stratum - 1) + $i] $server_peer_options " + echo -n "peer $(get_node_name $[$servers * ($stratum - 1) + $i]) $server_peer_options " [ $i -lt $peer ] && echo "$server_lpeer_options" || echo "$server_rpeer_options" done echo "$server_conf" else + echo "deny" if [ -n "$client_server_conf" ]; then echo "$client_server_conf" else for i in $(seq 1 $servers); do - echo "server 192.168.123.$[$servers * ($stratum - 2) + $i] $client_server_options" + echo "server $(get_node_name $[$servers * ($stratum - 2) + $i]) $client_server_options" done fi for i in $(seq 1 $peers); do [ $i -eq $peer -o $i -gt $clients ] && continue - echo -n "peer 192.168.123.$[$servers * ($stratum - 1) + $i] $client_peer_options " + echo -n "peer $(get_node_name $[$servers * ($stratum - 1) + $i]) $client_peer_options " [ $i -lt $peer ] && echo "$client_lpeer_options" || echo "$client_rpeer_options" done echo "$client_conf" @@ -263,6 +277,7 @@ grep -q 'chronyd exiting' tmp/log.$i && \ ! grep -q 'Adjustment.*exceeds.*exiting' tmp/log.$i && \ + ! grep -q 'Assertion.*failed' tmp/log.$i && \ test_ok || test_bad [ $? -eq 0 ] || ret=1 done @@ -391,9 +406,9 @@ for i in $(seq 1 $(get_chronyd_nodes)); do test_message 3 0 "node $i:" - grep -E -q " $port [0-9]+\$" tmp/log.packets && \ + grep -E -q "^([0-9e.+-]+ ){5}$port " tmp/log.packets && \ ! grep -E "^[0-9e.+-]+ $i " tmp/log.packets | \ - grep -E -q -v " $port [0-9]+\$" && \ + grep -E -q -v "^([0-9e.+-]+ ){5}$port " && \ test_ok || test_bad [ $? -eq 0 ] || ret=1 done @@ -437,7 +452,6 @@ for i in $(seq 1 $nodes); do echo "node${i}_shift_pll = $shift_pll" for j in $(seq 1 $nodes); do - [ $i -eq $j ] && continue echo "node${i}_delay${j} = $(get_delay_expr up)" echo "node${j}_delay${i} = $(get_delay_expr down)" done @@ -491,7 +505,7 @@ echo "node${node}_start = $chronyc_start" >> tmp/conf start_client $node chronyc "$chronyc_conf" "" \ - "-n -h 192.168.123.$[$node - $clients]" && \ + "$([ $dns -eq 0 ] && printf "%s" "-n") -h $(get_node_name $[$node - $clients]) $chronyc_options" && \ test_ok || test_error [ $? -ne 0 ] && return 1 diff -Nru chrony-3.5/test/system/005-scfilter chrony-4.1/test/system/005-scfilter --- chrony-3.5/test/system/005-scfilter 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/system/005-scfilter 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -. ./test.common - -check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled" - -test_start "system call filter" - -for extra_chronyd_options in "-F -1" "-F 1"; do - start_chronyd || test_fail - wait_for_sync || test_fail - stop_chronyd || test_fail - check_chronyd_messages || test_fail - check_chronyd_files || test_fail -done - -test_pass diff -Nru chrony-3.5/test/system/007-cmdmon chrony-4.1/test/system/007-cmdmon --- chrony-3.5/test/system/007-cmdmon 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/system/007-cmdmon 2021-05-12 11:06:15.000000000 +0000 @@ -5,65 +5,173 @@ test_start "chronyc commands" start_chronyd || test_fail +wait_for_sync || test_fail + +has_ipv6=$(check_chronyd_features IPV6 && ping6 -c 1 ::1 > /dev/null 2>&1 && echo 1 || echo 0) for command in \ - "accheck 1.2.3.4" \ - "delete $server" \ - "add server $server" \ - "deny" \ - "allow" \ - "burst 1/1" \ - "clients" \ - "cmdallow 1.2.3.4" \ - "cmdaccheck 1.2.3.4" \ + "allow 1.2.3.4" \ + "deny 1.2.3.4" \ + "cmddeny" \ + "cmdallow" \ "cmddeny 1.2.3.4" \ + "cmdallow 1.2.3.4" \ + "add server 127.123.1.1" \ + "delete 127.123.1.1" \ + "burst 1/1" \ "cyclelogs" \ "dfreq 1.0e-3" \ "doffset -0.1" \ "dump" \ + "offline" \ "local off" \ "local" \ - "manual on" \ - "settime now" \ - "manual delete 0" \ - "settime now" \ - "manual reset" \ - "manual off" \ - "maxdelay $server 1e-2" \ + "online" \ + "onoffline" \ + "maxdelay $server 1e-1" \ "maxdelaydevratio $server 5.0" \ "maxdelayratio $server 3.0" \ - "maxpoll $server 5" \ + "maxpoll $server 12" \ "maxupdateskew $server 10.0" \ - "minpoll $server 3" \ + "minpoll $server 10" \ "minstratum $server 1" \ - "ntpdata $server" \ - "offline" \ - "online" \ - "onoffline" \ "polltarget $server 10" \ "refresh" \ "rekey" \ + "reload sources" \ "reselect" \ "reselectdist 1e-3" \ - "serverstats" \ + "reset sources" \ "smoothtime reset" \ "smoothtime activate" \ - "shutdown" \ ; do run_chronyc "$command" || test_fail + check_chronyc_output "^200 OK$" || test_fail done +run_chronyc "accheck $server" || test_fail +check_chronyc_output "^208 Access allowed$" || test_fail +run_chronyc "accheck 1.2.3.4" || test_fail +check_chronyc_output "^209 Access denied$" || test_fail + +run_chronyc "cmdaccheck 1.2.3.4" || test_fail +check_chronyc_output "^208 Access allowed$" || test_fail + +run_chronyc "authdata" || test_fail +check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen +========================================================================= +127\.0\.0\.1 - 0 0 0 - 0 0 0 0$" \ + || test_chronyc + +run_chronyc "clients" || test_fail +check_chronyc_output "^Hostname NTP Drop Int IntL Last Cmd Drop Int Last +=============================================================================== +127\.0\.0\.1 [0-9 ]+ 0 [-0-9 ]+ - [ 0-9]+ 0 0 - -$" \ + || test_fail + +run_chronyc "ntpdata $server" || test_fail +check_chronyc_output "^Remote address : 127\.0\.0\.1 \(7F000001\) +Remote port : [0-9]+ +Local address : 127\.0\.0\.1 \(7F000001\) +Leap status : Normal +Version : 4 +Mode : Server +Stratum : 10 +Poll interval : (-6|[0-9]+) \([0-9]+ seconds\) +Precision : [0-9 +-]+ \(0\.[0-9]+ seconds\) +Root delay : 0\.000000 seconds +Root dispersion : 0\.000000 seconds +Reference ID : 7F7F0101 \(\) +Reference time : [A-Za-z0-9: ]+ +Offset : [+-]0\.......... seconds +Peer delay : 0\.......... seconds +Peer dispersion : 0\.......... seconds +Response time : 0\.......... seconds +Jitter asymmetry: \+0\.00 +NTP tests : 111 111 1110 +Interleaved : No +Authenticated : No +TX timestamping : (Daemon|Kernel) +RX timestamping : (Daemon|Kernel) +Total TX : [0-9]+ +Total RX : [0-9]+ +Total valid RX : [0-9]+$" || test_fail + +run_chronyc "selectdata" || test_fail +check_chronyc_output "^S Name/IP Address Auth COpts EOpts Last Score Interval Leap +======================================================================= +M 127\.0\.0\.1 N ----- ----- 0 1\.0 \+0ns \+0ns \?$" || test_fail + +run_chronyc "serverstats" || test_fail +check_chronyc_output "^NTP packets received : [0-9]+ +NTP packets dropped : 0 +Command packets received : [0-9]+ +Command packets dropped : 0 +Client log records dropped : 0 +NTS-KE connections accepted: 0 +NTS-KE connections dropped : 0 +Authenticated NTP packets : 0$" || test_fail + +run_chronyc "manual on" || test_fail +check_chronyc_output "^200 OK$" || test_fail + +run_chronyc "settime now" || test_fail +check_chronyc_output "^200 OK +Clock was.*$" || test_fail + +run_chronyc "manual delete 0" || test_fail +check_chronyc_output "^200 OK$" || test_fail + +run_chronyc "settime now" || test_fail +check_chronyc_output "^200 OK +Clock was.*$" || test_fail + +run_chronyc "manual list" || test_fail +check_chronyc_output "^210 n_samples = 1 +# Date Time\(UTC\) Slewed Original Residual +======================================================= + 0.*$" || test_fail + +run_chronyc "manual reset" || test_fail +check_chronyc_output "^200 OK$" || test_fail + +run_chronyc "manual off" || test_fail +check_chronyc_output "^200 OK$" || test_fail + +run_chronyc "shutdown" || test_fail +check_chronyc_output "^200 OK$" || test_fail + stop_chronyd || test_fail check_chronyd_messages || test_fail start_chronyd || test_fail run_chronyc "makestep" && test_fail check_chronyc_output "500 Failure" || test_fail + run_chronyc "trimrtc" && test_fail check_chronyc_output "513 RTC driver not running" || test_fail + run_chronyc "writertc" && test_fail check_chronyc_output "513 RTC driver not running" || test_fail +chronyc_host=127.0.0.1 + +run_chronyc "tracking" || test_fail +check_chronyc_output "^Reference ID" || test_fail + +run_chronyc "makestep" && test_fail +check_chronyc_output "^501 Not authorised$" || test_fail + +if [ "$has_ipv6" = "1" ]; then + chronyc_host=::1 + + run_chronyc "tracking" || test_fail + check_chronyc_output "^Reference ID" || test_fail + + run_chronyc "makestep" && test_fail + check_chronyc_output "^501 Not authorised$" || test_fail +fi + stop_chronyd || test_fail test_pass diff -Nru chrony-3.5/test/system/008-confload chrony-4.1/test/system/008-confload --- chrony-3.5/test/system/008-confload 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/system/008-confload 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,74 @@ +#!/usr/bin/env bash + +. ./test.common + +test_start "loading of configuration" + +minimal_config=1 +extra_chronyd_directives=" +include $TEST_DIR/conf1.d/conf.1 +confdir $TEST_DIR/conf1.d +confdir $TEST_DIR/conf2.d $TEST_DIR/conf3.d $TEST_DIR/conf4.d +sourcedir $TEST_DIR/conf5.d +include $TEST_DIR/conf1.d/conf.2" + +mkdir $TEST_DIR/conf{1,2,3,4,5}.d + +echo "server 127.123.1.1" > $TEST_DIR/conf1.d/conf.1 +echo "server 127.123.1.2" > $TEST_DIR/conf1.d/conf.2 +echo "server 127.123.1.3" > $TEST_DIR/conf1.d/3.conf +echo "server 127.123.1.4" > $TEST_DIR/conf1.d/4.conf +echo "server 127.123.2.2" > $TEST_DIR/conf2.d/2.conf +echo "server 127.123.2.3" > $TEST_DIR/conf2.d/3.conf +echo "server 127.123.3.1" > $TEST_DIR/conf3.d/1.conf +echo "server 127.123.3.2" > $TEST_DIR/conf3.d/2.conf +echo "server 127.123.3.3" > $TEST_DIR/conf3.d/3.conf +echo "server 127.123.4.1" > $TEST_DIR/conf4.d/1.conf +echo "server 127.123.4.2" > $TEST_DIR/conf4.d/2.conf +echo "server 127.123.4.3" > $TEST_DIR/conf4.d/3.conf +echo "server 127.123.4.4" > $TEST_DIR/conf4.d/4.conf +echo "server 127.123.5.1" > $TEST_DIR/conf5.d/1.sources +echo "server 127.123.5.2" > $TEST_DIR/conf5.d/2.sources +echo "server 127.123.5.3" > $TEST_DIR/conf5.d/3.sources + +start_chronyd || test_fail + +run_chronyc "sources" || test_fail +check_chronyc_output "^[^=]* +=* +.. 127\.123\.1\.1 [^^]* +.. 127\.123\.1\.3 [^^]* +.. 127\.123\.1\.4 [^^]* +.. 127\.123\.3\.1 [^^]* +.. 127\.123\.2\.2 [^^]* +.. 127\.123\.2\.3 [^^]* +.. 127\.123\.4\.4 [^^]* +.. 127\.123\.1\.2 [^^]* +.. 127\.123\.5\.1 [^^]* +.. 127\.123\.5\.2 [^^]* +.. 127\.123\.5\.3 [^^]*$" || test_fail + +rm $TEST_DIR/conf5.d/1.sources +echo "server 127.123.5.2 minpoll 7" > $TEST_DIR/conf5.d/2.sources +echo > $TEST_DIR/conf5.d/3.sources +echo "server 127.123.5.4" > $TEST_DIR/conf5.d/4.sources + +run_chronyc "reload sources" || test_fail + +run_chronyc "sources" || test_fail +check_chronyc_output "^[^=]* +=* +.. 127\.123\.1\.1 [^^]* +.. 127\.123\.1\.3 [^^]* +.. 127\.123\.1\.4 [^^]* +.. 127\.123\.3\.1 [^^]* +.. 127\.123\.2\.2 [^^]* +.. 127\.123\.2\.3 [^^]* +.. 127\.123\.4\.4 [^^]* +.. 127\.123\.1\.2 *[05] 6 [^^]* +.. 127\.123\.5\.2 *[05] 7 [^^]* +.. 127\.123\.5\.4 [^^]*$" || test_fail + +stop_chronyd || test_fail + +test_pass diff -Nru chrony-3.5/test/system/009-binddevice chrony-4.1/test/system/009-binddevice --- chrony-3.5/test/system/009-binddevice 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/system/009-binddevice 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +. ./test.common + +[ "$(uname -s)" = "Linux" ] || test_skip "non-Linux system" + +test_start "binddevice directives" + +extra_chronyd_directives=" +binddevice lo +bindacqdevice lo +bindcmddevice lo" + +start_chronyd || test_fail +wait_for_sync || test_fail + +run_chronyc "ntpdata $server" || test_fail +check_chronyc_output "^Remote address" || test_fail + +stop_chronyd || test_fail +check_chronyd_messages || test_fail +check_chronyd_files || test_fail + +test_pass diff -Nru chrony-3.5/test/system/010-nts chrony-4.1/test/system/010-nts --- chrony-3.5/test/system/010-nts 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/system/010-nts 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +. ./test.common + +check_chronyd_features NTS || test_skip "NTS support disabled" +certtool --help &> /dev/null || test_skip "certtool missing" + +test_start "NTS authentication" + +cat > $TEST_DIR/cert.cfg < $TEST_DIR/certtool.log +certtool --generate-self-signed --load-privkey $TEST_DIR/server.key \ + --template $TEST_DIR/cert.cfg --outfile $TEST_DIR/server.crt &>> $TEST_DIR/certtool.log +chown $user $TEST_DIR/server.* + +ntpport=$(get_free_port) +ntsport=$(get_free_port) + +server_options="port $ntpport nts ntsport $ntsport" +extra_chronyd_directives=" +port $ntpport +ntsport $ntsport +ntsserverkey $TEST_DIR/server.key +ntsservercert $TEST_DIR/server.crt +ntstrustedcerts $TEST_DIR/server.crt +ntsdumpdir $TEST_LIBDIR +ntsprocesses 3" + +start_chronyd || test_fail +wait_for_sync || test_fail + +run_chronyc "authdata" || test_fail +check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen +========================================================================= +127\.0\.0\.1 NTS 1 15 256 [0-9] 0 0 [78] 100$" || test_fail + +stop_chronyd || test_fail +check_chronyd_messages || test_fail +check_chronyd_files || test_fail + +server_options="port $ntpport nts ntsport $((ntsport + 1))" + +start_chronyd || test_fail +wait_for_sync || test_fail + +run_chronyc "authdata" || test_fail +check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen +========================================================================= +127\.0\.0\.1 NTS 1 15 256 [0-9] 0 0 [78] 100$" || test_fail + +stop_chronyd || test_fail +check_chronyd_messages || test_fail +check_chronyd_files || test_fail + +test_pass diff -Nru chrony-3.5/test/system/099-scfilter chrony-4.1/test/system/099-scfilter --- chrony-3.5/test/system/099-scfilter 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/system/099-scfilter 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +. ./test.common + +check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled" + +test_start "system call filter in non-destructive tests" + +for level in "-1" "1" "-2" "2"; do + test_message 1 1 "level $level:" + for test in 0[0-8][0-9]-*[^_]; do + test_message 2 0 "$test" + TEST_SCFILTER=$level "./$test" > /dev/null 2> /dev/null + result=$? + + if [ $result != 0 ] && [ $result != 9 ] ; then + test_bad + test_fail + fi + test_ok + done +done + +test_pass diff -Nru chrony-3.5/test/system/101-rtc chrony-4.1/test/system/101-rtc --- chrony-3.5/test/system/101-rtc 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/system/101-rtc 2021-05-12 11:06:15.000000000 +0000 @@ -14,6 +14,6 @@ start_chronyd || test_fail stop_chronyd || test_fail -check_chronyd_message_count "\(clock off from RTC\|RTC time before last\)" 1 1 || test_fail +check_chronyd_message_count "\(clock off from RTC\|RTC time before last\|Could not \(enable\|disable\) RTC interrupt\)" 1 1 || test_fail test_pass diff -Nru chrony-3.5/test/system/102-hwtimestamp chrony-4.1/test/system/102-hwtimestamp --- chrony-3.5/test/system/102-hwtimestamp 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/system/102-hwtimestamp 2021-05-12 11:06:15.000000000 +0000 @@ -7,7 +7,7 @@ hwts_iface="" for iface_path in /sys/class/net/*; do iface=$(basename "$iface_path") - if ethtool -T "$iface" 2> /dev/null | grep -q HWTSTAMP_FILTER_ALL; then + if ethtool -T "$iface" 2> /dev/null | grep -q ' all\($\| \)'; then hwts_iface="$iface" break fi diff -Nru chrony-3.5/test/system/199-scfilter chrony-4.1/test/system/199-scfilter --- chrony-3.5/test/system/199-scfilter 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/system/199-scfilter 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +. ./test.common + +check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled" + +test_start "system call filter in destructive tests" + +for level in "-1" "1" "-2" "2"; do + test_message 1 1 "level $level:" + for test in 1[0-8][0-9]-*[^_]; do + test_message 2 0 "$test" + TEST_SCFILTER=$level "./$test" > /dev/null 2> /dev/null + result=$? + + if [ $result != 0 ] && [ $result != 9 ] ; then + test_bad + test_fail + fi + test_ok + done +done + +test_pass diff -Nru chrony-3.5/test/system/test.common chrony-4.1/test/system/test.common --- chrony-3.5/test/system/test.common 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/system/test.common 2021-05-12 11:06:15.000000000 +0000 @@ -20,6 +20,7 @@ TEST_LIBDIR=${TEST_LIBDIR:-$TEST_DIR} TEST_LOGDIR=${TEST_LOGDIR:-$TEST_DIR} TEST_RUNDIR=${TEST_RUNDIR:-$TEST_DIR} +TEST_SCFILTER=${TEST_SCFILTER:-0} test_start() { check_chronyd_features NTP CMDMON || test_skip "NTP/CMDMON support disabled" @@ -89,7 +90,13 @@ [ -x "$chronyd" ] || test_skip "chronyd not found" [ -x "$chronyc" ] || test_skip "chronyc not found" -netstat -aln > /dev/null 2> /dev/null || test_skip "missing netstat" +if netstat -aln > /dev/null 2> /dev/null; then + port_list_command="netstat -aln" +elif ss -atun > /dev/null 2> /dev/null; then + port_list_command="ss -atun" +else + test_skip "missing netstat or ss" +fi # Default test testings default_minimal_config=0 @@ -97,6 +104,8 @@ default_extra_chronyd_options="" default_clock_control=0 default_server=127.0.0.1 +default_server_name=127.0.0.1 +default_server_options="" default_user=root # Initialize test settings from their defaults @@ -178,7 +187,7 @@ while true; do port=$((RANDOM % 10000 + 10000)) - netstat -aln | grep '^udp.*:'$port && continue + $port_list_command | grep -q '^\(tcp\|udp\).*[:.]'"$port " && continue break done @@ -210,7 +219,7 @@ echo "cmdallow" echo "local" - echo "server $server port $ntpport minpoll -6 maxpoll -6" + echo "server $server_name port $ntpport minpoll -6 maxpoll -6 $server_options" [ "$server" = "127.0.0.1" ] && echo "bindacqaddress $server" echo "bindaddress 127.0.0.1" @@ -220,6 +229,8 @@ echo "log tempcomp rawmeasurements refclocks statistics tracking rtc" echo "logbanner 0" echo "smoothtime 100.0 0.001" + echo "leapsectz right/UTC" + echo "dscp 46" echo "include /dev/null" echo "keyfile $TEST_DIR/keys" @@ -234,6 +245,7 @@ echo "-l $(get_logfile)" echo "-f $(get_conffile)" echo "-u $user" + echo "-F $TEST_SCFILTER" echo "$extra_chronyd_options" } @@ -248,14 +260,31 @@ trap stop_chronyd EXIT + rm -f "$TEST_LOGDIR"/*.log + $CHRONYD_WRAPPER "$chronyd" $(get_chronyd_options) > "$TEST_DIR/chronyd.out" 2>&1 [ $? -eq 0 ] && [ -f "$pidfile" ] && ps -p "$(cat "$pidfile")" > /dev/null && test_ok || test_error } wait_for_sync() { + local prev_length + test_message 1 0 "waiting for synchronization" - sleep 1 && test_ok || test_error + prev_length=$msg_length + + for i in $(seq 1 10); do + run_chronyc "ntpdata $server" > /dev/null 2>&1 || break + if check_chronyc_output "Total RX +: [1-9]" > /dev/null 2>&1; then + msg_length=$prev_length + test_ok + return + fi + sleep 1 + done + + msg_length=$prev_length + test_error } # Stop the chronyd instance @@ -313,19 +342,24 @@ check_chronyd_files() { test_message 1 0 "checking chronyd files" - grep -q " $server .* 111 111 1111 " "$TEST_LOGDIR/measurements.log" && \ - grep -q " $server " "$TEST_LOGDIR/statistics.log" && \ - grep -q " $server " "$TEST_LOGDIR/tracking.log" && \ + grep -q " $server .* 111 111 1110 " "$TEST_LOGDIR/measurements.log" && \ [ -f "$TEST_LOGDIR/tempcomp.log" ] && [ "$(wc -l < "$TEST_LOGDIR/tempcomp.log")" -ge 2 ] && \ - [ -f "$TEST_RUNDIR/$server.dat" ] && [ "$(wc -l < "$TEST_RUNDIR/$server.dat")" -ge 5 ] && \ test_ok || test_bad } # Run a chronyc command run_chronyc() { - test_message 1 0 "running chronyc $*" + local host=$chronyc_host options="-n -m" + + test_message 1 0 "running chronyc $([ -n "$host" ] && echo "@$host ")$*" + + if [ -z "$host" ]; then + host="$(get_cmdsocket)" + else + options="$options -p $(grep cmdport "$(get_conffile)" | awk '{print $2}')" + fi - $CHRONYC_WRAPPER "$chronyc" -h "$(get_cmdsocket)" -n -m "$@" > "$TEST_DIR/chronyc.out" && \ + $CHRONYC_WRAPPER "$chronyc" -h "$host" $options "$@" > "$TEST_DIR/chronyc.out" && \ test_ok || test_error } diff -Nru chrony-3.5/test/unit/clientlog.c chrony-4.1/test/unit/clientlog.c --- chrony-3.5/test/unit/clientlog.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/unit/clientlog.c 2021-05-12 11:06:15.000000000 +0000 @@ -18,19 +18,25 @@ ********************************************************************** */ -#include +#include #include "test.h" +#if defined(FEAT_NTP) || defined(FEAT_CMDMON) + +#include + void test_unit(void) { int i, j, index; + CLG_Service s; struct timespec ts; IPAddr ip; char conf[][100] = { "clientloglimit 10000", "ratelimit interval 3 burst 4 leak 3", "cmdratelimit interval 3 burst 4 leak 3", + "ntsratelimit interval 6 burst 8 leak 3", }; CNF_Initialise(0, 0); @@ -51,15 +57,10 @@ TST_GetRandomAddress(&ip, IPADDR_UNSPEC, i % 8 ? -1 : i / 8 % 9); DEBUG_LOG("address %s", UTI_IPToString(&ip)); - if (random() % 2) { - index = CLG_LogNTPAccess(&ip, &ts); - TEST_CHECK(index >= 0); - CLG_LimitNTPResponseRate(index); - } else { - index = CLG_LogCommandAccess(&ip, &ts); - TEST_CHECK(index >= 0); - CLG_LimitCommandResponseRate(index); - } + s = random() % MAX_SERVICES; + index = CLG_LogServiceAccess(s, &ip, &ts); + TEST_CHECK(index >= 0); + CLG_LimitServiceRate(s, index); UTI_AddDoubleToTimespec(&ts, (1 << random() % 14) / 100.0, &ts); } @@ -68,11 +69,13 @@ DEBUG_LOG("records %u", ARR_GetSize(records)); TEST_CHECK(ARR_GetSize(records) == 64); + s = CLG_NTP; + for (i = j = 0; i < 10000; i++) { ts.tv_sec += 1; - index = CLG_LogNTPAccess(&ip, &ts); + index = CLG_LogServiceAccess(s, &ip, &ts); TEST_CHECK(index >= 0); - if (!CLG_LimitNTPResponseRate(index)) + if (!CLG_LimitServiceRate(s, index)) j++; } @@ -82,3 +85,10 @@ CLG_Finalise(); CNF_Finalise(); } +#else +void +test_unit(void) +{ + TEST_REQUIRE(0); +} +#endif diff -Nru chrony-3.5/test/unit/cmac.c chrony-4.1/test/unit/cmac.c --- chrony-3.5/test/unit/cmac.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/unit/cmac.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,109 @@ +/* + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + */ + +#include +#include +#include +#include +#include +#include "test.h" + +#define MAX_KEY_LENGTH 64 +#define MAX_HASH_LENGTH 64 + +struct cmac_test { + const char *name; + const unsigned char key[MAX_KEY_LENGTH]; + int key_length; + const unsigned char hash[MAX_HASH_LENGTH]; + int hash_length; +}; + +void +test_unit(void) +{ + unsigned char data[] = "abcdefghijklmnopqrstuvwxyz0123456789"; + unsigned char hash[MAX_HASH_LENGTH]; + struct cmac_test tests[] = { + { "AES128", "\xfc\x24\x97\x1b\x52\x66\xdc\x46\xef\xe0\xe8\x08\x46\x89\xb6\x88", 16, + "\xaf\x3c\xfe\xc2\x66\x71\x08\x04\xd4\xaf\x5b\x16\x2b\x11\xf4\x85", 16 }, + { "AES256", "\x14\x02\x8e\x7d\x17\x3c\x2f\x4e\x17\x0f\x37\x96\xc3\x2c\xc5\x99" + "\x18\xdd\x55\x23\xb7\xd7\x9b\xc5\x76\x36\x88\x3f\xc5\x82\xb5\x83", 32, + "\xfe\xf7\x94\x96\x14\x04\x11\x0b\x87\xe4\xd4\x3f\x81\xb3\xb2\x2d", 16 }, + { "", "", 0, "", 0 } + }; + + CMC_Algorithm algorithm; + CMC_Instance inst; + int i, j, length; + +#ifndef HAVE_CMAC + TEST_REQUIRE(0); +#endif + + TEST_CHECK(CMC_INVALID == 0); + + for (i = 0; tests[i].name[0] != '\0'; i++) { + algorithm = UTI_CmacNameToAlgorithm(tests[i].name); + TEST_CHECK(algorithm != 0); + TEST_CHECK(CMC_GetKeyLength(algorithm) == tests[i].key_length); + + DEBUG_LOG("testing %s", tests[i].name); + + for (j = -1; j <= 128; j++) { + if (j == tests[i].key_length) + continue; + TEST_CHECK(!CMC_CreateInstance(algorithm, tests[i].key, j)); + } + + inst = CMC_CreateInstance(algorithm, tests[i].key, tests[i].key_length); + TEST_CHECK(inst); + + TEST_CHECK(!CMC_CreateInstance(0, tests[i].key, tests[i].key_length)); + + TEST_CHECK(CMC_Hash(inst, data, -1, hash, sizeof (hash)) == 0); + TEST_CHECK(CMC_Hash(inst, data, sizeof (data) - 1, hash, -1) == 0); + + for (j = 0; j <= sizeof (hash); j++) { + memset(hash, 0, sizeof (hash)); + length = CMC_Hash(inst, data, sizeof (data) - 1, hash, j); + +#if 0 + for (int k = 0; k < length; k++) + printf("\\x%02x", hash[k]); + printf("\n"); +#endif + + if (j >= tests[i].hash_length) + TEST_CHECK(length == tests[i].hash_length); + else + TEST_CHECK(length == j); + + TEST_CHECK(!memcmp(hash, tests[i].hash, length)); + } + + for (j = 0; j < sizeof (data); j++) { + length = CMC_Hash(inst, data, j, hash, sizeof (hash)); + TEST_CHECK(length == tests[i].hash_length); + } + + CMC_DestroyInstance(inst); + } +} diff -Nru chrony-3.5/test/unit/hash.c chrony-4.1/test/unit/hash.c --- chrony-3.5/test/unit/hash.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/unit/hash.c 2021-05-12 11:06:15.000000000 +0000 @@ -22,12 +22,13 @@ #include #include #include +#include #include "test.h" struct hash_test { const char *name; const unsigned char out[MAX_HASH_LENGTH]; - unsigned int length; + int length; }; void @@ -60,14 +61,6 @@ "\x39\xfc\xcb\xc1\x29\xe1\x23\x7d\x8b\x56\x54\xe3\x08\x9d\xf9\x74" "\x78\x69\x2e\x3c\x7e\x51\x1e\x9d\xab\x09\xbe\xe7\x6b\x1a\xa1\x22" "\x93\xb1\x2b\x82\x9d\x1e\xcf\xa8\x99\xc5\xec\x7b\x1d\x89\x07\x2b", 64 }, - { "RMD128", "\x6f\xd7\x1f\x37\x47\x0f\xbd\x42\x57\xc8\xbb\xee\xba\x65\xf9\x35", 16 }, - { "RMD160", "\x7a\x88\xec\xc7\x09\xc5\x65\x34\x11\x24\xe3\xf9\xf7\xa5\xbf\xc6" - "\x01\xe2\xc9\x32", 20}, - { "RMD256", "\x59\xdf\xd4\xcb\xc9\xbe\x7c\x27\x08\xa7\x23\xf7\xb3\x0c\xf0\x0d" - "\xa0\xcf\x5b\x18\x16\x51\x56\x6d\xda\x7b\x87\x24\x9d\x83\x35\xe1", 32 }, - { "RMD320", "\x68\x98\x10\xf4\xb6\x79\xb6\x15\xf1\x48\x2d\x73\xd0\x23\x84\x01" - "\xbf\xaa\x67\xcf\x1e\x35\x5c\xbf\xe9\xb8\xaf\xe1\xee\x0d\xf0\x6b" - "\xe2\x3a\x9a\x3a\xa7\x56\xad\x70", 40}, { "TIGER", "\x1c\xcd\x68\x74\xca\xd6\xd5\x17\xba\x3e\x82\xaf\xbd\x70\xdc\x66" "\x99\xaa\xae\x16\x72\x59\xd1\x64", 24}, { "WHIRLPOOL", "\xe3\xcd\xe6\xbf\xe1\x8c\xe4\x4d\xc8\xb4\xa5\x7c\x36\x8d\xc8\x8a" @@ -77,27 +70,35 @@ { "", "", 0 } }; - unsigned int length; - int i, j, hash_id; + HSH_Algorithm algorithm; + int i, j, hash_id, length; + + TEST_CHECK(HSH_INVALID == 0); for (i = 0; tests[i].name[0] != '\0'; i++) { - hash_id = HSH_GetHashId(tests[i].name); + algorithm = UTI_HashNameToAlgorithm(tests[i].name); + TEST_CHECK(algorithm != 0); + hash_id = HSH_GetHashId(algorithm); if (hash_id < 0) { - TEST_CHECK(strcmp(tests[i].name, "MD5")); + TEST_CHECK(algorithm != HSH_MD5); #ifdef FEAT_SECHASH - TEST_CHECK(strcmp(tests[i].name, "SHA1")); - TEST_CHECK(strcmp(tests[i].name, "SHA256")); - TEST_CHECK(strcmp(tests[i].name, "SHA384")); - TEST_CHECK(strcmp(tests[i].name, "SHA512")); + TEST_CHECK(algorithm != HSH_SHA1); + TEST_CHECK(algorithm != HSH_SHA256); + TEST_CHECK(algorithm != HSH_SHA384); + TEST_CHECK(algorithm != HSH_SHA512); #endif continue; } DEBUG_LOG("testing %s", tests[i].name); + TEST_CHECK(HSH_Hash(hash_id, data1, -1, NULL, 0, out, sizeof (out)) == 0); + TEST_CHECK(HSH_Hash(hash_id, data1, sizeof (data1) - 1, data2, -1, out, sizeof (out)) == 0); + TEST_CHECK(HSH_Hash(hash_id, data1, sizeof (data1) - 1, NULL, 0, out, -1) == 0); + for (j = 0; j <= sizeof (out); j++) { - TEST_CHECK(HSH_GetHashId(tests[i].name) == hash_id); - TEST_CHECK(HSH_GetHashId("nosuchhash") < 0); + TEST_CHECK(HSH_GetHashId(algorithm) == hash_id); + TEST_CHECK(HSH_GetHashId(0) < 0); memset(out, 0, sizeof (out)); length = HSH_Hash(hash_id, data1, sizeof (data1) - 1, data2, sizeof (data2) - 1, diff -Nru chrony-3.5/test/unit/keys.c chrony-4.1/test/unit/keys.c --- chrony-3.5/test/unit/keys.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/unit/keys.c 2021-05-12 11:06:15.000000000 +0000 @@ -18,48 +18,64 @@ ********************************************************************** */ -#include +#include #include "test.h" +#if defined(FEAT_NTP) || defined(FEAT_CMDMON) + +#include + #define KEYS 100 #define KEYFILE "keys.test-keys" static uint32_t write_random_key(FILE *f) { - const char *hash_name; + const char *type, *prefix; char key[128]; uint32_t id; int i, length; length = random() % sizeof (key) + 1; length = MAX(length, 4); - UTI_GetRandomBytes(&id, sizeof (id)); - UTI_GetRandomBytes(key, length); + prefix = random() % 2 ? "HEX:" : ""; - switch (random() % 6) { + switch (random() % 8) { #ifdef FEAT_SECHASH case 0: - hash_name = "SHA1"; + type = "SHA1"; break; case 1: - hash_name = "SHA256"; + type = "SHA256"; break; case 2: - hash_name = "SHA384"; + type = "SHA384"; break; case 3: - hash_name = "SHA512"; + type = "SHA512"; break; #endif +#ifdef HAVE_CMAC case 4: - hash_name = "MD5"; + type = "AES128"; + length = prefix[0] == '\0' ? 8 : 16; + break; + case 5: + type = "AES256"; + length = prefix[0] == '\0' ? 16 : 32; + break; +#endif + case 6: + type = "MD5"; break; default: - hash_name = ""; + type = ""; } - fprintf(f, "%u %s %s", id, hash_name, random() % 2 ? "HEX:" : ""); + UTI_GetRandomBytes(&id, sizeof (id)); + UTI_GetRandomBytes(key, length); + + fprintf(f, "%u %s %s", id, type, prefix); for (i = 0; i < length; i++) fprintf(f, "%02hhX", key[i]); fprintf(f, "\n"); @@ -83,7 +99,7 @@ void test_unit(void) { - int i, j, data_len, auth_len; + int i, j, data_len, auth_len, type, bits; uint32_t keys[KEYS], key; unsigned char data[100], auth[MAX_HASH_LENGTH]; char conf[][100] = { @@ -109,7 +125,6 @@ for (j = 0; j < KEYS; j++) { TEST_CHECK(KEY_KeyKnown(keys[j])); - TEST_CHECK(KEY_GetAuthDelay(keys[j]) >= 0); TEST_CHECK(KEY_GetAuthLength(keys[j]) >= 16); data_len = random() % (sizeof (data) + 1); @@ -128,12 +143,16 @@ auth[auth_len - 1]++; TEST_CHECK(!KEY_CheckAuth(keys[j], data, data_len, auth, auth_len, auth_len)); + + TEST_CHECK(KEY_GetKeyInfo(keys[j], &type, &bits)); + TEST_CHECK(type > 0 && bits > 0); } for (j = 0; j < 1000; j++) { UTI_GetRandomBytes(&key, sizeof (key)); if (KEY_KeyKnown(key)) continue; + TEST_CHECK(!KEY_GetKeyInfo(key, &type, &bits)); TEST_CHECK(!KEY_GenerateAuth(key, data, data_len, auth, sizeof (auth))); TEST_CHECK(!KEY_CheckAuth(key, data, data_len, auth, auth_len, auth_len)); } @@ -145,3 +164,10 @@ CNF_Finalise(); HSH_Finalise(); } +#else +void +test_unit(void) +{ + TEST_REQUIRE(0); +} +#endif diff -Nru chrony-3.5/test/unit/Makefile.in chrony-4.1/test/unit/Makefile.in --- chrony-3.5/test/unit/Makefile.in 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/unit/Makefile.in 2021-05-12 11:06:15.000000000 +0000 @@ -12,7 +12,8 @@ TESTS := $(patsubst %.o,%.test,$(filter-out $(SHARED_OBJS),$(TEST_OBJS))) CHRONYD_OBJS := $(patsubst %.o,$(CHRONY_SRCDIR)/%.o,$(filter-out main.o,\ - $(filter %.o,$(shell $(MAKE) -f $(CHRONY_SRCDIR)/Makefile print-chronyd-objects)))) + $(filter %.o,$(shell $(MAKE) -f $(CHRONY_SRCDIR)/Makefile \ + print-chronyd-objects NODEPS=1)))) all: $(TESTS) diff -Nru chrony-3.5/test/unit/ntp_auth.c chrony-4.1/test/unit/ntp_auth.c --- chrony-3.5/test/unit/ntp_auth.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/unit/ntp_auth.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,295 @@ +/* + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "test.h" + +#ifdef FEAT_NTP + +#include + +static void +prepare_packet(NTP_AuthMode auth_mode, NTP_Packet *packet, NTP_PacketInfo *info, int req) +{ + unsigned char buf[64]; + int i, version; + NTP_Mode mode; + + switch (auth_mode) { + case NTP_AUTH_MSSNTP: + case NTP_AUTH_MSSNTP_EXT: + version = 3; + mode = random() % 2 ? (req ? MODE_CLIENT : MODE_SERVER) : + (req ? MODE_ACTIVE : MODE_PASSIVE); + break; + case NTP_AUTH_NTS: + version = 4; + mode = req ? MODE_CLIENT : MODE_SERVER; + break; + default: + version = 3 + random() % 2; + mode = random() % 2 ? (req ? MODE_CLIENT : MODE_SERVER) : + (req ? MODE_ACTIVE : MODE_PASSIVE); + break; + } + + memset(packet, 0, sizeof (*packet)); + memset(info, 0, sizeof (*info)); + packet->lvm = NTP_LVM(LEAP_Normal, version, mode); + info->length = NTP_HEADER_LENGTH; + info->version = version; + info->mode = mode; + + if (version == 4) { + memset(buf, 0, sizeof (buf)); + for (i = random() % 5; i > 0; i--) + TEST_CHECK(NEF_AddField(packet, info, 0, buf, sizeof (buf))); + } +} + +static void +add_dummy_auth(NTP_AuthMode auth_mode, uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info) +{ + unsigned char buf[64]; + int len, fill; + + info->auth.mode = auth_mode; + + switch (auth_mode) { + case NTP_AUTH_NONE: + break; + case NTP_AUTH_SYMMETRIC: + case NTP_AUTH_MSSNTP: + case NTP_AUTH_MSSNTP_EXT: + switch (auth_mode) { + case NTP_AUTH_SYMMETRIC: + len = 16 + random() % 2 * 4; + fill = 1 + random() % 255; + break; + case NTP_AUTH_MSSNTP: + len = 16; + fill = 0; + break; + case NTP_AUTH_MSSNTP_EXT: + len = 68; + fill = 0; + break; + default: + assert(0); + } + + assert(info->length + 4 + len <= sizeof (*packet)); + + *(uint32_t *)((unsigned char *)packet + info->length) = htonl(key_id); + info->auth.mac.key_id = key_id; + info->length += 4; + + memset((unsigned char *)packet + info->length, fill, len); + info->length += len; + break; + case NTP_AUTH_NTS: + memset(buf, 0, sizeof (buf)); + TEST_CHECK(NEF_AddField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, buf, sizeof (buf))); + break; + default: + assert(0); + } +} + +void +test_unit(void) +{ + int i, j, can_auth_req, can_auth_res; + NTP_PacketInfo req_info, res_info; + NTP_Packet req, res; + NAU_Instance inst; + RPT_AuthReport report; + NTP_AuthMode mode; + IPSockAddr nts_addr; + uint32_t key_id, kod; + char conf[][100] = { + "keyfile ntp_core.keys" + }; + + CNF_Initialise(0, 0); + for (i = 0; i < sizeof conf / sizeof conf[0]; i++) + CNF_ParseLine(NULL, i + 1, conf[i]); + + LCL_Initialise(); + KEY_Initialise(); + NSD_Initialise(); + NNS_Initialise(); + + SCK_GetAnyLocalIPAddress(IPADDR_INET4, &nts_addr.ip_addr); + nts_addr.port = 0; + + for (i = 0; i < 1000; i++) { + key_id = INACTIVE_AUTHKEY; + + switch (i % 5) { + case 0: + inst = NAU_CreateNoneInstance(); + TEST_CHECK(!NAU_IsAuthEnabled(inst)); + TEST_CHECK(NAU_GetSuggestedNtpVersion(inst) == 4); + mode = NTP_AUTH_NONE; + can_auth_req = 1; + can_auth_res = 1; + break; + case 1: + key_id = random() % 7 + 2; + inst = NAU_CreateSymmetricInstance(key_id); + TEST_CHECK(NAU_IsAuthEnabled(inst)); + TEST_CHECK(NAU_GetSuggestedNtpVersion(inst) == + (KEY_KeyKnown(inst->key_id) && KEY_GetAuthLength(inst->key_id) > 20 ? 3 : 4)); + mode = NTP_AUTH_SYMMETRIC; + can_auth_req = KEY_KeyKnown(key_id); + can_auth_res = can_auth_req; + break; + case 2: + inst = NAU_CreateNtsInstance(&nts_addr, "test", 0, 0); + TEST_CHECK(NAU_IsAuthEnabled(inst)); + TEST_CHECK(NAU_GetSuggestedNtpVersion(inst) == 4); + mode = NTP_AUTH_NTS; + can_auth_req = 0; + can_auth_res = 0; + break; + case 3: + key_id = 1 + random() % 100; + inst = NULL; + mode = NTP_AUTH_MSSNTP; + can_auth_req = 1; + can_auth_res = 0; + break; + case 4: + key_id = 1 + random() % 100; + inst = NULL; + mode = NTP_AUTH_MSSNTP_EXT; + can_auth_req = 0; + can_auth_res = 0; + break; + default: + assert(0); + } + + DEBUG_LOG("iteration %d auth=%d key_id=%d", i, (int)mode, (int)key_id); + + prepare_packet(mode, &req, &req_info, 1); + + if (inst) { + TEST_CHECK(inst->mode == mode); + TEST_CHECK(inst->key_id == key_id); + + NAU_DumpData(inst); + NAU_GetReport(inst, &report); + if (random() % 2) + NAU_ChangeAddress(inst, &nts_addr.ip_addr); + + if (inst->mode == NTP_AUTH_NTS) { + for (j = random() % 5; j > 0; j--) +#ifdef FEAT_NTS + TEST_CHECK(!NAU_PrepareRequestAuth(inst)); +#else + TEST_CHECK(NAU_PrepareRequestAuth(inst)); +#endif + TEST_CHECK(!NAU_GenerateRequestAuth(inst, &req, &req_info)); + } else if (can_auth_req) { + TEST_CHECK(NAU_PrepareRequestAuth(inst)); + TEST_CHECK(NAU_GenerateRequestAuth(inst, &req, &req_info)); + } else { + TEST_CHECK(NAU_PrepareRequestAuth(inst)); + TEST_CHECK(!NAU_GenerateRequestAuth(inst, &req, &req_info)); + } + } + + if (!inst || !can_auth_req) + add_dummy_auth(mode, key_id, &req, &req_info); + + TEST_CHECK(req_info.auth.mode == mode); + + memset(&req_info.auth, 0, sizeof (req_info.auth)); + TEST_CHECK(NAU_ParsePacket(&req, &req_info)); + TEST_CHECK(req_info.auth.mode == mode); + TEST_CHECK(req_info.auth.mac.key_id == key_id); + + kod = 1; + TEST_CHECK(NAU_CheckRequestAuth(&req, &req_info, &kod) == can_auth_req); + TEST_CHECK(kod == 0); + + if (inst) { + for (j = NTP_AUTH_NONE; j <= NTP_AUTH_NTS; j++) { + if (j == mode && j == NTP_AUTH_NONE) + continue; + + prepare_packet(j, &res, &res_info, 0); + add_dummy_auth(j, key_id ? key_id : 1, &res, &res_info); + + TEST_CHECK(res_info.auth.mode == j); + TEST_CHECK(!NAU_CheckResponseAuth(inst, &res, &res_info)); + } + } + + prepare_packet(mode, &res, &res_info, 0); + TEST_CHECK(NAU_GenerateResponseAuth(&req, &req_info, &res, &res_info, NULL, NULL, kod) == + can_auth_res); + if (!can_auth_res) + add_dummy_auth(mode, key_id, &res, &res_info); + + memset(&res_info.auth, 0, sizeof (res_info.auth)); + TEST_CHECK(NAU_ParsePacket(&res, &res_info)); + TEST_CHECK(res_info.auth.mode == mode); + TEST_CHECK(res_info.auth.mac.key_id == key_id); + + if (inst) { + if (mode == NTP_AUTH_SYMMETRIC) { + res_info.auth.mac.key_id ^= 1; + TEST_CHECK(!NAU_CheckResponseAuth(inst, &res, &res_info)); + res_info.auth.mac.key_id ^= 1; + } + + TEST_CHECK(NAU_CheckResponseAuth(inst, &res, &res_info) == can_auth_res); + + NAU_GetReport(inst, &report); + NAU_DestroyInstance(inst); + } + } + + NNS_Finalise(); + NSD_Finalise(); + KEY_Finalise(); + LCL_Finalise(); + CNF_Finalise(); + HSH_Finalise(); +} + +#else +void +test_unit(void) +{ + TEST_REQUIRE(0); +} +#endif diff -Nru chrony-3.5/test/unit/ntp_core.c chrony-4.1/test/unit/ntp_core.c --- chrony-3.5/test/unit/ntp_core.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/unit/ntp_core.c 2021-05-12 11:06:15.000000000 +0000 @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +32,7 @@ #ifdef FEAT_NTP static struct timespec current_time; -static NTP_Receive_Buffer req_buffer, res_buffer; +static NTP_Packet req_buffer, res_buffer; static int req_length, res_length; #define NIO_OpenServerSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 100 : 0) @@ -39,6 +40,7 @@ #define NIO_OpenClientSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 101 : 0) #define NIO_CloseClientSocket(fd) assert(fd == 101) #define NIO_IsServerSocket(fd) (fd == 100) +#define NIO_IsServerSocketOpen() 1 #define NIO_SendPacket(msg, to, from, len, process_tx) (memcpy(&req_buffer, msg, len), req_length = len, 1) #define SCH_AddTimeoutByDelay(delay, handler, arg) (1 ? 102 : (handler(arg), 1)) #define SCH_AddTimeoutInClass(delay, separation, randomness, class, handler, arg) \ @@ -71,7 +73,7 @@ uint32_t id; do { - id = random() % 6 + 2; + id = random() % 8 + 2; } while (!KEY_KeyKnown(id)); return id; @@ -100,7 +102,7 @@ local_ts.err = 0.0; local_ts.source = NTP_TS_KERNEL; - NCR_ProcessTxKnown(inst, &local_addr, &local_ts, &req_buffer.ntp_pkt, req_length); + NCR_ProcessTxKnown(inst, &local_addr, &local_ts, &req_buffer, req_length); } } @@ -119,7 +121,7 @@ res_length = 0; NCR_ProcessRxUnknown(remote_addr, &local_addr, &local_ts, - &req_buffer.ntp_pkt, req_length); + &req_buffer, req_length); res_length = req_length; res_buffer = req_buffer; @@ -128,7 +130,7 @@ if (random() % 2) { local_ts.ts = current_time; NCR_ProcessTxUnknown(remote_addr, &local_addr, &local_ts, - &res_buffer.ntp_pkt, res_length); + &res_buffer, res_length); } } @@ -136,12 +138,13 @@ send_response(int interleaved, int authenticated, int allow_update, int valid_ts, int valid_auth) { NTP_Packet *req, *res; - int auth_len = 0; + uint32_t key_id = 0; + int i, auth_len = 0, ef_len, efs; - req = &req_buffer.ntp_pkt; - res = &res_buffer.ntp_pkt; + req = &req_buffer; + res = &res_buffer; - TEST_CHECK(req_length >= NTP_NORMAL_PACKET_LENGTH); + TEST_CHECK(req_length >= NTP_HEADER_LENGTH); res->lvm = NTP_LVM(LEAP_Normal, NTP_LVM_TO_VERSION(req->lvm), NTP_LVM_TO_MODE(req->lvm) == MODE_CLIENT ? MODE_SERVER : MODE_ACTIVE); @@ -182,60 +185,98 @@ } } + res_length = NTP_HEADER_LENGTH; + + if (NTP_LVM_TO_VERSION(res->lvm) == 4 && random() % 2) { + unsigned char buf[128]; + + memset(buf, 0, sizeof (buf)); + efs = random() % 5; + + for (i = 0; i < efs; i++) { + ef_len = (i + 1 == efs ? NTP_MAX_V4_MAC_LENGTH + 4 : NTP_MIN_EF_LENGTH) + + 4 * (random() % 10); + TEST_CHECK(NEF_SetField((unsigned char *)res, sizeof (*res), res_length, 0, + buf, ef_len - 4, &ef_len)); + res_length += ef_len; + } + } + if (authenticated) { - res->auth_keyid = req->auth_keyid ? req->auth_keyid : htonl(get_random_key_id()); - auth_len = KEY_GetAuthLength(ntohl(res->auth_keyid)); + key_id = ntohl(*(uint32_t *)req->extensions); + if (key_id == 0) + key_id = get_random_key_id(); + auth_len = KEY_GetAuthLength(key_id); assert(auth_len); - if (NTP_LVM_TO_VERSION(res->lvm) == 4 && random() % 2) + if (NTP_LVM_TO_VERSION(res->lvm) == 4) auth_len = MIN(auth_len, NTP_MAX_V4_MAC_LENGTH - 4); - if (KEY_GenerateAuth(ntohl(res->auth_keyid), (unsigned char *)res, - NTP_NORMAL_PACKET_LENGTH, res->auth_data, auth_len) != auth_len) + if (KEY_GenerateAuth(key_id, res, res_length, + (unsigned char *)res + res_length + 4, auth_len) != auth_len) assert(0); - res_length = NTP_NORMAL_PACKET_LENGTH + 4 + auth_len; - } else { - res_length = NTP_NORMAL_PACKET_LENGTH; + res_length += 4 + auth_len; } if (!valid_auth && authenticated) { assert(auth_len); - switch (random() % 4) { + switch (random() % 5) { case 0: - res->auth_keyid = htonl(ntohl(res->auth_keyid) + 1); + key_id++; break; case 1: - res->auth_keyid = htonl(ntohl(res->auth_keyid) ^ 1); - if (KEY_GenerateAuth(ntohl(res->auth_keyid), (unsigned char *)res, - NTP_NORMAL_PACKET_LENGTH, res->auth_data, auth_len) != auth_len) + key_id ^= 1; + if (KEY_GenerateAuth(key_id, res, res_length - auth_len - 4, + (unsigned char *)res + res_length - auth_len, auth_len) != auth_len) assert(0); break; case 2: - res->auth_data[random() % auth_len]++; + ((unsigned char *)res)[res_length - auth_len + random() % auth_len]++; break; case 3: - res_length = NTP_NORMAL_PACKET_LENGTH + 4 * (random() % ((4 + auth_len) / 4)); - if (NTP_LVM_TO_VERSION(res->lvm) == 4 && - res_length == NTP_NORMAL_PACKET_LENGTH + NTP_MAX_V4_MAC_LENGTH) - res_length -= 4; + res_length -= 4 + auth_len; + auth_len = 4 * (random() % (auth_len / 4)); + res_length += 4 + auth_len; + break; + case 4: + if (NTP_LVM_TO_VERSION(res->lvm) == 4 && random() % 2 && + KEY_GetAuthLength(key_id) > NTP_MAX_V4_MAC_LENGTH - 4) { + res_length -= 4 + auth_len; + auth_len += 4 + 4 * (random() % + ((KEY_GetAuthLength(key_id) - NTP_MAX_V4_MAC_LENGTH - 4) / 4)); + if (KEY_GenerateAuth(key_id, res, res_length, + (unsigned char *)res + res_length + 4, auth_len) != auth_len) + assert(0); + res_length += 4 + auth_len; + } else { + memset((unsigned char *)res + res_length, 0, 4); + auth_len += 4; + res_length += 4; + } break; default: assert(0); } } + + assert(res_length <= sizeof (*res)); + assert(res_length >= NTP_HEADER_LENGTH + auth_len); + + if (authenticated) + *(uint32_t *)((unsigned char *)res + res_length - auth_len - 4) = htonl(key_id); } static void -process_response(NCR_Instance inst, int good, int valid, int updated_sync, int updated_init) +proc_response(NCR_Instance inst, int good, int valid, int updated_sync, int updated_init) { NTP_Local_Address local_addr; NTP_Local_Timestamp local_ts; NTP_Packet *res; uint32_t prev_rx_count, prev_valid_count; struct timespec prev_rx_ts, prev_init_rx_ts; - int prev_open_socket, ret; + int ret; - res = &res_buffer.ntp_pkt; + res = &res_buffer; local_addr.ip_addr.family = IPADDR_UNSPEC; local_addr.if_index = INVALID_IF_INDEX; @@ -248,7 +289,6 @@ prev_valid_count = inst->report.total_valid_count; prev_rx_ts = inst->local_rx.ts; prev_init_rx_ts = inst->init_local_rx.ts; - prev_open_socket = inst->local_addr.sock_fd != INVALID_SOCK_FD; ret = NCR_ProcessRxKnown(inst, &local_addr, &local_ts, res, res_length); @@ -257,10 +297,7 @@ else if (!good) TEST_CHECK(!ret); - if (prev_open_socket) - TEST_CHECK(prev_rx_count + 1 == inst->report.total_rx_count); - else - TEST_CHECK(prev_rx_count == inst->report.total_rx_count); + TEST_CHECK(prev_rx_count + 1 == inst->report.total_rx_count); if (valid) TEST_CHECK(prev_valid_count + 1 == inst->report.total_valid_count); @@ -284,14 +321,13 @@ } static void -process_replay(NCR_Instance inst, NTP_Receive_Buffer *packet_queue, +process_replay(NCR_Instance inst, NTP_Packet *packet_queue, int queue_length, int updated_init) { do { res_buffer = packet_queue[random() % queue_length]; - } while (!UTI_CompareNtp64(&res_buffer.ntp_pkt.transmit_ts, - &inst->remote_ntp_tx)); - process_response(inst, 0, 0, 0, updated_init); + } while (!UTI_CompareNtp64(&res_buffer.transmit_ts, &inst->remote_ntp_tx)); + proc_response(inst, 0, 0, 0, updated_init); advance_time(1e-6); } @@ -311,7 +347,7 @@ CPS_NTP_Source source; NTP_Remote_Address remote_addr; NCR_Instance inst1, inst2; - NTP_Receive_Buffer packet_queue[PACKET_QUEUE_LENGTH]; + NTP_Packet packet_queue[PACKET_QUEUE_LENGTH]; CNF_Initialise(0, 0); for (i = 0; i < sizeof conf / sizeof conf[0]; i++) @@ -321,13 +357,10 @@ TST_RegisterDummyDrivers(); SCH_Initialise(); SRC_Initialise(); - NIO_Initialise(IPADDR_UNSPEC); + NIO_Initialise(); NCR_Initialise(); REF_Initialise(); - - TST_SuspendLogging(); KEY_Initialise(); - TST_ResumeLogging(); CNF_SetupAccessRestrictions(); @@ -344,7 +377,8 @@ TST_GetRandomAddress(&remote_addr.ip_addr, IPADDR_UNSPEC, -1); remote_addr.port = 123; - inst1 = NCR_GetInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, &source.params); + inst1 = NCR_CreateInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, + &source.params, NULL); NCR_StartInstance(inst1); has_updated = 0; @@ -355,47 +389,52 @@ inst1->tx_count < MAX_CLIENT_INTERLEAVED_TX); authenticated = random() % 2; valid = (!interleaved || (source.params.interleaved && has_updated)) && - (!source.params.authkey || authenticated); + ((source.params.authkey == INACTIVE_AUTHKEY) == !authenticated); updated = (valid || inst1->mode == MODE_ACTIVE) && - (!source.params.authkey || authenticated); + ((source.params.authkey == INACTIVE_AUTHKEY) == !authenticated); has_updated = has_updated || updated; if (inst1->mode == MODE_CLIENT) updated = 0; + DEBUG_LOG("authkey=%d version=%d interleaved=%d authenticated=%d valid=%d updated=%d has_updated=%d", + (int)source.params.authkey, source.params.version, + interleaved, authenticated, valid, updated, has_updated); + send_request(inst1); send_response(interleaved, authenticated, 1, 0, 1); DEBUG_LOG("response 1"); - process_response(inst1, 0, 0, 0, updated); + proc_response(inst1, 0, 0, 0, updated); if (source.params.authkey) { send_response(interleaved, authenticated, 1, 1, 0); DEBUG_LOG("response 2"); - process_response(inst1, 0, 0, 0, 0); + proc_response(inst1, 0, 0, 0, 0); } send_response(interleaved, authenticated, 1, 1, 1); DEBUG_LOG("response 3"); - process_response(inst1, -1, valid, valid, updated); + proc_response(inst1, -1, valid, valid, updated); DEBUG_LOG("response 4"); - process_response(inst1, 0, 0, 0, 0); + proc_response(inst1, 0, 0, 0, 0); advance_time(-1.0); send_response(interleaved, authenticated, 1, 1, 1); DEBUG_LOG("response 5"); - process_response(inst1, 0, 0, 0, updated && valid); + proc_response(inst1, 0, 0, 0, updated && valid); advance_time(1.0); send_response(interleaved, authenticated, 1, 1, 1); DEBUG_LOG("response 6"); - process_response(inst1, 0, 0, valid && updated, updated); + proc_response(inst1, 0, 0, valid && updated, updated); } NCR_DestroyInstance(inst1); - inst1 = NCR_GetInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, &source.params); + inst1 = NCR_CreateInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, + &source.params, NULL); NCR_StartInstance(inst1); for (j = 0; j < 20; j++) { @@ -403,15 +442,15 @@ send_request(inst1); process_request(&remote_addr); - process_response(inst1, 1, 1, 1, 0); + proc_response(inst1, 1, 1, 1, 0); advance_time(1 << inst1->local_poll); } NCR_DestroyInstance(inst1); - inst1 = NCR_GetInstance(&remote_addr, NTP_PEER, &source.params); + inst1 = NCR_CreateInstance(&remote_addr, NTP_PEER, &source.params, NULL); NCR_StartInstance(inst1); - inst2 = NCR_GetInstance(&remote_addr, NTP_PEER, &source.params); + inst2 = NCR_CreateInstance(&remote_addr, NTP_PEER, &source.params, NULL); NCR_StartInstance(inst2); res_length = req_length = 0; @@ -427,7 +466,7 @@ TEST_CHECK(inst1->valid_timestamps == (j > 0)); DEBUG_LOG("response 1->2"); - process_response(inst2, j > source.params.interleaved, j > 0, j > 0, 1); + proc_response(inst2, j > source.params.interleaved, j > 0, j > 0, 1); packet_queue[(j * 2) % PACKET_QUEUE_LENGTH] = res_buffer; @@ -447,7 +486,7 @@ TEST_CHECK(inst2->valid_timestamps == (j > 0)); DEBUG_LOG("response 2->1"); - process_response(inst1, 1, 1, 1, 1); + proc_response(inst1, 1, 1, 1, 1); packet_queue[(j * 2 + 1) % PACKET_QUEUE_LENGTH] = res_buffer; diff -Nru chrony-3.5/test/unit/ntp_core.keys chrony-4.1/test/unit/ntp_core.keys --- chrony-3.5/test/unit/ntp_core.keys 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/unit/ntp_core.keys 2021-05-12 11:06:15.000000000 +0000 @@ -4,3 +4,5 @@ 5 SHA1 HEX:B71744EA01FBF01CA30D173ECDDF901952AE356A 6 SHA512 HEX:DE027482F22B201FC20863F58C74095E7906089F 7 SHA512 HEX:DE027482F22B201FC20863F58C74095E7906089F +8 AES128 HEX:5D5E8A31D4B459A66D445259E147CFB5 +9 AES128 HEX:5D5E8A31D4B459A66D445259E147CFB5 diff -Nru chrony-3.5/test/unit/ntp_ext.c chrony-4.1/test/unit/ntp_ext.c --- chrony-3.5/test/unit/ntp_ext.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/unit/ntp_ext.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,167 @@ +/* + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + */ + +#include +#include "test.h" + +#ifdef FEAT_NTP + +#include +#include + +#include + +void +test_unit(void) +{ + unsigned char *buffer, body[NTP_MAX_EXTENSIONS_LENGTH]; + void *bodyp; + NTP_PacketInfo info; + NTP_Packet packet; + int i, j, start, length, type, type2, body_length, body_length2; + + assert(sizeof (uint16_t) == 2); + assert(sizeof (body) == sizeof (packet.extensions)); + + buffer = (unsigned char *)packet.extensions; + + for (i = 0; i < 10000; i++) { + body_length = random() % (sizeof (body) - 4 + 1) / 4 * 4; + start = random() % (sizeof (packet.extensions) - body_length - 4 + 1) / 4 * 4; + type = random() % 0x10000; + + DEBUG_LOG("body_length=%d start=%d type=%d", body_length, start, type); + assert(body_length + start <= sizeof (packet.extensions)); + + UTI_GetRandomBytes(body, body_length); + + TEST_CHECK(!NEF_SetField(buffer, body_length + start + 4, start, + type, body, body_length + 4, &length)); + TEST_CHECK(!NEF_SetField(buffer, body_length + start + 4, start + 4, + type, body, body_length, &length)); + TEST_CHECK(!NEF_SetField(buffer, body_length + start + 4, start, + type, body, body_length - 1, &length)); + TEST_CHECK(!NEF_SetField(buffer, body_length + start + 4, start, + type, body, body_length - 2, &length)); + TEST_CHECK(!NEF_SetField(buffer, body_length + start + 4, start, + type, body, body_length - 3, &length)); + TEST_CHECK(!NEF_SetField(buffer, body_length + start + 3, start, + type, body, body_length, &length)); + TEST_CHECK(!NEF_SetField(buffer, body_length + start + 5, start + 1, + type, body, body_length, &length)); + + TEST_CHECK(NEF_SetField(buffer, body_length + start + 4, start, + type, body, body_length, &length)); + TEST_CHECK(length == body_length + 4); + TEST_CHECK(((uint16_t *)buffer)[start / 2] == htons(type)); + TEST_CHECK(((uint16_t *)buffer)[start / 2 + 1] == htons(length)); + TEST_CHECK(memcmp(buffer + start + 4, body, body_length) == 0); + + memset(&packet, 0, sizeof (packet)); + packet.lvm = NTP_LVM(0, 4, MODE_CLIENT); + memset(&info, 0, sizeof (info)); + + info.version = 3; + info.length = NTP_HEADER_LENGTH; + TEST_CHECK(!NEF_AddBlankField(&packet, &info, type, body_length, &bodyp)); + + info.version = 4; + info.length = NTP_HEADER_LENGTH - 4; + TEST_CHECK(!NEF_AddBlankField(&packet, &info, type, body_length, &bodyp)); + + info.length = sizeof (packet) - body_length; + TEST_CHECK(!NEF_AddBlankField(&packet, &info, type, body_length, &bodyp)); + + info.length = NTP_HEADER_LENGTH + start; + + if (body_length < 12) { + TEST_CHECK(!NEF_AddBlankField(&packet, &info, type, body_length, &bodyp)); + continue; + } + + TEST_CHECK(NEF_AddBlankField(&packet, &info, type, body_length, &bodyp)); + TEST_CHECK(info.length == NTP_HEADER_LENGTH + start + body_length + 4); + TEST_CHECK(((uint16_t *)buffer)[start / 2] == htons(type)); + TEST_CHECK(((uint16_t *)buffer)[start / 2 + 1] == htons(length)); + TEST_CHECK(bodyp == buffer + start + 4); + TEST_CHECK(info.ext_fields == 1); + + memset(buffer, 0, sizeof (packet.extensions)); + info.length = NTP_HEADER_LENGTH + start; + info.ext_fields = 0; + + TEST_CHECK(NEF_AddField(&packet, &info, type, body, body_length)); + TEST_CHECK(info.length == NTP_HEADER_LENGTH + start + body_length + 4); + TEST_CHECK(((uint16_t *)buffer)[start / 2] == htons(type)); + TEST_CHECK(((uint16_t *)buffer)[start / 2 + 1] == htons(length)); + TEST_CHECK(memcmp(buffer + start + 4, body, body_length) == 0); + TEST_CHECK(info.ext_fields == 1); + + for (j = 1; j <= 4; j++) { + TEST_CHECK(((uint16_t *)buffer)[start / 2 + 1] = htons(length + j)); + TEST_CHECK(!NEF_ParseSingleField(buffer, start + body_length + 4, start, + &length, &type2, &bodyp, &body_length2)); + } + + TEST_CHECK(((uint16_t *)buffer)[start / 2 + 1] = htons(length)); + + TEST_CHECK(NEF_ParseSingleField(buffer, sizeof (packet.extensions), start, + &length, &type2, &bodyp, &body_length2)); + TEST_CHECK(length == body_length + 4); + TEST_CHECK(type2 == type); + TEST_CHECK(bodyp == buffer + start + 4); + TEST_CHECK(body_length2 == body_length); + + TEST_CHECK(!NEF_ParseField(&packet, sizeof (packet) + 4, + NTP_HEADER_LENGTH + start, + &length, &type2, &bodyp, &body_length2)); + + if (body_length < 24) { + TEST_CHECK(!NEF_ParseField(&packet, NTP_HEADER_LENGTH + start + length, + NTP_HEADER_LENGTH + start, + &length, &type2, &bodyp, &body_length2)); + if (sizeof (packet.extensions) - start <= 24) { + TEST_CHECK(!NEF_ParseField(&packet, sizeof (packet), NTP_HEADER_LENGTH + start, + &length, &type2, &bodyp, &body_length2)); + continue; + } else { + TEST_CHECK(NEF_ParseField(&packet, sizeof (packet), NTP_HEADER_LENGTH + start, + &length, &type2, &bodyp, &body_length2)); + } + } else { + TEST_CHECK(NEF_ParseField(&packet, NTP_HEADER_LENGTH + start + length, + NTP_HEADER_LENGTH + start, + &length, &type2, &bodyp, &body_length2)); + } + TEST_CHECK(length == body_length + 4); + TEST_CHECK(type2 == type); + TEST_CHECK(bodyp == buffer + start + 4); + TEST_CHECK(body_length2 == body_length); + + } +} + +#else +void +test_unit(void) +{ + TEST_REQUIRE(0); +} +#endif diff -Nru chrony-3.5/test/unit/ntp_sources.c chrony-4.1/test/unit/ntp_sources.c --- chrony-3.5/test/unit/ntp_sources.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/unit/ntp_sources.c 2021-05-12 11:06:15.000000000 +0000 @@ -1,6 +1,6 @@ /* ********************************************************************** - * Copyright (C) Miroslav Lichvar 2016 + * Copyright (C) Miroslav Lichvar 2016, 2021 * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -23,40 +23,138 @@ #ifdef FEAT_NTP -#include #include +#include +#include +#include #include +static char *requested_name = NULL; +static DNS_NameResolveHandler resolve_handler = NULL; +static void *resolve_handler_arg = NULL; + +#define DNS_Name2IPAddressAsync(name, handler, arg) \ + requested_name = (name), \ + resolve_handler = (handler), \ + resolve_handler_arg = (arg) +#define NCR_ChangeRemoteAddress(inst, remote_addr, ntp_only) \ + change_remote_address(inst, remote_addr, ntp_only) +#define NCR_ProcessRxKnown(remote_addr, local_addr, ts, msg, len) (random() % 2) +#define NIO_IsServerConnectable(addr) (random() % 2) + +static void change_remote_address(NCR_Instance inst, NTP_Remote_Address *remote_addr, + int ntp_only); + +#include + +#undef NCR_ChangeRemoteAddress + +static void +resolve_random_address(DNS_Status status, int rand_bits) +{ + IPAddr ip_addrs[DNS_MAX_ADDRESSES]; + int i, n_addrs; + + TEST_CHECK(requested_name); + requested_name = NULL; + + if (status == DNS_Success) { + n_addrs = random() % DNS_MAX_ADDRESSES + 1; + for (i = 0; i < n_addrs; i++) + TST_GetRandomAddress(&ip_addrs[i], IPADDR_UNSPEC, rand_bits); + } else { + n_addrs = 0; + } + + (resolve_handler)(status, n_addrs, ip_addrs, resolve_handler_arg); +} + +static int +update_random_address(NTP_Remote_Address *addr, int rand_bits) +{ + NTP_Remote_Address new_addr; + NSR_Status status; + + TST_GetRandomAddress(&new_addr.ip_addr, IPADDR_UNSPEC, rand_bits); + new_addr.port = random() % 1024; + + status = NSR_UpdateSourceNtpAddress(addr, &new_addr); + if (status == NSR_InvalidAF) { + TEST_CHECK(!UTI_IsIPReal(&addr->ip_addr)); + } else { + TEST_CHECK(status == NSR_Success || status == NSR_AlreadyInUse); + } + + return status == NSR_Success; +} + +static void +change_remote_address(NCR_Instance inst, NTP_Remote_Address *remote_addr, int ntp_only) +{ + int update = !ntp_only && random() % 4 == 0, update_pos = random() % 2, r = 0; + + TEST_CHECK(record_lock); + + if (update && update_pos == 0) + r = update_random_address(remote_addr, 4); + + NCR_ChangeRemoteAddress(inst, remote_addr, ntp_only); + + if (update && update_pos == 1) + r = update_random_address(remote_addr, 4); + + if (r) + TEST_CHECK(UTI_IsIPReal(&saved_address_update.old_address.ip_addr)); +} + void test_unit(void) { - int i, j, k, slot, found; - uint32_t hash = 0; + char source_line[] = "127.0.0.1 offline", conf[] = "port 0", name[64], msg[1]; + int i, j, k, slot, found, pool, prev_n; + uint32_t hash = 0, conf_id; NTP_Remote_Address addrs[256], addr; - SourceParameters params; - char conf[] = "port 0"; - - memset(¶ms, 0, sizeof (params)); + NTP_Local_Address local_addr; + NTP_Local_Timestamp local_ts; + struct UnresolvedSource *us; + RPT_ActivityReport report; + CPS_NTP_Source source; + NSR_Status status; CNF_Initialise(0, 0); CNF_ParseLine(NULL, 1, conf); + PRV_Initialise(); LCL_Initialise(); + TST_RegisterDummyDrivers(); SCH_Initialise(); SRC_Initialise(); - NIO_Initialise(IPADDR_UNSPEC); + NIO_Initialise(); NCR_Initialise(); + REF_Initialise(); NSR_Initialise(); + CPS_ParseNTPSourceAdd(source_line, &source); + + TEST_CHECK(n_sources == 0); + for (i = 0; i < 6; i++) { TEST_CHECK(ARR_GetSize(records) == 1); DEBUG_LOG("collision mod %u", 1U << i); for (j = 0; j < sizeof (addrs) / sizeof (addrs[0]); j++) { - do { - TST_GetRandomAddress(&addrs[j].ip_addr, IPADDR_UNSPEC, -1); - } while (UTI_IPToHash(&addrs[j].ip_addr) % (1U << i) != hash % (1U << i)); + while (1) { + do { + TST_GetRandomAddress(&addrs[j].ip_addr, IPADDR_UNSPEC, -1); + } while (UTI_IPToHash(&addrs[j].ip_addr) % (1U << i) != hash % (1U << i)); + + for (k = 0; k < j; k++) + if (UTI_CompareIPs(&addrs[k].ip_addr, &addrs[j].ip_addr, NULL) == 0) + break; + if (k == j) + break; + } addrs[j].port = random() % 1024; @@ -66,39 +164,190 @@ DEBUG_LOG("adding source %s hash %"PRIu32, UTI_IPToString(&addrs[j].ip_addr), UTI_IPToHash(&addrs[j].ip_addr) % (1U << i)); - NSR_AddSource(&addrs[j], random() % 2 ? NTP_SERVER : NTP_PEER, ¶ms); + status = NSR_AddSource(&addrs[j], random() % 2 ? NTP_SERVER : NTP_PEER, + &source.params, NULL); + TEST_CHECK(status == NSR_Success); + TEST_CHECK(n_sources == j + 1); - for (k = 0; k < j; k++) { + TEST_CHECK(strcmp(NSR_GetName(&addrs[j].ip_addr), UTI_IPToString(&addrs[j].ip_addr)) == 0); + + for (k = 0; k <= j; k++) { addr = addrs[k]; - find_slot(&addr, &slot, &found); + found = find_slot2(&addr, &slot); TEST_CHECK(found == 2); TEST_CHECK(!UTI_CompareIPs(&get_record(slot)->remote_addr->ip_addr, &addr.ip_addr, NULL)); addr.port++; - find_slot(&addr, &slot, &found); + found = find_slot2(&addr, &slot); TEST_CHECK(found == 1); TEST_CHECK(!UTI_CompareIPs(&get_record(slot)->remote_addr->ip_addr, &addr.ip_addr, NULL)); } + + status = NSR_AddSource(&addrs[j], NTP_SERVER, &source.params, &conf_id); + TEST_CHECK(status == NSR_AlreadyInUse); } for (j = 0; j < sizeof (addrs) / sizeof (addrs[0]); j++) { DEBUG_LOG("removing source %s", UTI_IPToString(&addrs[j].ip_addr)); - NSR_RemoveSource(&addrs[j]); + NSR_RemoveSource(&addrs[j].ip_addr); for (k = 0; k < sizeof (addrs) / sizeof (addrs[0]); k++) { - find_slot(&addrs[k], &slot, &found); + found = find_slot2(&addrs[k], &slot); TEST_CHECK(found == (k <= j ? 0 : 2)); } } } + TEST_CHECK(n_sources == 0); + + status = NSR_AddSourceByName("a b", 0, 0, 0, &source.params, &conf_id); + TEST_CHECK(status == NSR_InvalidName); + + local_addr.ip_addr.family = IPADDR_INET4; + local_addr.ip_addr.addr.in4 = 0; + local_addr.if_index = -1; + local_addr.sock_fd = 0; + memset(&local_ts, 0, sizeof (local_ts)); + + for (i = 0; i < 500; i++) { + for (j = 0; j < 20; j++) { + snprintf(name, sizeof (name), "ntp%d.example.net", (int)(random() % 10)); + pool = random() % 2; + prev_n = n_sources; + + DEBUG_LOG("%d/%d adding source %s pool=%d", i, j, name, pool); + status = NSR_AddSourceByName(name, 0, pool, random() % 2 ? NTP_SERVER : NTP_PEER, + &source.params, &conf_id); + TEST_CHECK(status == NSR_UnresolvedName); + + TEST_CHECK(n_sources == prev_n + (pool ? source.params.max_sources * 2 : 1)); + TEST_CHECK(unresolved_sources); + + for (us = unresolved_sources; us->next; us = us->next) + ; + TEST_CHECK(strcmp(us->name, name) == 0); + if (pool) { + TEST_CHECK(us->address.ip_addr.family == IPADDR_UNSPEC && us->pool_id >= 0); + } else { + TEST_CHECK(strcmp(NSR_GetName(&us->address.ip_addr), name) == 0); + TEST_CHECK(find_slot2(&us->address, &slot) == 2); + } + + if (random() % 2) { + if (!resolving_id || random() % 2) { + NSR_ResolveSources(); + } else { + SCH_RemoveTimeout(resolving_id); + resolve_sources_timeout(NULL); + TEST_CHECK(resolving_id == 0); + TEST_CHECK(requested_name); + } + + TEST_CHECK(!!unresolved_sources == (resolving_id != 0) || requested_name); + } + + while (requested_name && random() % 2) { + TEST_CHECK(resolving_source); + TEST_CHECK(strcmp(requested_name, resolving_source->name) == 0); + TEST_CHECK(!record_lock); + + switch (random() % 3) { + case 0: + resolve_random_address(DNS_Success, 4); + break; + case 1: + resolve_random_address(DNS_TryAgain, 0); + break; + case 2: + resolve_random_address(DNS_Failure, 0); + break; + } + } + + while (random() % 8 > 0) { + slot = random() % ARR_GetSize(records); + if (!get_record(slot)->remote_addr) + continue; + + switch (random() % 5) { + case 0: + NSR_ProcessTx(get_record(slot)->remote_addr, &local_addr, + &local_ts, (NTP_Packet *)msg, 0); + break; + case 1: + NSR_ProcessRx(get_record(slot)->remote_addr, &local_addr, + &local_ts, (NTP_Packet *)msg, 0); + break; + case 2: + NSR_HandleBadSource(&get_record(slot)->remote_addr->ip_addr); + break; + case 3: + NSR_SetConnectivity(NULL, &get_record(slot)->remote_addr->ip_addr, SRC_OFFLINE); + break; + case 4: + update_random_address(get_record(slot)->remote_addr, 4); + TEST_CHECK(!UTI_IsIPReal(&saved_address_update.old_address.ip_addr)); + break; + } + + TEST_CHECK(!record_lock); + } + + NSR_GetActivityReport(&report); + TEST_CHECK(report.online == 0); + TEST_CHECK(report.offline >= 0); + TEST_CHECK(report.burst_online == 0); + TEST_CHECK(report.burst_offline == 0); + TEST_CHECK(report.unresolved >= 0); + + if (random() % 4 == 0) { + NSR_RemoveSourcesById(conf_id); + TEST_CHECK(n_sources <= prev_n); + } else if (random() % 8 == 0) { + NSR_RefreshAddresses(); + TEST_CHECK(unresolved_sources); + } + } + + NSR_RemoveAllSources(); + TEST_CHECK(n_sources == 0); + + for (j = 0; j < ARR_GetSize(pools); j++) { + TEST_CHECK(get_pool(j)->sources == 0); + TEST_CHECK(get_pool(j)->unresolved_sources == 0); + TEST_CHECK(get_pool(j)->confirmed_sources == 0); + TEST_CHECK(get_pool(j)->max_sources == 0); + } + + while (requested_name) { + TEST_CHECK(resolving_source); + resolve_random_address(random() % 2 ? DNS_Success : DNS_TryAgain, 4); + } + + if (unresolved_sources && resolving_id == 0) + NSR_ResolveSources(); + + TEST_CHECK(!!unresolved_sources == (resolving_id != 0)); + + if (resolving_id) { + SCH_RemoveTimeout(resolving_id); + resolve_sources_timeout(NULL); + } + + TEST_CHECK(resolving_id == 0); + TEST_CHECK(!requested_name); + TEST_CHECK(!unresolved_sources); + } + NSR_Finalise(); + REF_Finalise(); NCR_Finalise(); NIO_Finalise(); SRC_Finalise(); SCH_Finalise(); LCL_Finalise(); + PRV_Finalise(); CNF_Finalise(); HSH_Finalise(); } diff -Nru chrony-3.5/test/unit/nts_ke_client.c chrony-4.1/test/unit/nts_ke_client.c --- chrony-3.5/test/unit/nts_ke_client.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/unit/nts_ke_client.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,144 @@ +/* + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + */ + +#include +#include "test.h" + +#ifdef FEAT_NTS + +#include +#include + +static void +prepare_response(NKSN_Instance session, int valid) +{ + uint16_t data[16]; + int i, index, length; + + if (valid) + index = -1; + else + index = random() % 10; + DEBUG_LOG("index=%d", index); + + NKSN_BeginMessage(session); + + memset(data, 0, sizeof (data)); + length = 2; + assert(sizeof (data[0]) == 2); + + if (index == 0) { + data[0] = htons(random() % 100); + TEST_CHECK(NKSN_AddRecord(session, 1, random() % 2 ? NKE_RECORD_ERROR : NKE_RECORD_WARNING, + data, length)); + } else if (index == 1) { + TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_ERROR + 1000, data, length)); + } + + if (index != 2) { + if (index == 3) + data[0] = htons(NKE_NEXT_PROTOCOL_NTPV4 + random() % 10 + 1); + else + data[0] = htons(NKE_NEXT_PROTOCOL_NTPV4); + if (index == 4) + length = 3 + random() % 10; + TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, length)); + } + + if (index != 5) { + if (index == 6) + data[0] = htons(AEAD_AES_SIV_CMAC_256 + random() % 10 + 1); + else + data[0] = htons(AEAD_AES_SIV_CMAC_256); + if (index == 7) + length = 3 + random() % 10; + TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data, length)); + } + + if (random() % 2) { + const char server[] = "127.0.0.1"; + TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_SERVER_NEGOTIATION, + server, sizeof (server) - 1)); + } + + if (random() % 2) { + data[0] = htons(123); + TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_PORT_NEGOTIATION, data, length)); + } + + if (random() % 2) { + length = random() % (sizeof (data) + 1); + TEST_CHECK(NKSN_AddRecord(session, 0, 1000 + random() % 1000, data, length)); + } + + if (index != 8) { + for (i = 0; i < NKE_MAX_COOKIES; i++) { + length = (random() % sizeof (data) + 1) / 4 * 4; + if (index == 9) + length += (length < sizeof (data) ? 1 : -1) * (random() % 3 + 1); + TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_COOKIE, data, length)); + } + } + + TEST_CHECK(NKSN_EndMessage(session)); +} + +void +test_unit(void) +{ + NKC_Instance inst; + IPSockAddr addr; + int i, r, valid; + + char conf[][100] = { + "nosystemcert", + }; + + CNF_Initialise(0, 0); + for (i = 0; i < sizeof conf / sizeof conf[0]; i++) + CNF_ParseLine(NULL, i + 1, conf[i]); + + LCL_Initialise(); + + SCK_GetLoopbackIPAddress(AF_INET, &addr.ip_addr); + addr.port = 0; + + inst = NKC_CreateInstance(&addr, "test", 0); + TEST_CHECK(inst); + + for (i = 0; i < 10000; i++) { + valid = random() % 2; + prepare_response(inst->session, valid); + r = process_response(inst); + TEST_CHECK(r == valid); + } + + NKC_DestroyInstance(inst); + + LCL_Finalise(); + CNF_Finalise(); +} +#else +void +test_unit(void) +{ + TEST_REQUIRE(0); +} +#endif diff -Nru chrony-3.5/test/unit/nts_ke.crt chrony-4.1/test/unit/nts_ke.crt --- chrony-3.5/test/unit/nts_ke.crt 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/unit/nts_ke.crt 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,8 @@ +-----BEGIN CERTIFICATE----- +MIIBDjCBwaADAgECAgEBMAUGAytlcDAPMQ0wCwYDVQQDEwR0ZXN0MCAXDTcwMDEw +MTAwMDAwMFoYDzIxMDAwMTAyMDAwMDAwWjAPMQ0wCwYDVQQDEwR0ZXN0MCowBQYD +K2VwAyEA3oh/FZeOxRYvJVLfCDEwGI6Oe23gTgLHx8a87tvwgfyjQDA+MAwGA1Ud +EwEB/wQCMAAwDwYDVR0PAQH/BAUDAweAADAdBgNVHQ4EFgQUYwAqF9q3jxUk68m1 +cuz8DueOHeMwBQYDK2VwA0EAne0dCRXb0dW8bn2v3RhVTqeTJWXfl74x8MTQMTM7 +/uGTqoYOA0YJffypd+p27pvx2BEoNQWRYM6pqBg55KbwDw== +-----END CERTIFICATE----- diff -Nru chrony-3.5/test/unit/nts_ke.key chrony-4.1/test/unit/nts_ke.key --- chrony-3.5/test/unit/nts_ke.key 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/unit/nts_ke.key 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,25 @@ +Public Key Info: + Public Key Algorithm: EdDSA (Ed25519) + Key Security Level: High (256 bits) + +curve: Ed25519 +private key: + 01:d9:75:42:7c:52:cc:29:9e:90:01:f3:da:26:f6:d7 + ad:af:a5:2a:82:36:1d:86:c6:57:a7:b4:99:9b:6c:6d + + +x: + de:88:7f:15:97:8e:c5:16:2f:25:52:df:08:31:30:18 + 8e:8e:7b:6d:e0:4e:02:c7:c7:c6:bc:ee:db:f0:81:fc + + + +Public Key PIN: + pin-sha256:C4LBJP2cRxvLcZ6pjowcOEQhcW3ZPMVTpLgRGsBDeMw= +Public Key ID: + sha256:0b82c124fd9c471bcb719ea98e8c1c384421716dd93cc553a4b8111ac04378cc + sha1:63002a17dab78f1524ebc9b572ecfc0ee78e1de3 + +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIAHZdUJ8UswpnpAB89om9tetr6UqgjYdhsZXp7SZm2xt +-----END PRIVATE KEY----- diff -Nru chrony-3.5/test/unit/nts_ke_server.c chrony-4.1/test/unit/nts_ke_server.c --- chrony-3.5/test/unit/nts_ke_server.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/unit/nts_ke_server.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,230 @@ +/* + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + */ + +#include +#include "test.h" + +#ifdef FEAT_NTS + +#include +#include +#include + +#define NKSN_GetKeys get_keys + +static int +get_keys(NKSN_Instance session, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c) +{ + c2s->length = SIV_GetKeyLength(siv); + UTI_GetRandomBytes(c2s->key, c2s->length); + s2c->length = SIV_GetKeyLength(siv); + UTI_GetRandomBytes(s2c->key, s2c->length); + return 1; +} + +#include + +static void +prepare_request(NKSN_Instance session, int valid) +{ + uint16_t data[16]; + int index, length; + + if (valid) + index = -1; + else + index = random() % 9; + DEBUG_LOG("index=%d", index); + + NKSN_BeginMessage(session); + + memset(data, 0, sizeof (data)); + length = 2; + assert(sizeof (data[0]) == 2); + + if (index != 0) { + memset(data, NKE_NEXT_PROTOCOL_NTPV4 + 1, sizeof (data)); + data[0] = htons(NKE_NEXT_PROTOCOL_NTPV4); + if (index == 1) + length = 0; + else if (index == 2) + length = 3 + random() % 15 * 2; + else + length = 2 + random() % 16 * 2; + TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, length)); + } + + if (index == 3) + TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, length)); + + if (index != 4) { + data[0] = htons(AEAD_AES_SIV_CMAC_256); + if (index == 5) + length = 0; + else if (index == 6) + length = 3 + random() % 15 * 2; + else + length = 2 + random() % 16 * 2; + TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data, length)); + } + + if (index == 7) + TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data, length)); + + if (index == 8) { + length = random() % (sizeof (data) + 1); + TEST_CHECK(NKSN_AddRecord(session, 1, 1000 + random() % 1000, data, length)); + } + + if (random() % 2) { + const char server[] = "127.0.0.1"; + TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_NTPV4_SERVER_NEGOTIATION, + server, sizeof (server) - 1)); + } + + if (random() % 2) { + data[0] = htons(123); + TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_NTPV4_PORT_NEGOTIATION, data, length)); + } + + if (random() % 2) { + length = random() % (sizeof (data) + 1); + TEST_CHECK(NKSN_AddRecord(session, 0, 1000 + random() % 1000, data, length)); + } + + TEST_CHECK(NKSN_EndMessage(session)); +} + +static void +process_response(NKSN_Instance session, int valid) +{ + int records, errors, critical, type, length; + + for (records = errors = 0; ; records++) { + if (!NKSN_GetRecord(session, &critical, &type, &length, NULL, 0)) + break; + if (type == NKE_RECORD_ERROR) + errors++; + } + + if (valid) { + TEST_CHECK(records >= 2); + } else { + TEST_CHECK(records == 1); + TEST_CHECK(errors == 1); + } +} + +void +test_unit(void) +{ + NKSN_Instance session; + NKE_Context context, context2; + NKE_Cookie cookie; + int i, valid, l; + uint32_t sum, sum2; + + char conf[][100] = { + "ntsdumpdir .", + "ntsport 0", + "ntsprocesses 0", + "ntsserverkey nts_ke.key", + "ntsservercert nts_ke.crt", + }; + + CNF_Initialise(0, 0); + for (i = 0; i < sizeof conf / sizeof conf[0]; i++) + CNF_ParseLine(NULL, i + 1, conf[i]); + + LCL_Initialise(); + TST_RegisterDummyDrivers(); + SCH_Initialise(); + + unlink("ntskeys"); + NKS_PreInitialise(0, 0, 0); + NKS_Initialise(); + + session = NKSN_CreateInstance(1, NULL, handle_message, NULL); + + for (i = 0; i < 10000; i++) { + valid = random() % 2; + prepare_request(session, valid); + TEST_CHECK(process_request(session)); + process_response(session, valid); + } + + + for (i = 0; i < 10000; i++) { + context.algorithm = AEAD_AES_SIV_CMAC_256; + get_keys(session, context.algorithm, &context.c2s, &context.s2c); + memset(&cookie, 0, sizeof (cookie)); + TEST_CHECK(NKS_GenerateCookie(&context, &cookie)); + TEST_CHECK(NKS_DecodeCookie(&cookie, &context2)); + TEST_CHECK(context.algorithm == context2.algorithm); + TEST_CHECK(context.c2s.length == context2.c2s.length); + TEST_CHECK(context.s2c.length == context2.s2c.length); + TEST_CHECK(memcmp(context.c2s.key, context2.c2s.key, context.c2s.length) == 0); + TEST_CHECK(memcmp(context.s2c.key, context2.s2c.key, context.s2c.length) == 0); + + if (random() % 4) { + cookie.cookie[random() % (cookie.length)]++; + } else if (random() % 4) { + generate_key(current_server_key); + } else { + l = cookie.length; + while (l == cookie.length) + cookie.length = random() % (sizeof (cookie.cookie) + 1); + } + TEST_CHECK(!NKS_DecodeCookie(&cookie, &context2)); + } + + unlink("ntskeys"); + save_keys(); + + for (i = 0, sum = 0; i < MAX_SERVER_KEYS; i++) { + sum += server_keys[i].id + server_keys[i].key[0]; + generate_key(i); + } + + load_keys(); + TEST_CHECK(unlink("ntskeys") == 0); + + for (i = 0, sum2 = 0; i < MAX_SERVER_KEYS; i++) { + sum2 += server_keys[i].id + server_keys[i].key[0]; + } + + TEST_CHECK(sum == sum2); + + NKSN_DestroyInstance(session); + + NKS_Finalise(); + TEST_CHECK(unlink("ntskeys") == 0); + + SCH_Finalise(); + LCL_Finalise(); + CNF_Finalise(); +} +#else +void +test_unit(void) +{ + TEST_REQUIRE(0); +} +#endif diff -Nru chrony-3.5/test/unit/nts_ke_session.c chrony-4.1/test/unit/nts_ke_session.c --- chrony-3.5/test/unit/nts_ke_session.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/unit/nts_ke_session.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,224 @@ +/* + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + */ + +#include +#include "test.h" + +#ifdef FEAT_NTS + +#include + +#include +#include +#include + +static NKSN_Instance client, server; +static unsigned char record[NKE_MAX_MESSAGE_LENGTH]; +static int record_length, critical, type_start, records; +static int request_received; +static int response_received; + +static void +send_message(NKSN_Instance inst) +{ + int i; + + record_length = random() % (NKE_MAX_MESSAGE_LENGTH - 4 + 1); + for (i = 0; i < record_length; i++) + record[i] = random() % 256; + critical = random() % 2; + type_start = random() % 30000 + 1; + assert(sizeof (struct RecordHeader) == 4); + records = random() % ((NKE_MAX_MESSAGE_LENGTH - 4) / (4 + record_length) + 1); + + DEBUG_LOG("critical=%d type_start=%d records=%d*%d", + critical, type_start, records, record_length); + + NKSN_BeginMessage(inst); + + TEST_CHECK(check_message_format(&inst->message, 0)); + TEST_CHECK(!check_message_format(&inst->message, 1)); + + TEST_CHECK(!NKSN_AddRecord(inst, 0, 1, record, NKE_MAX_MESSAGE_LENGTH - 4 + 1)); + + TEST_CHECK(check_message_format(&inst->message, 0)); + TEST_CHECK(!check_message_format(&inst->message, 1)); + + for (i = 0; i < records; i++) { + TEST_CHECK(NKSN_AddRecord(inst, critical, type_start + i, record, record_length)); + TEST_CHECK(!NKSN_AddRecord(inst, 0, 1, &record, + NKE_MAX_MESSAGE_LENGTH - inst->message.length - 4 + 1)); + + TEST_CHECK(check_message_format(&inst->message, 0)); + TEST_CHECK(!check_message_format(&inst->message, 1)); + } + + TEST_CHECK(NKSN_EndMessage(inst)); + + TEST_CHECK(check_message_format(&inst->message, 0)); + TEST_CHECK(check_message_format(&inst->message, 1)); +} + +static void +verify_message(NKSN_Instance inst) +{ + unsigned char buffer[NKE_MAX_MESSAGE_LENGTH]; + int i, c, t, length, buffer_length, msg_length, prev_parsed; + NKE_Key c2s, s2c; + + for (i = 0; i < records; i++) { + memset(buffer, 0, sizeof (buffer)); + buffer_length = random() % (record_length + 1); + assert(buffer_length <= sizeof (buffer)); + + prev_parsed = inst->message.parsed; + msg_length = inst->message.length; + + TEST_CHECK(NKSN_GetRecord(inst, &c, &t, &length, buffer, buffer_length)); + TEST_CHECK(c == critical); + TEST_CHECK(t == type_start + i); + TEST_CHECK(length == record_length); + TEST_CHECK(memcmp(record, buffer, buffer_length) == 0); + if (buffer_length < record_length) + TEST_CHECK(buffer[buffer_length] == 0); + + inst->message.length = inst->message.parsed - 1; + inst->message.parsed = prev_parsed; + TEST_CHECK(!get_record(&inst->message, NULL, NULL, NULL, buffer, buffer_length)); + TEST_CHECK(inst->message.parsed == prev_parsed); + inst->message.length = msg_length; + if (msg_length < 0x8000) { + inst->message.data[prev_parsed + 2] ^= 0x80; + TEST_CHECK(!get_record(&inst->message, NULL, NULL, NULL, buffer, buffer_length)); + TEST_CHECK(inst->message.parsed == prev_parsed); + inst->message.data[prev_parsed + 2] ^= 0x80; + } + TEST_CHECK(get_record(&inst->message, NULL, NULL, NULL, buffer, buffer_length)); + TEST_CHECK(inst->message.parsed > prev_parsed); + } + + TEST_CHECK(!NKSN_GetRecord(inst, &critical, &t, &length, buffer, sizeof (buffer))); + + TEST_CHECK(NKSN_GetKeys(inst, AEAD_AES_SIV_CMAC_256, &c2s, &s2c)); + TEST_CHECK(c2s.length == SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256)); + TEST_CHECK(s2c.length == SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256)); +} + +static int +handle_request(void *arg) +{ + NKSN_Instance server = arg; + + verify_message(server); + + request_received = 1; + + send_message(server); + + return 1; +} + +static int +handle_response(void *arg) +{ + NKSN_Instance client = arg; + + response_received = 1; + + verify_message(client); + + return 1; +} + +static void +check_finished(void *arg) +{ + DEBUG_LOG("checking for stopped sessions"); + if (!NKSN_IsStopped(server) || !NKSN_IsStopped(client)) { + SCH_AddTimeoutByDelay(0.001, check_finished, NULL); + return; + } + + SCH_QuitProgram(); +} + +void +test_unit(void) +{ + NKSN_Credentials client_cred, server_cred; + const char *cert, *key; + int sock_fds[2], i; + uint32_t cert_id; + + LCL_Initialise(); + TST_RegisterDummyDrivers(); + + cert = "nts_ke.crt"; + key = "nts_ke.key"; + cert_id = 0; + + for (i = 0; i < 50; i++) { + SCH_Initialise(); + + server = NKSN_CreateInstance(1, NULL, handle_request, NULL); + client = NKSN_CreateInstance(0, "test", handle_response, NULL); + + server_cred = NKSN_CreateServerCertCredentials(&cert, &key, 1); + client_cred = NKSN_CreateClientCertCredentials(&cert, &cert_id, 1, 0); + + TEST_CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fds) == 0); + TEST_CHECK(fcntl(sock_fds[0], F_SETFL, O_NONBLOCK) == 0); + TEST_CHECK(fcntl(sock_fds[1], F_SETFL, O_NONBLOCK) == 0); + + TEST_CHECK(NKSN_StartSession(server, sock_fds[0], "client", server_cred, 4.0)); + TEST_CHECK(NKSN_StartSession(client, sock_fds[1], "server", client_cred, 4.0)); + + send_message(client); + + request_received = response_received = 0; + + check_finished(NULL); + + SCH_MainLoop(); + + TEST_CHECK(NKSN_IsStopped(server)); + TEST_CHECK(NKSN_IsStopped(client)); + + TEST_CHECK(request_received); + TEST_CHECK(response_received); + + NKSN_DestroyInstance(server); + NKSN_DestroyInstance(client); + + NKSN_DestroyCertCredentials(server_cred); + NKSN_DestroyCertCredentials(client_cred); + + SCH_Finalise(); + } + + LCL_Finalise(); +} +#else +void +test_unit(void) +{ + TEST_REQUIRE(0); +} +#endif diff -Nru chrony-3.5/test/unit/nts_ntp_auth.c chrony-4.1/test/unit/nts_ntp_auth.c --- chrony-3.5/test/unit/nts_ntp_auth.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/unit/nts_ntp_auth.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,112 @@ +/* + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + */ + +#include +#include "test.h" + +#ifdef FEAT_NTS + +#include + +#include "ntp_ext.h" +#include "siv.h" + +void +test_unit(void) +{ + unsigned char key[SIV_MAX_KEY_LENGTH], nonce[256], plaintext[256], plaintext2[256]; + NTP_PacketInfo info; + NTP_Packet packet; + SIV_Instance siv; + int i, j, r, packet_length, nonce_length, key_length; + int plaintext_length, plaintext2_length, min_ef_length; + + siv = SIV_CreateInstance(AEAD_AES_SIV_CMAC_256); + TEST_CHECK(siv); + + for (i = 0; i < 10000; i++) { + key_length = SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256); + for (j = 0; j < key_length; j++) + key[j] = random() % 256; + TEST_CHECK(SIV_SetKey(siv, key, key_length)); + + nonce_length = random() % sizeof (nonce) + 1; + for (j = 0; j < nonce_length; j++) + nonce[j] = random() % 256; + + plaintext_length = random() % (sizeof (plaintext) + 1); + for (j = 0; j < plaintext_length; j++) + plaintext[j] = random() % 256; + + packet_length = NTP_HEADER_LENGTH + random() % 100 * 4; + min_ef_length = random() % (sizeof (packet) - packet_length); + + memset(&packet, 0, sizeof (packet)); + packet.lvm = NTP_LVM(0, 4, 0); + memset(&info, 0, sizeof (info)); + info.version = 4; + info.length = packet_length; + + DEBUG_LOG("packet_length=%d nonce_length=%d plaintext_length=%d min_ef_length=%d", + packet_length, nonce_length, plaintext_length, min_ef_length); + + r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext, + -1, 0); + TEST_CHECK(!r); + r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, 0, plaintext, + plaintext_length, 0); + TEST_CHECK(!r); + r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext, + plaintext_length, sizeof (packet) - info.length + 1); + TEST_CHECK(!r); + + r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext, + plaintext_length, min_ef_length); + TEST_CHECK(r); + TEST_CHECK(info.length - packet_length >= min_ef_length); + + r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2, + -1, &plaintext2_length); + TEST_CHECK(!r); + + r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2, + sizeof (plaintext2), &plaintext2_length); + TEST_CHECK(r); + TEST_CHECK(plaintext_length == plaintext2_length); + TEST_CHECK(memcmp(plaintext, plaintext2, plaintext_length) == 0); + + j = random() % (packet_length + plaintext_length + + nonce_length + SIV_GetTagLength(siv) + 8) / 4 * 4; + ((unsigned char *)&packet)[j]++; + r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2, + sizeof (plaintext2), &plaintext2_length); + TEST_CHECK(!r); + ((unsigned char *)&packet)[j]--; + } + + SIV_DestroyInstance(siv); +} +#else +void +test_unit(void) +{ + TEST_REQUIRE(0); +} +#endif diff -Nru chrony-3.5/test/unit/nts_ntp_client.c chrony-4.1/test/unit/nts_ntp_client.c --- chrony-3.5/test/unit/nts_ntp_client.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/unit/nts_ntp_client.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,284 @@ +/* + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + */ + +#include +#include "test.h" + +#ifdef FEAT_NTS + +#include "socket.h" +#include "ntp.h" +#include "nts_ke_client.h" + +#define NKC_CreateInstance(address, name, cert_set) Malloc(1) +#define NKC_DestroyInstance(inst) Free(inst) +#define NKC_Start(inst) (random() % 2) +#define NKC_IsActive(inst) (random() % 2) +#define NKC_GetRetryFactor(inst) (1) + +static int get_nts_data(NKC_Instance inst, NKE_Context *context, + NKE_Cookie *cookies, int *num_cookies, int max_cookies, + IPSockAddr *ntp_address); +#define NKC_GetNtsData get_nts_data + +#include + +static int +get_nts_data(NKC_Instance inst, NKE_Context *context, + NKE_Cookie *cookies, int *num_cookies, int max_cookies, + IPSockAddr *ntp_address) +{ + int i; + + if (random() % 2) + return 0; + + context->algorithm = AEAD_AES_SIV_CMAC_256; + + context->c2s.length = SIV_GetKeyLength(context->algorithm); + UTI_GetRandomBytes(context->c2s.key, context->c2s.length); + context->s2c.length = SIV_GetKeyLength(context->algorithm); + UTI_GetRandomBytes(context->s2c.key, context->s2c.length); + + *num_cookies = random() % max_cookies + 1; + for (i = 0; i < *num_cookies; i++) { + cookies[i].length = random() % (sizeof (cookies[i].cookie) + 1); + if (random() % 4 != 0) + cookies[i].length = cookies[i].length / 4 * 4; + memset(cookies[i].cookie, random(), cookies[i].length); + } + + ntp_address->ip_addr.family = IPADDR_UNSPEC; + ntp_address->port = 0; + + return 1; +} + +static int +get_request(NNC_Instance inst) +{ + unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH], uniq_id[NTS_MIN_UNIQ_ID_LENGTH]; + NTP_PacketInfo info; + NTP_Packet packet; + int expected_length, req_cookies; + + memset(&packet, 0, sizeof (packet)); + memset(&info, 0, sizeof (info)); + info.version = 4; + info.mode = MODE_CLIENT; + info.length = random() % (sizeof (packet) + 1); + if (random() % 4 != 0) + info.length = info.length / 4 * 4; + + if (inst->num_cookies > 0 && random() % 2) { + inst->num_cookies = 0; + + TEST_CHECK(!NNC_GenerateRequestAuth(inst, &packet, &info)); + } + + while (!NNC_PrepareForAuth(inst)) { + inst->next_nke_attempt = SCH_GetLastEventMonoTime() + random() % 10 - 7; + } + + TEST_CHECK(inst->num_cookies > 0); + TEST_CHECK(inst->siv); + + memcpy(nonce, inst->nonce, sizeof (nonce)); + memcpy(uniq_id, inst->uniq_id, sizeof (uniq_id)); + TEST_CHECK(NNC_PrepareForAuth(inst)); + TEST_CHECK(memcmp(nonce, inst->nonce, sizeof (nonce)) != 0); + TEST_CHECK(memcmp(uniq_id, inst->uniq_id, sizeof (uniq_id)) != 0); + + req_cookies = MIN(NTS_MAX_COOKIES - inst->num_cookies + 1, + MAX_TOTAL_COOKIE_LENGTH / + (inst->cookies[inst->cookie_index].length + 4)); + expected_length = info.length + 4 + sizeof (inst->uniq_id) + + req_cookies * (4 + inst->cookies[inst->cookie_index].length) + + 4 + 4 + sizeof (inst->nonce) + SIV_GetTagLength(inst->siv); + DEBUG_LOG("length=%d cookie_length=%d expected_length=%d", + info.length, inst->cookies[inst->cookie_index].length, expected_length); + + if (info.length % 4 == 0 && info.length >= NTP_HEADER_LENGTH && + inst->cookies[inst->cookie_index].length % 4 == 0 && + inst->cookies[inst->cookie_index].length >= (NTP_MIN_EF_LENGTH - 4) && + expected_length <= sizeof (packet)) { + TEST_CHECK(NNC_GenerateRequestAuth(inst, &packet, &info)); + TEST_CHECK(info.length == expected_length); + return 1; + } else { + TEST_CHECK(!NNC_GenerateRequestAuth(inst, &packet, &info)); + return 0; + } +} + +static void +prepare_response(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info, int valid, int nak) +{ + unsigned char cookie[508], plaintext[528], nonce[448]; + int nonce_length, ef_length, cookie_length, plaintext_length, min_auth_length; + int i, index, auth_start; + SIV_Instance siv; + + memset(packet, 0, sizeof (*packet)); + packet->lvm = NTP_LVM(0, 4, MODE_SERVER); + memset(info, 0, sizeof (*info)); + info->version = 4; + info->mode = MODE_SERVER; + info->length = NTP_HEADER_LENGTH; + + if (valid) + index = -1; + else + index = random() % (nak ? 2 : 8); + + DEBUG_LOG("index=%d nak=%d", index, nak); + + if (index != 0) + TEST_CHECK(NEF_AddField(packet, info, NTP_EF_NTS_UNIQUE_IDENTIFIER, inst->uniq_id, + sizeof (inst->uniq_id))); + if (index == 1) + ((unsigned char *)packet)[NTP_HEADER_LENGTH + 4]++; + + if (nak) { + packet->stratum = NTP_INVALID_STRATUM; + packet->reference_id = htonl(NTP_KOD_NTS_NAK); + return; + } + + nonce_length = random() % (sizeof (nonce)) + 1; + + do { + cookie_length = random() % (sizeof (cookie) + 1); + } while (cookie_length % 4 != 0 || + ((index != 2) == (cookie_length < NTP_MIN_EF_LENGTH - 4 || + cookie_length > NKE_MAX_COOKIE_LENGTH))); + + min_auth_length = random() % (512 + 1); + + DEBUG_LOG("nonce_length=%d cookie_length=%d min_auth_length=%d", + nonce_length, cookie_length, min_auth_length); + + UTI_GetRandomBytes(nonce, nonce_length); + UTI_GetRandomBytes(cookie, cookie_length); + + if (cookie_length >= 12 && cookie_length <= 32 && random() % 2 == 0) + TEST_CHECK(NEF_AddField(packet, info, NTP_EF_NTS_COOKIE, cookie, cookie_length)); + + plaintext_length = 0; + if (index != 3) { + for (i = random() % ((sizeof (plaintext) - 16) / (cookie_length + 4)); i >= 0; i--) { + TEST_CHECK(NEF_SetField(plaintext, sizeof (plaintext), plaintext_length, + NTP_EF_NTS_COOKIE, cookie, + i == 0 ? cookie_length : random() % (cookie_length + 1) / 4 * 4, + &ef_length)); + plaintext_length += ef_length; + } + } + auth_start = info->length; + if (index != 4) { + if (index == 5) { + assert(plaintext_length + 16 <= sizeof (plaintext)); + memset(plaintext + plaintext_length, 0, 16); + plaintext_length += 16; + } + siv = SIV_CreateInstance(inst->context.algorithm); + TEST_CHECK(siv); + TEST_CHECK(SIV_SetKey(siv, inst->context.s2c.key, inst->context.s2c.length)); + TEST_CHECK(NNA_GenerateAuthEF(packet, info, siv, + nonce, nonce_length, plaintext, plaintext_length, + min_auth_length)); + SIV_DestroyInstance(siv); + } + if (index == 6) + ((unsigned char *)packet)[auth_start + 8]++; + if (index == 7) + TEST_CHECK(NEF_AddField(packet, info, 0x7000, inst->uniq_id, sizeof (inst->uniq_id))); +} + +void +test_unit(void) +{ + NNC_Instance inst; + NTP_PacketInfo info; + NTP_Packet packet; + IPSockAddr addr; + IPAddr ip_addr; + int i, j, prev_num_cookies, valid; + + TEST_CHECK(SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) > 0); + + SCK_GetLoopbackIPAddress(AF_INET, &addr.ip_addr); + addr.port = 0; + + inst = NNC_CreateInstance(&addr, "test", 0, 0); + TEST_CHECK(inst); + + for (i = 0; i < 100000; i++) { + if (!get_request(inst)) + continue; + + valid = random() % 2; + + TEST_CHECK(!inst->nak_response); + TEST_CHECK(!inst->ok_response); + + if (random() % 2) { + prepare_response(inst, &packet, &info, 0, 1); + TEST_CHECK(!NNC_CheckResponseAuth(inst, &packet, &info)); + TEST_CHECK(!inst->nak_response); + TEST_CHECK(!inst->ok_response); + for (j = random() % 3; j > 0; j--) { + prepare_response(inst, &packet, &info, 1, 1); + TEST_CHECK(!NNC_CheckResponseAuth(inst, &packet, &info)); + TEST_CHECK(inst->nak_response); + TEST_CHECK(!inst->ok_response); + } + } + + prev_num_cookies = inst->num_cookies; + prepare_response(inst, &packet, &info, valid, 0); + + if (valid) { + TEST_CHECK(NNC_CheckResponseAuth(inst, &packet, &info)); + TEST_CHECK(inst->num_cookies >= MIN(NTS_MAX_COOKIES, prev_num_cookies + 1)); + TEST_CHECK(inst->ok_response); + } + + prev_num_cookies = inst->num_cookies; + TEST_CHECK(!NNC_CheckResponseAuth(inst, &packet, &info)); + TEST_CHECK(inst->num_cookies == prev_num_cookies); + TEST_CHECK(inst->ok_response == valid); + + if (random() % 10 == 0) { + TST_GetRandomAddress(&ip_addr, IPADDR_INET4, 32); + NNC_ChangeAddress(inst, &ip_addr); + TEST_CHECK(UTI_CompareIPs(&inst->nts_address.ip_addr, &ip_addr, NULL) == 0); + } + } + + NNC_DestroyInstance(inst); +} +#else +void +test_unit(void) +{ + TEST_REQUIRE(0); +} +#endif diff -Nru chrony-3.5/test/unit/nts_ntp_server.c chrony-4.1/test/unit/nts_ntp_server.c --- chrony-3.5/test/unit/nts_ntp_server.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/unit/nts_ntp_server.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,176 @@ +/* + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + */ + +#include +#include "test.h" + +#ifdef FEAT_NTS + +#include +#include + +#include + +static void +prepare_request(NTP_Packet *packet, NTP_PacketInfo *info, int valid, int nak) +{ + unsigned char uniq_id[NTS_MIN_UNIQ_ID_LENGTH], nonce[NTS_MIN_UNPADDED_NONCE_LENGTH]; + SIV_Instance siv; + NKE_Context context; + NKE_Cookie cookie; + int i, index, cookie_start, auth_start; + + context.algorithm = SERVER_SIV; + context.c2s.length = SIV_GetKeyLength(context.algorithm); + UTI_GetRandomBytes(&context.c2s.key, context.c2s.length); + context.s2c.length = SIV_GetKeyLength(context.algorithm); + UTI_GetRandomBytes(&context.s2c.key, context.s2c.length); + + TEST_CHECK(NKS_GenerateCookie(&context, &cookie)); + + UTI_GetRandomBytes(uniq_id, sizeof (uniq_id)); + UTI_GetRandomBytes(nonce, sizeof (nonce)); + + memset(packet, 0, sizeof (*packet)); + packet->lvm = NTP_LVM(0, 4, MODE_CLIENT); + memset(info, 0, sizeof (*info)); + info->version = 4; + info->mode = MODE_CLIENT; + info->length = NTP_HEADER_LENGTH; + + if (valid) + index = -1; + else + index = random() % 3; + + DEBUG_LOG("valid=%d nak=%d index=%d", valid, nak, index); + + if (index != 0) + TEST_CHECK(NEF_AddField(packet, info, NTP_EF_NTS_UNIQUE_IDENTIFIER, + uniq_id, sizeof (uniq_id))); + + cookie_start = info->length; + + if (index != 1) + TEST_CHECK(NEF_AddField(packet, info, NTP_EF_NTS_COOKIE, + cookie.cookie, cookie.length)); + + for (i = random() % 4; i > 0; i--) + TEST_CHECK(NEF_AddField(packet, info, NTP_EF_NTS_COOKIE_PLACEHOLDER, + cookie.cookie, cookie.length)); + + auth_start = info->length; + + if (index != 2) { + siv = SIV_CreateInstance(context.algorithm); + TEST_CHECK(SIV_SetKey(siv, context.c2s.key, context.c2s.length)); + TEST_CHECK(NNA_GenerateAuthEF(packet, info, siv, nonce, sizeof (nonce), + (const unsigned char *)"", 0, 0)); + SIV_DestroyInstance(siv); + } + + if (nak) + ((unsigned char *)packet)[(index == 2 ? cookie_start : + (index == 1 ? auth_start : + (random() % 2 ? cookie_start : auth_start))) + + 4 + random() % 16]++; +} + +static void +init_response(NTP_Packet *packet, NTP_PacketInfo *info) +{ + memset(packet, 0, sizeof (*packet)); + packet->lvm = NTP_LVM(0, 4, MODE_SERVER); + memset(info, 0, sizeof (*info)); + info->version = 4; + info->mode = MODE_SERVER; + info->length = NTP_HEADER_LENGTH; +} + +void +test_unit(void) +{ + NTP_PacketInfo req_info, res_info; + NTP_Packet request, response; + int i, valid, nak; + uint32_t kod; + + char conf[][100] = { + "ntsport 0", + "ntsprocesses 0", + "ntsserverkey nts_ke.key", + "ntsservercert nts_ke.crt", + }; + + CNF_Initialise(0, 0); + for (i = 0; i < sizeof conf / sizeof conf[0]; i++) + CNF_ParseLine(NULL, i + 1, conf[i]); + + LCL_Initialise(); + TST_RegisterDummyDrivers(); + SCH_Initialise(); + NKS_PreInitialise(0, 0, 0); + NKS_Initialise(); + NNS_Initialise(); + + for (i = 0; i < 50000; i++) { + valid = random() % 2; + nak = random() % 2; + prepare_request(&request, &req_info, valid, nak); + + TEST_CHECK(NNS_CheckRequestAuth(&request, &req_info, &kod) == (valid && !nak)); + + if (valid && !nak) { + TEST_CHECK(kod == 0); + TEST_CHECK(server->num_cookies > 0); + + init_response(&response, &res_info); + TEST_CHECK(NNS_GenerateResponseAuth(&request, &req_info, &response, &res_info, kod)); + + TEST_CHECK(res_info.ext_fields == 2); + TEST_CHECK(server->num_cookies == 0); + } else if (valid && nak) { + TEST_CHECK(kod == NTP_KOD_NTS_NAK); + TEST_CHECK(server->num_cookies == 0); + + init_response(&response, &res_info); + TEST_CHECK(NNS_GenerateResponseAuth(&request, &req_info, &response, &res_info, kod)); + + TEST_CHECK(res_info.ext_fields == 1); + TEST_CHECK(server->num_cookies == 0); + } else { + TEST_CHECK(kod == 0); + TEST_CHECK(server->num_cookies == 0); + } + } + + NNS_Finalise(); + NKS_Finalise(); + SCH_Finalise(); + LCL_Finalise(); + CNF_Finalise(); +} +#else +void +test_unit(void) +{ + TEST_REQUIRE(0); +} +#endif diff -Nru chrony-3.5/test/unit/samplefilt.c chrony-4.1/test/unit/samplefilt.c --- chrony-3.5/test/unit/samplefilt.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/unit/samplefilt.c 2021-05-12 11:06:15.000000000 +0000 @@ -35,6 +35,9 @@ LCL_Initialise(); + memset(&sample_in, 0, sizeof (sample_in)); + memset(&sample_out, 0, sizeof (sample_out)); + for (i = 0; i <= 100; i++) { max_samples = random() % 20 + 1; min_samples = random() % (max_samples) + 1; @@ -56,8 +59,6 @@ sample_in.root_dispersion = TST_GetRandomDouble(1.0e-3, 2.0e-3); sample_in.peer_delay = TST_GetRandomDouble(1.0e-2, 2.0e-2); sample_in.root_delay = TST_GetRandomDouble(1.0e-1, 2.0e-1); - sample_in.stratum = random() % 16; - sample_in.leap = random() % 4; TEST_CHECK(SPF_AccumulateSample(filter, &sample_in)); TEST_CHECK(!SPF_AccumulateSample(filter, &sample_in)); @@ -95,8 +96,6 @@ sample_out.peer_delay <= 2.0e-2); TEST_CHECK(sample_out.root_delay >= 1.0e-1 && sample_out.root_delay <= 2.0e-1); - TEST_CHECK(sample_out.leap >= 0 && sample_out.leap <= 3); - TEST_CHECK(sample_out.stratum >= 0 && sample_out.stratum <= 15); if (max_samples == 1) TEST_CHECK(!memcmp(&sample_in, &sample_out, sizeof (sample_in))); diff -Nru chrony-3.5/test/unit/siv.c chrony-4.1/test/unit/siv.c --- chrony-3.5/test/unit/siv.c 1970-01-01 00:00:00.000000000 +0000 +++ chrony-4.1/test/unit/siv.c 2021-05-12 11:06:15.000000000 +0000 @@ -0,0 +1,321 @@ +/* + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + */ + +#include +#include +#include +#include +#include "test.h" + +#ifdef HAVE_SIV + +struct siv_test { + SIV_Algorithm algorithm; + const unsigned char key[64]; + int key_length; + const unsigned char nonce[128]; + int nonce_length; + const unsigned char assoc[128]; + int assoc_length; + const unsigned char plaintext[128]; + int plaintext_length; + const unsigned char ciphertext[128]; + int ciphertext_length; +}; + +void +test_unit(void) +{ + struct siv_test tests[] = { + { AEAD_AES_SIV_CMAC_256, + "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde" + "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32, + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", 16, + "", 0, + "", 0, + "\x22\x3e\xb5\x94\xe0\xe0\x25\x4b\x00\x25\x8e\x21\x9a\x1c\xa4\x21", 16 + }, + { AEAD_AES_SIV_CMAC_256, + "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde" + "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32, + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", 16, + "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b", 16, + "", 0, + "\xd7\x20\x19\x89\xc6\xdb\xc6\xd6\x61\xfc\x62\xbc\x86\x5e\xee\xef", 16 + }, + { AEAD_AES_SIV_CMAC_256, + "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde" + "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32, + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", 16, + "", 0, + "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b", 16, + "\xb6\xc1\x60\xe9\xc2\xfd\x2a\xe8\xde\xc5\x36\x8b\x2a\x33\xed\xe1" + "\x14\xff\xb3\x97\x34\x5c\xcb\xe4\x4a\xa4\xde\xac\xd9\x36\x90\x46", 32 + }, + { AEAD_AES_SIV_CMAC_256, + "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde" + "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32, + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e", 15, + "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c", 15, + "\xba\x99\x79\x31\x23\x7e\x3c\x53\x58\x7e\xd4\x93\x02\xab\xe4", 15, + "\x03\x8c\x41\x51\xba\x7a\x8f\x77\x6e\x56\x31\x99\x42\x0b\xc7\x03" + "\xe7\x6c\x67\xc9\xda\xb7\x0d\x5b\x44\x06\x26\x5a\xd0\xd2\x3b", 31 + }, + { AEAD_AES_SIV_CMAC_256, + "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde" + "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32, + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", 16, + "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b", 16, + "\xba\x99\x79\x31\x23\x7e\x3c\x53\x58\x7e\xd4\x93\x02\xab\xe4\xa7", 16, + "\x5c\x05\x23\x65\xf4\x57\x0a\xa0\xfb\x38\x3e\xce\x9b\x75\x85\xeb" + "\x68\x85\x19\x36\x0c\x7c\x48\x11\x40\xcb\x9b\x57\x9a\x0e\x65\x32", 32 + }, + { AEAD_AES_SIV_CMAC_256, + "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde" + "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32, + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\xd5", 17, + "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b" + "\xa0", 17, + "\xba\x99\x79\x31\x23\x7e\x3c\x53\x58\x7e\xd4\x93\x02\xab\xe4\xa7" + "\x08", 17, + "\xaf\x58\x4b\xe7\x82\x1e\x96\x19\x29\x91\x25\xe0\xdd\x80\x3b\x49" + "\xa5\x11\xcd\xb6\x08\xf3\x76\xa0\xb6\xfa\x15\x82\xf3\x95\xe1\xeb" + "\xbd", 33 + }, + { AEAD_AES_SIV_CMAC_256, + "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde" + "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32, + "\xb0\x5a\x1b\xc7\x56\xe7\xb6\x2c\xb4\x85\xe5\x56\xa5\x28\xc0\x6c" + "\x2f\x3b\x0b\x9d\x1a\x0c\xdf\x69\x47\xe0\xcc\xc0\x87\xaa\x5c\x09" + "\x98\x48\x8d\x6a\x8e\x1e\x05\xd7\x8b\x68\x74\x83\xb5\x1d\xf1\x2c", 48, + "\xe5\x8b\xd2\x6a\x30\xc5\xc5\x61\xcc\xbd\x7c\x27\xbf\xfe\xf9\x06" + "\x00\x5b\xd7\xfc\x11\x0b\xcf\x16\x61\xef\xac\x05\xa7\xaf\xec\x27" + "\x41\xc8\x5e\x9e\x0d\xf9\x2f\xaf\x20\x79\x17\xe5\x17\x91\x2a\x27" + "\x34\x1c\xbc\xaf\xeb\xef\x7f\x52\xe7\x1e\x4c\x2a\xca\xbd\x2b\xbe" + "\x34\xd6\xfb\x69\xd3\x3e\x49\x59\x60\xb4\x26\xc9\xb8\xce\xba", 79, + "\x6c\xe7\xcf\x7e\xab\x7b\xa0\xe1\xa7\x22\xcb\x88\xde\x5e\x42\xd2" + "\xec\x79\xe0\xa2\xcf\x5f\x0f\x6f\x6b\x89\x57\xcd\xae\x17\xd4\xc2" + "\xf3\x1b\xa2\xa8\x13\x78\x23\x2f\x83\xa8\xd4\x0c\xc0\xd2\xf3\x99" + "\xae\x81\xa1\xca\x5b\x5f\x45\xa6\x6f\x0c\x8a\xf3\xd4\x67\x40\x81" + "\x26\xe2\x01\x86\xe8\x5a\xd5\xf8\x58\x80\x9f\x56\xaa\x76\x96\xbf" + "\x31", 81, + "\x9a\x06\x33\xe0\xee\x00\x6a\x9b\xc8\x20\xd5\xe2\xc2\xed\xb5\x75" + "\xfa\x9e\x42\x2a\x31\x6b\xda\xca\xaa\x7d\x31\x8b\x84\x7a\xb8\xd7" + "\x8a\x81\x25\x64\xed\x41\x9b\xa9\x77\x10\xbd\x05\x0c\x4e\xc5\x31" + "\x0c\xa2\x86\xec\x8a\x94\xc8\x24\x23\x3c\x13\xee\xa5\x51\xc9\xdf" + "\x48\xc9\x55\xc5\x2f\x40\x73\x3f\x98\xbb\x8d\x69\x78\x46\x64\x17" + "\x8d\x49\x2f\x14\x62\xa4\x7c\x2a\x57\x38\x87\xce\xc6\x72\xd3\x5c" + "\xa1", 97 + }, + { 0, "", 0 } + }; + + unsigned char plaintext[sizeof (((struct siv_test *)NULL)->plaintext)]; + unsigned char ciphertext[sizeof (((struct siv_test *)NULL)->ciphertext)]; + SIV_Instance siv; + int i, j, r; + + TEST_CHECK(SIV_CreateInstance(0) == NULL); + + for (i = 0; tests[i].algorithm != 0; i++) { + DEBUG_LOG("testing %d (%d)", (int)tests[i].algorithm, i); + + assert(tests[i].key_length <= sizeof (tests[i].key)); + assert(tests[i].nonce_length <= sizeof (tests[i].nonce)); + assert(tests[i].assoc_length <= sizeof (tests[i].assoc)); + assert(tests[i].plaintext_length <= sizeof (tests[i].plaintext)); + assert(tests[i].ciphertext_length <= sizeof (tests[i].ciphertext)); + + siv = SIV_CreateInstance(tests[i].algorithm); + TEST_CHECK(siv != NULL); + + TEST_CHECK(SIV_GetKeyLength(tests[i].algorithm) == tests[i].key_length); + + r = SIV_Encrypt(siv, tests[i].nonce, tests[i].nonce_length, + tests[i].assoc, tests[i].assoc_length, + tests[i].plaintext, tests[i].plaintext_length, + ciphertext, tests[i].ciphertext_length); + TEST_CHECK(!r); + r = SIV_Decrypt(siv, tests[i].nonce, tests[i].nonce_length, + tests[i].assoc, tests[i].assoc_length, + tests[i].ciphertext, tests[i].ciphertext_length, + plaintext, tests[i].plaintext_length); + TEST_CHECK(!r); + + for (j = -1; j < 1024; j++) { + r = SIV_SetKey(siv, tests[i].key, j); + TEST_CHECK(r == (j == tests[i].key_length)); + } + + TEST_CHECK(SIV_GetTagLength(siv) == tests[i].ciphertext_length - tests[i].plaintext_length); + + r = SIV_Encrypt(siv, tests[i].nonce, tests[i].nonce_length, + tests[i].assoc, tests[i].assoc_length, + tests[i].plaintext, tests[i].plaintext_length, + ciphertext, tests[i].ciphertext_length); + TEST_CHECK(r); + +#if 0 + for (j = 0; j < tests[i].ciphertext_length; j++) { + printf("\\x%02x", ciphertext[j]); + if (j % 16 == 15) + printf("\n"); + } + printf("\n"); +#endif + TEST_CHECK(memcmp(ciphertext, tests[i].ciphertext, tests[i].ciphertext_length) == 0); + + for (j = -1; j < tests[i].nonce_length; j++) { + r = SIV_Encrypt(siv, tests[i].nonce, j, + tests[i].assoc, tests[i].assoc_length, + tests[i].plaintext, tests[i].plaintext_length, + ciphertext, tests[i].ciphertext_length); + if (j > 0) { + TEST_CHECK(r); + TEST_CHECK(memcmp(ciphertext, tests[i].ciphertext, tests[i].ciphertext_length) != 0); + } else { + TEST_CHECK(!r); + } + } + + for (j = -1; j < tests[i].assoc_length; j++) { + r = SIV_Encrypt(siv, tests[i].nonce, tests[i].nonce_length, + tests[i].assoc, j, + tests[i].plaintext, tests[i].plaintext_length, + ciphertext, tests[i].ciphertext_length); + if (j >= 0) { + TEST_CHECK(r); + TEST_CHECK(memcmp(ciphertext, tests[i].ciphertext, tests[i].ciphertext_length) != 0); + } else { + TEST_CHECK(!r); + } + } + + for (j = -1; j < tests[i].plaintext_length; j++) { + r = SIV_Encrypt(siv, tests[i].nonce, tests[i].nonce_length, + tests[i].assoc, tests[i].assoc_length, + tests[i].plaintext, j, + ciphertext, j + SIV_GetTagLength(siv)); + if (j >= 0) { + TEST_CHECK(r); + TEST_CHECK(memcmp(ciphertext, tests[i].ciphertext, j + SIV_GetTagLength(siv)) != 0); + } else { + TEST_CHECK(!r); + } + } + + for (j = -1; j < 2 * tests[i].plaintext_length; j++) { + if (j == tests[i].plaintext_length) + continue; + r = SIV_Encrypt(siv, tests[i].nonce, tests[i].nonce_length, + tests[i].assoc, tests[i].assoc_length, + tests[i].plaintext, j, + ciphertext, tests[i].ciphertext_length); + TEST_CHECK(!r); + } + + for (j = -1; j < 2 * tests[i].ciphertext_length; j++) { + if (j == tests[i].ciphertext_length) + continue; + r = SIV_Encrypt(siv, tests[i].nonce, tests[i].nonce_length, + tests[i].assoc, tests[i].assoc_length, + tests[i].plaintext, tests[i].plaintext_length, + ciphertext, j); + TEST_CHECK(!r); + } + + r = SIV_Decrypt(siv, tests[i].nonce, tests[i].nonce_length, + tests[i].assoc, tests[i].assoc_length, + tests[i].ciphertext, tests[i].ciphertext_length, + plaintext, tests[i].plaintext_length); + TEST_CHECK(r); + TEST_CHECK(memcmp(plaintext, tests[i].plaintext, tests[i].plaintext_length) == 0); + + for (j = -1; j < tests[i].nonce_length; j++) { + r = SIV_Decrypt(siv, tests[i].nonce, j, + tests[i].assoc, tests[i].assoc_length, + tests[i].ciphertext, tests[i].ciphertext_length, + plaintext, tests[i].plaintext_length); + TEST_CHECK(!r); + } + + for (j = -1; j < tests[i].assoc_length; j++) { + r = SIV_Decrypt(siv, tests[i].nonce, tests[i].nonce_length, + tests[i].assoc, j, + tests[i].ciphertext, tests[i].ciphertext_length, + plaintext, tests[i].plaintext_length); + TEST_CHECK(!r); + } + + for (j = -1; j < 2 * tests[i].ciphertext_length; j++) { + if (j == tests[i].ciphertext_length) + continue; + + r = SIV_Decrypt(siv, tests[i].nonce, tests[i].nonce_length, + tests[i].assoc, tests[i].assoc_length, + tests[i].ciphertext, j, + plaintext, tests[i].plaintext_length); + TEST_CHECK(!r); + } + + for (j = -1; j < tests[i].plaintext_length; j++) { + r = SIV_Decrypt(siv, tests[i].nonce, tests[i].nonce_length, + tests[i].assoc, tests[i].assoc_length, + tests[i].ciphertext, tests[i].ciphertext_length, + plaintext, j); + TEST_CHECK(!r); + + r = SIV_Decrypt(siv, tests[i].nonce, tests[i].nonce_length, + tests[i].assoc, tests[i].assoc_length, + tests[i].ciphertext, j + SIV_GetTagLength(siv), + plaintext, j); + TEST_CHECK(!r); + } + + SIV_DestroyInstance(siv); + } + + siv = SIV_CreateInstance(tests[0].algorithm); + for (i = 0; i < 1000; i++) { + for (j = 0; tests[j].algorithm == tests[0].algorithm; j++) { + r = SIV_SetKey(siv, tests[j].key, tests[j].key_length); + TEST_CHECK(r); + r = SIV_Encrypt(siv, tests[j].nonce, tests[j].nonce_length, + tests[j].assoc, tests[j].assoc_length, + tests[j].plaintext, tests[j].plaintext_length, + ciphertext, tests[j].ciphertext_length); + TEST_CHECK(r); + r = SIV_Decrypt(siv, tests[j].nonce, tests[j].nonce_length, + tests[j].assoc, tests[j].assoc_length, + tests[j].ciphertext, tests[j].ciphertext_length, + plaintext, tests[j].plaintext_length); + TEST_CHECK(r); + } + } + SIV_DestroyInstance(siv); +} +#else +void +test_unit(void) +{ + TEST_REQUIRE(0); +} +#endif diff -Nru chrony-3.5/test/unit/sources.c chrony-4.1/test/unit/sources.c --- chrony-3.5/test/unit/sources.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/unit/sources.c 2021-05-12 11:06:15.000000000 +0000 @@ -21,14 +21,26 @@ #include #include "test.h" +static SRC_Instance +create_source(SRC_Type type, IPAddr *addr, int authenticated, int sel_options) +{ + TST_GetRandomAddress(addr, IPADDR_UNSPEC, -1); + + 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); +} + void test_unit(void) { + SRC_AuthSelectMode sel_mode; SRC_Instance srcs[16]; + IPAddr addrs[16]; RPT_SourceReport report; NTP_Sample sample; - IPAddr addr; - int i, j, k, l, samples, sel_options; + int i, j, k, l, n1, n2, n3, n4, samples, sel_options; + char conf[128]; CNF_Initialise(0, 0); LCL_Initialise(); @@ -36,6 +48,7 @@ SCH_Initialise(); SRC_Initialise(); REF_Initialise(); + NSR_Initialise(); REF_SetMode(REF_ModeIgnore); @@ -45,15 +58,11 @@ for (j = 0; j < sizeof (srcs) / sizeof (srcs[0]); j++) { TEST_CHECK(n_sources == j); - TST_GetRandomAddress(&addr, IPADDR_UNSPEC, -1); - sel_options = i & random() & (SRC_SELECT_NOSELECT | SRC_SELECT_PREFER | SRC_SELECT_TRUST | SRC_SELECT_REQUIRE); DEBUG_LOG("added source %d options %d", j, sel_options); - srcs[j] = SRC_CreateNewInstance(UTI_IPToRefid(&addr), SRC_NTP, sel_options, &addr, - SRC_DEFAULT_MINSAMPLES, SRC_DEFAULT_MAXSAMPLES, - 0.0, 1.0); + srcs[j] = create_source(SRC_NTP, &addrs[j], 0, sel_options); SRC_UpdateReachability(srcs[j], 1); samples = (i + j) % 5 + 3; @@ -70,8 +79,9 @@ sample.peer_dispersion = TST_GetRandomDouble(1.0e-6, 1.0e-1); sample.root_delay = sample.peer_delay; sample.root_dispersion = sample.peer_dispersion; - sample.stratum = 1; - sample.leap = LEAP_Normal; + + if (random() % 2) + SRC_UpdateStatus(srcs[j], 1, random() % 4); DEBUG_LOG("source %d sample %d offset %f delay %f disp %f", j, k, sample.offset, sample.peer_delay, sample.peer_dispersion); @@ -118,12 +128,18 @@ } } - DEBUG_LOG("sources %d passed %d trusted %d/%d required %d/%d", j, passed, + DEBUG_LOG("sources %d passed %d trusted %d/%d required %d/%d", j + 1, passed, trusted_passed, trusted, required_passed, required); TEST_CHECK(!trusted || !passed || (passed_lo >= trusted_lo && passed_hi <= trusted_hi)); - TEST_CHECK(!passed || trusted != 1 || (trusted == 1 && trusted_passed == 1)); + TEST_CHECK(!passed || !trusted || trusted_passed >= 1); 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))); + } } } @@ -133,6 +149,90 @@ } } + TEST_CHECK(CNF_GetAuthSelectMode() == SRC_AUTHSELECT_MIX); + + for (i = 0; i < 1000; i++) { + DEBUG_LOG("iteration %d", i); + + switch (i % 4) { + case 0: + snprintf(conf, sizeof (conf), "authselectmode require"); + sel_mode = SRC_AUTHSELECT_REQUIRE; + break; + case 1: + snprintf(conf, sizeof (conf), "authselectmode prefer"); + sel_mode = SRC_AUTHSELECT_PREFER; + break; + case 2: + snprintf(conf, sizeof (conf), "authselectmode mix"); + sel_mode = SRC_AUTHSELECT_MIX; + break; + case 3: + snprintf(conf, sizeof (conf), "authselectmode ignore"); + sel_mode = SRC_AUTHSELECT_IGNORE; + break; + } + + CNF_ParseLine(NULL, 0, conf); + TEST_CHECK(CNF_GetAuthSelectMode() == sel_mode); + + sel_options = random() & (SRC_SELECT_PREFER | SRC_SELECT_TRUST | SRC_SELECT_REQUIRE); + + n1 = random() % 3; + n2 = random() % 3; + n3 = random() % 3; + n4 = random() % 3; + assert(n1 + n2 + n3 < sizeof (srcs) / sizeof (srcs[0])); + + for (j = 0; j < n1; j++) + srcs[j] = create_source(SRC_REFCLOCK, &addrs[j], random() % 2, sel_options); + for (; j < n1 + n2; j++) + srcs[j] = create_source(SRC_NTP, &addrs[j], 1, sel_options); + for (; j < n1 + n2 + n3; j++) + srcs[j] = create_source(SRC_NTP, &addrs[j], 0, sel_options); + for (; j < n1 + n2 + n3 + n4; j++) + srcs[j] = create_source(random() % 2 ? SRC_REFCLOCK : SRC_NTP, &addrs[j], + random() % 2, sel_options | SRC_SELECT_NOSELECT); + + switch (sel_mode) { + case SRC_AUTHSELECT_IGNORE: + for (j = 0; j < n1 + n2 + n3; j++) + TEST_CHECK(srcs[j]->sel_options == sel_options); + break; + case SRC_AUTHSELECT_MIX: + for (j = 0; j < n1 + n2; j++) + TEST_CHECK(srcs[j]->sel_options == + (sel_options | (n2 > 0 && n3 > 0 ? SRC_SELECT_REQUIRE | SRC_SELECT_TRUST : 0))); + for (; j < n1 + n2 + n3; j++) + TEST_CHECK(srcs[j]->sel_options == sel_options); + break; + case SRC_AUTHSELECT_PREFER: + for (j = 0; j < n1 + n2; j++) + TEST_CHECK(srcs[j]->sel_options == sel_options); + for (; j < n1 + n2 + n3; j++) + TEST_CHECK(srcs[j]->sel_options == (sel_options | (n2 > 0 ? SRC_SELECT_NOSELECT : 0))); + break; + case SRC_AUTHSELECT_REQUIRE: + for (j = 0; j < n1 + n2; j++) + TEST_CHECK(srcs[j]->sel_options == sel_options); + for (; j < n1 + n2 + n3; j++) + TEST_CHECK(srcs[j]->sel_options == (sel_options | SRC_SELECT_NOSELECT)); + break; + default: + assert(0); + } + + for (j = n1 + n2 + n3; j < n1 + n2 + n3 + n4; j++) + TEST_CHECK(srcs[j]->sel_options == (sel_options | SRC_SELECT_NOSELECT)); + + for (j = n1 + n2 + n3 + n4 - 1; j >= 0; j--) { + if (j < n1 + n2) + TEST_CHECK(srcs[j]->sel_options == sel_options); + SRC_DestroyInstance(srcs[j]); + } + } + + NSR_Finalise(); REF_Finalise(); SRC_Finalise(); SCH_Finalise(); diff -Nru chrony-3.5/test/unit/test.c chrony-4.1/test/unit/test.c --- chrony-3.5/test/unit/test.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/unit/test.c 2021-05-12 11:06:15.000000000 +0000 @@ -43,6 +43,7 @@ int main(int argc, char **argv) { + LOG_Severity log_severity; char *test_name, *s; int i, seed = 0; struct timeval tv; @@ -55,9 +56,11 @@ if (s) test_name = s + 1; + log_severity = LOGS_FATAL; + for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-d")) { - LOG_SetDebugLevel(2); + log_severity = LOGS_DEBUG; } else if (!strcmp(argv[i], "-s") && i + 1 < argc) { seed = atoi(argv[++i]); } else { @@ -73,6 +76,7 @@ fflush(stdout); LOG_Initialise(); + LOG_SetMinSeverity(log_severity); test_unit(); @@ -83,16 +87,6 @@ return 0; } -void TST_SuspendLogging(void) -{ - LOG_OpenFileLog("/dev/null"); -} - -void TST_ResumeLogging(void) -{ - LOG_OpenFileLog(NULL); -} - double TST_GetRandomDouble(double min, double max) { diff -Nru chrony-3.5/test/unit/test.h chrony-4.1/test/unit/test.h --- chrony-3.5/test/unit/test.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/unit/test.h 2021-05-12 11:06:15.000000000 +0000 @@ -44,9 +44,6 @@ extern void TST_Fail(int line); extern void TST_Skip(int line); -extern void TST_SuspendLogging(void); -extern void TST_ResumeLogging(void); - extern double TST_GetRandomDouble(double min, double max); extern void TST_GetRandomAddress(IPAddr *ip, int family, int bits); extern void TST_SwapAddressBit(IPAddr *ip, unsigned int b); diff -Nru chrony-3.5/test/unit/util.c chrony-4.1/test/unit/util.c --- chrony-3.5/test/unit/util.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/test/unit/util.c 2021-05-12 11:06:15.000000000 +0000 @@ -1,19 +1,52 @@ +/* + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2017-2018, 2021 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + */ + #include #include "test.h" -void test_unit(void) { +static volatile int handled_signal = 0; + +static void +handle_signal(int signal) +{ + handled_signal = signal; +} + +void +test_unit(void) +{ + struct timespec ts, ts2, ts3, ts4; + char buf[16], *s, *s2, *words[3]; NTP_int64 ntp_ts, ntp_fuzz; NTP_int32 ntp32_ts; - struct timespec ts, ts2; struct timeval tv; - struct sockaddr_un sun; double x, y, nan, inf; + IPAddr ip, ip2, ip3; + IPSockAddr ip_saddr; Timespec tspec; Float f; int i, j, c; - char buf[16], *s; uid_t uid; gid_t gid; + struct stat st; + FILE *file; for (i = -31; i < 31; i++) { x = pow(2.0, i); @@ -37,11 +70,6 @@ TEST_CHECK(x > 0.0 || x <= 0.0); } - for (i = 0; i < 100000; i++) { - UTI_GetRandomBytes(&ntp32_ts, sizeof (ntp32_ts)); - TEST_CHECK(UTI_DoubleToNtp32(UTI_Ntp32ToDouble(ntp32_ts)) == ntp32_ts); - } - TEST_CHECK(UTI_DoubleToNtp32(1.0) == htonl(65536)); TEST_CHECK(UTI_DoubleToNtp32(0.0) == htonl(0)); TEST_CHECK(UTI_DoubleToNtp32(1.0 / (65536.0)) == htonl(1)); @@ -79,11 +107,19 @@ ntp_ts.hi = htonl(JAN_1970); ntp_ts.lo = 0xffffffff; UTI_Ntp64ToTimespec(&ntp_ts, &ts); +#if defined(HAVE_LONG_TIME_T) && NTP_ERA_SPLIT > 0 + TEST_CHECK(ts.tv_sec == 0x100000000LL * (1 + (NTP_ERA_SPLIT - 1) / 0x100000000LL)); +#else TEST_CHECK(ts.tv_sec == 0); +#endif TEST_CHECK(ts.tv_nsec == 999999999); UTI_AddDoubleToTimespec(&ts, 1e-9, &ts); +#if defined(HAVE_LONG_TIME_T) && NTP_ERA_SPLIT > 0 + TEST_CHECK(ts.tv_sec == 1 + 0x100000000LL * (1 + (NTP_ERA_SPLIT - 1) / 0x100000000LL)); +#else TEST_CHECK(ts.tv_sec == 1); +#endif TEST_CHECK(ts.tv_nsec == 0); ntp_fuzz.hi = 0; @@ -93,12 +129,71 @@ TEST_CHECK(ntp_ts.hi == htonl(JAN_1970 + 1)); TEST_CHECK(ntp_ts.lo == ntp_fuzz.lo); - ts.tv_sec = ts.tv_nsec = 0; + ts.tv_sec = ts.tv_nsec = 1; + UTI_ZeroTimespec(&ts); + TEST_CHECK(ts.tv_sec == 0); + TEST_CHECK(ts.tv_nsec == 0); + TEST_CHECK(UTI_IsZeroTimespec(&ts)); + + ntp_ts.hi = ntp_ts.lo == 1; + UTI_ZeroNtp64(&ntp_ts); + TEST_CHECK(ntp_ts.hi == 0); + TEST_CHECK(ntp_ts.lo == 0); + + tv.tv_sec = tv.tv_usec = 1; + UTI_TimevalToTimespec(&tv, &ts); + TEST_CHECK(ts.tv_sec == 1); + TEST_CHECK(ts.tv_nsec == 1000); + + UTI_TimespecToTimeval(&ts, &tv); + TEST_CHECK(tv.tv_sec == 1); + TEST_CHECK(tv.tv_usec == 1); + + ts.tv_sec = 1; + ts.tv_nsec = 500000000; + TEST_CHECK(fabs(UTI_TimespecToDouble(&ts) - 1.5) < 1.0e-15); + + UTI_DoubleToTimespec(2.75, &ts); + TEST_CHECK(ts.tv_sec == 2); + TEST_CHECK(ts.tv_nsec == 750000000); + + ts.tv_sec = 1; + ts.tv_nsec = 1200000000; + UTI_NormaliseTimespec(&ts); + TEST_CHECK(ts.tv_sec == 2); + TEST_CHECK(ts.tv_nsec == 200000000); + + ts.tv_sec = 1; + ts.tv_nsec = -200000000; + UTI_NormaliseTimespec(&ts); + TEST_CHECK(ts.tv_sec == 0); + TEST_CHECK(ts.tv_nsec == 800000000); + + tv.tv_sec = 1; + tv.tv_usec = 500000; + TEST_CHECK(fabs(UTI_TimevalToDouble(&tv) - 1.5) < 1.0e-15); + + UTI_DoubleToTimeval(2.75, &tv); + TEST_CHECK(tv.tv_sec == 2); + TEST_CHECK(tv.tv_usec == 750000); + + tv.tv_sec = 1; + tv.tv_usec = 1200000; + UTI_NormaliseTimeval(&tv); + TEST_CHECK(tv.tv_sec == 2); + TEST_CHECK(tv.tv_usec == 200000); + + tv.tv_sec = 1; + tv.tv_usec = -200000; + UTI_NormaliseTimeval(&tv); + TEST_CHECK(tv.tv_sec == 0); + TEST_CHECK(tv.tv_usec == 800000); + + UTI_ZeroTimespec(&ts); UTI_TimespecToNtp64(&ts, &ntp_ts, &ntp_fuzz); TEST_CHECK(ntp_ts.hi == 0); TEST_CHECK(ntp_ts.lo == 0); - TEST_CHECK(UTI_IsZeroTimespec(&ts)); TEST_CHECK(UTI_IsZeroNtp64(&ntp_ts)); ts.tv_sec = 1; @@ -165,6 +260,221 @@ TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) < 0); TEST_CHECK(UTI_CompareTimespecs(&ts2, &ts) > 0); + ts.tv_sec = 2; + ts.tv_nsec = 250000000; + ts2.tv_sec = 1; + ts2.tv_nsec = 750000000; + UTI_DiffTimespecs(&ts3, &ts, &ts2); + TEST_CHECK(ts3.tv_sec == 0); + TEST_CHECK(ts3.tv_nsec == 500000000); + TEST_CHECK(fabs(UTI_DiffTimespecsToDouble(&ts, &ts2) - 0.5) < 1.0e-15); + + ts.tv_sec = 2; + ts.tv_nsec = 250000000; + ts2.tv_sec = 3; + ts2.tv_nsec = 750000000; + UTI_DiffTimespecs(&ts3, &ts, &ts2); + TEST_CHECK(ts3.tv_sec == -2); + TEST_CHECK(ts3.tv_nsec == 500000000); + TEST_CHECK(fabs(UTI_DiffTimespecsToDouble(&ts, &ts2) - -1.5) < 1.0e-15); + + ts.tv_sec = 2; + ts.tv_nsec = 250000000; + UTI_AddDoubleToTimespec(&ts, 2.5, &ts2); + TEST_CHECK(ts2.tv_sec == 4); + TEST_CHECK(ts2.tv_nsec == 750000000); + + ts.tv_sec = 4; + ts.tv_nsec = 500000000; + ts2.tv_sec = 1; + ts2.tv_nsec = 750000000; + UTI_AverageDiffTimespecs(&ts, &ts2, &ts3, &x); + TEST_CHECK(ts3.tv_sec == 3); + TEST_CHECK(ts3.tv_nsec == 125000000); + TEST_CHECK(x == -2.75); + + ts.tv_sec = 4; + ts.tv_nsec = 500000000; + ts2.tv_sec = 1; + ts2.tv_nsec = 750000000; + ts3.tv_sec = 5; + ts3.tv_nsec = 250000000; + UTI_AddDiffToTimespec(&ts, &ts2, &ts3, &ts4); + TEST_CHECK(ts4.tv_sec == 8); + TEST_CHECK(ts4.tv_nsec == 0); + + ts.tv_sec = 1600000000; + ts.tv_nsec = 123; + s = UTI_TimespecToString(&ts); + TEST_CHECK(strcmp(s, "1600000000.000000123") == 0); + + ntp_ts.hi = 1; + ntp_ts.hi = 2; + UTI_Ntp64ToTimespec(&ntp_ts, &ts); + s = UTI_Ntp64ToString(&ntp_ts); + s2 = UTI_TimespecToString(&ts); + TEST_CHECK(strcmp(s, s2) == 0); + + s = UTI_RefidToString(0x41424344); + TEST_CHECK(strcmp(s, "ABCD") == 0); + + ip.family = IPADDR_UNSPEC; + s = UTI_IPToString(&ip); + TEST_CHECK(strcmp(s, "[UNSPEC]") == 0); + TEST_CHECK(UTI_IPToRefid(&ip) == 0); + TEST_CHECK(UTI_IPToHash(&ip) == UTI_IPToHash(&ip)); + + ip.family = IPADDR_INET4; + ip.addr.in4 = 0x7f010203; + s = UTI_IPToString(&ip); + TEST_CHECK(strcmp(s, "127.1.2.3") == 0); + TEST_CHECK(UTI_IPToRefid(&ip) == 0x7f010203); + TEST_CHECK(UTI_IPToHash(&ip) == UTI_IPToHash(&ip)); + + ip.family = IPADDR_INET6; + memset(&ip.addr.in6, 0, sizeof (ip.addr.in6)); + ip.addr.in6[0] = 0xab; + ip.addr.in6[15] = 0xcd; + s = UTI_IPToString(&ip); +#ifdef FEAT_IPV6 + TEST_CHECK(strcmp(s, "ab00::cd") == 0); +#else + TEST_CHECK(strcmp(s, "ab00:0000:0000:0000:0000:0000:0000:00cd") == 0); +#endif + TEST_CHECK(UTI_IPToRefid(&ip) == 0x5f9aa602); + TEST_CHECK(UTI_IPToHash(&ip) == UTI_IPToHash(&ip)); + + ip.family = IPADDR_ID; + ip.addr.id = 12345; + s = UTI_IPToString(&ip); + TEST_CHECK(strcmp(s, "ID#0000012345") == 0); + TEST_CHECK(UTI_IPToRefid(&ip) == 0); + TEST_CHECK(UTI_IPToHash(&ip) == UTI_IPToHash(&ip)); + + ip.family = IPADDR_UNSPEC + 10; + s = UTI_IPToString(&ip); + TEST_CHECK(strcmp(s, "[UNKNOWN]") == 0); + TEST_CHECK(UTI_IPToRefid(&ip) == 0); + TEST_CHECK(UTI_IPToHash(&ip) == UTI_IPToHash(&ip)); + + TEST_CHECK(UTI_StringToIP("200.4.5.6", &ip)); + TEST_CHECK(ip.family == IPADDR_INET4); + TEST_CHECK(ip.addr.in4 == 0xc8040506); + +#ifdef FEAT_IPV6 + TEST_CHECK(UTI_StringToIP("1234::7890", &ip)); + TEST_CHECK(ip.family == IPADDR_INET6); + TEST_CHECK(ip.addr.in6[0] == 0x12 && ip.addr.in6[1] == 0x34); + TEST_CHECK(ip.addr.in6[2] == 0x00 && ip.addr.in6[13] == 0x00); + TEST_CHECK(ip.addr.in6[14] == 0x78 && ip.addr.in6[15] == 0x90); +#else + TEST_CHECK(!UTI_StringToIP("1234::7890", &ip)); +#endif + + TEST_CHECK(!UTI_StringToIP("ID#0000012345", &ip)); + + TEST_CHECK(UTI_IsStringIP("1.2.3.4")); + TEST_CHECK(!UTI_IsStringIP("127.3.3")); + TEST_CHECK(!UTI_IsStringIP("127.3")); + TEST_CHECK(!UTI_IsStringIP("127")); +#ifdef FEAT_IPV6 + TEST_CHECK(UTI_IsStringIP("1234:5678::aaaa")); +#else + TEST_CHECK(!UTI_IsStringIP("1234:5678::aaaa")); +#endif + TEST_CHECK(!UTI_StringToIP("ID#0000012345", &ip)); + + TEST_CHECK(!UTI_StringToIdIP("1.2.3.4", &ip)); + TEST_CHECK(UTI_StringToIdIP("ID#0000056789", &ip)); + TEST_CHECK(ip.family == IPADDR_ID); + TEST_CHECK(ip.addr.id == 56789); + + for (i = IPADDR_UNSPEC; i <= IPADDR_ID + 1; i++) { + ip.family = i; + TEST_CHECK(UTI_IsIPReal(&ip) == (i == IPADDR_INET4 || i == IPADDR_INET6)); + } + + ip.family = IPADDR_UNSPEC; + UTI_IPHostToNetwork(&ip, &ip2); + TEST_CHECK(ip2.family == htons(IPADDR_UNSPEC)); + UTI_IPNetworkToHost(&ip2, &ip3); + TEST_CHECK(ip3.family == IPADDR_UNSPEC); + + ip.family = IPADDR_INET4; + ip.addr.in4 = 0x12345678; + UTI_IPHostToNetwork(&ip, &ip2); + TEST_CHECK(ip2.family == htons(IPADDR_INET4)); + TEST_CHECK(ip2.addr.in4 == htonl(0x12345678)); + UTI_IPNetworkToHost(&ip2, &ip3); + TEST_CHECK(ip3.family == IPADDR_INET4); + TEST_CHECK(ip3.addr.in4 == 0x12345678); + + ip.family = IPADDR_INET6; + for (i = 0; i < 16; i++) + ip.addr.in6[i] = i; + UTI_IPHostToNetwork(&ip, &ip2); + TEST_CHECK(ip2.family == htons(IPADDR_INET6)); + for (i = 0; i < 16; i++) + TEST_CHECK(ip.addr.in6[i] == i); + UTI_IPNetworkToHost(&ip2, &ip3); + TEST_CHECK(ip3.family == IPADDR_INET6); + for (i = 0; i < 16; i++) + TEST_CHECK(ip.addr.in6[i] == i); + + ip.family = IPADDR_ID; + ip.addr.in4 = 0x87654321; + UTI_IPHostToNetwork(&ip, &ip2); + TEST_CHECK(ip2.family == htons(IPADDR_ID)); + TEST_CHECK(ip2.addr.in4 == htonl(0x87654321)); + UTI_IPNetworkToHost(&ip2, &ip3); + TEST_CHECK(ip3.family == IPADDR_ID); + TEST_CHECK(ip3.addr.in4 == 0x87654321); + + for (i = 0; i < 16; i++) + ip.addr.in6[i] = 0x80; + ip2 = ip; + + for (i = IPADDR_UNSPEC; i <= IPADDR_ID; i++) { + ip.family = i; + ip2.family = i + 1; + TEST_CHECK(UTI_CompareIPs(&ip, &ip2, NULL) < 0); + TEST_CHECK(UTI_CompareIPs(&ip2, &ip, NULL) > 0); + ip2 = ip; + ip2.addr.in4++; + if (i == IPADDR_UNSPEC) { + TEST_CHECK(UTI_CompareIPs(&ip, &ip2, NULL) == 0); + TEST_CHECK(UTI_CompareIPs(&ip, &ip2, &ip) == 0); + } else { + TEST_CHECK(UTI_CompareIPs(&ip, &ip2, NULL) < 0); + TEST_CHECK(UTI_CompareIPs(&ip2, &ip, NULL) > 0); + if (i == IPADDR_ID) { + TEST_CHECK(UTI_CompareIPs(&ip, &ip2, &ip) < 0); + TEST_CHECK(UTI_CompareIPs(&ip, &ip2, &ip2) < 0); + } else { + TEST_CHECK(UTI_CompareIPs(&ip, &ip2, &ip) == 0); + TEST_CHECK(UTI_CompareIPs(&ip, &ip2, &ip2) < 0); + } + } + } + + ip_saddr.ip_addr.family = IPADDR_INET4; + ip_saddr.ip_addr.addr.in4 = 0x01020304; + ip_saddr.port = 12345; + s = UTI_IPSockAddrToString(&ip_saddr); + TEST_CHECK(strcmp(s, "1.2.3.4:12345") == 0); + + s = UTI_TimeToLogForm(2000000000); + TEST_CHECK(strcmp(s, "2033-05-18 03:33:20") == 0); + + ts.tv_sec = 3; + ts.tv_nsec = 500000000; + ts2.tv_sec = 4; + ts2.tv_nsec = 250000000; + UTI_AdjustTimespec(&ts, &ts2, &ts3, &x, 2.0, -5.0); + TEST_CHECK(fabs(x - 6.5) < 1.0e-15); + TEST_CHECK(ts3.tv_sec == 10); + TEST_CHECK(ts3.tv_nsec == 0); + for (i = -32; i <= 32; i++) { for (j = c = 0; j < 1000; j++) { UTI_GetNtp64Fuzz(&ntp_fuzz, i); @@ -184,6 +494,18 @@ TEST_CHECK(c > 400 && c < 600); } + TEST_CHECK(UTI_DoubleToNtp32(-1.0) == htonl(0)); + TEST_CHECK(UTI_DoubleToNtp32(0.0) == htonl(0)); + TEST_CHECK(UTI_DoubleToNtp32(1e-9) == htonl(1)); + TEST_CHECK(UTI_DoubleToNtp32(32768.0) == htonl(0x80000000)); + TEST_CHECK(UTI_DoubleToNtp32(65536.0) == htonl(0xffffffff)); + TEST_CHECK(UTI_DoubleToNtp32(65537.0) == htonl(0xffffffff)); + + for (i = 0; i < 100000; i++) { + UTI_GetRandomBytes(&ntp32_ts, sizeof (ntp32_ts)); + TEST_CHECK(UTI_DoubleToNtp32(UTI_Ntp32ToDouble(ntp32_ts)) == ntp32_ts); + } + ts.tv_nsec = 0; ts.tv_sec = 10; @@ -197,31 +519,54 @@ TEST_CHECK(!UTI_IsTimeOffsetSane(&ts, 10.0)); TEST_CHECK(UTI_IsTimeOffsetSane(&ts, -20.0)); + TEST_CHECK(UTI_Log2ToDouble(-1) == 0.5); + TEST_CHECK(UTI_Log2ToDouble(0) == 1.0); + TEST_CHECK(UTI_Log2ToDouble(1) == 2.0); + TEST_CHECK(UTI_Log2ToDouble(-31) < UTI_Log2ToDouble(-30)); + TEST_CHECK(UTI_Log2ToDouble(-32) == UTI_Log2ToDouble(-31)); + TEST_CHECK(UTI_Log2ToDouble(30) < UTI_Log2ToDouble(32)); + TEST_CHECK(UTI_Log2ToDouble(31) == UTI_Log2ToDouble(32)); + UTI_TimespecHostToNetwork(&ts, &tspec); +#ifdef HAVE_LONG_TIME_T + TEST_CHECK(tspec.tv_sec_high == htonl(ts.tv_sec >> 32)); +#else + TEST_CHECK(tspec.tv_sec_high == htonl(TV_NOHIGHSEC)); +#endif + TEST_CHECK(tspec.tv_sec_low == htonl(ts.tv_sec)); + TEST_CHECK(tspec.tv_nsec == htonl(ts.tv_nsec)); UTI_TimespecNetworkToHost(&tspec, &ts2); TEST_CHECK(!UTI_CompareTimespecs(&ts, &ts2)); - for (i = c = 0; i < 100000; i++) { - j = random() % (sizeof (buf) + 1); - UTI_GetRandomBytes(buf, j); - if (j && buf[j - 1] % 2) - c++; - } - TEST_CHECK(c > 46000 && c < 48000); - - for (i = 1; i < 2 * BUFFER_LENGTH; i++) { - sun.sun_family = AF_UNIX; - for (j = 0; j + 1 < i && j + 1 < sizeof (sun.sun_path); j++) - sun.sun_path[j] = 'A' + j % 26; - sun.sun_path[j] = '\0'; - s = UTI_SockaddrToString((struct sockaddr *)&sun); - if (i <= BUFFER_LENGTH) { - TEST_CHECK(!strcmp(s, sun.sun_path)); - } else { - TEST_CHECK(!strncmp(s, sun.sun_path, BUFFER_LENGTH - 2)); - TEST_CHECK(s[BUFFER_LENGTH - 2] == '>'); - } - } + TEST_CHECK(UTI_CmacNameToAlgorithm("AES128") == CMC_AES128); + TEST_CHECK(UTI_CmacNameToAlgorithm("AES256") == CMC_AES256); + TEST_CHECK(UTI_CmacNameToAlgorithm("NOSUCHCMAC") == CMC_INVALID); + + TEST_CHECK(UTI_HashNameToAlgorithm("MD5") == HSH_MD5); + TEST_CHECK(UTI_HashNameToAlgorithm("SHA1") == HSH_SHA1); + TEST_CHECK(UTI_HashNameToAlgorithm("SHA256") == HSH_SHA256); + TEST_CHECK(UTI_HashNameToAlgorithm("SHA384") == HSH_SHA384); + TEST_CHECK(UTI_HashNameToAlgorithm("SHA512") == HSH_SHA512); + TEST_CHECK(UTI_HashNameToAlgorithm("SHA3-224") == HSH_SHA3_224); + TEST_CHECK(UTI_HashNameToAlgorithm("SHA3-256") == HSH_SHA3_256); + TEST_CHECK(UTI_HashNameToAlgorithm("SHA3-384") == HSH_SHA3_384); + TEST_CHECK(UTI_HashNameToAlgorithm("SHA3-512") == HSH_SHA3_512); + TEST_CHECK(UTI_HashNameToAlgorithm("TIGER") == HSH_TIGER); + TEST_CHECK(UTI_HashNameToAlgorithm("WHIRLPOOL") == HSH_WHIRLPOOL); + TEST_CHECK(UTI_HashNameToAlgorithm("NOSUCHHASH") == HSH_INVALID); + + i = open("/dev/null", 0); + TEST_CHECK(UTI_FdSetCloexec(i)); + j = fcntl(i, F_GETFD); + TEST_CHECK(j & F_GETFD); + close(i); + + UTI_SetQuitSignalsHandler(handle_signal, 0); + TEST_CHECK(handled_signal == 0); + kill(getpid(), SIGPIPE); + while (handled_signal == 0) + ; + TEST_CHECK(handled_signal == SIGPIPE); s = UTI_PathToDir("/aaa/bbb/ccc/ddd"); TEST_CHECK(!strcmp(s, "/aaa/bbb/ccc")); @@ -258,10 +603,114 @@ TEST_CHECK(UTI_CreateDirAndParents("testdir", 0700, uid, gid)); - TST_SuspendLogging(); TEST_CHECK(UTI_CheckDirPermissions("testdir", 0700, uid, gid)); TEST_CHECK(!UTI_CheckDirPermissions("testdir", 0300, uid, gid)); TEST_CHECK(!UTI_CheckDirPermissions("testdir", 0700, uid + 1, gid)); TEST_CHECK(!UTI_CheckDirPermissions("testdir", 0700, uid, gid + 1)); - TST_ResumeLogging(); + + umask(0); + + unlink("testfile"); + file = UTI_OpenFile(NULL, "testfile", NULL, 'r', 0); + TEST_CHECK(!file); + TEST_CHECK(stat("testfile", &st) < 0); + + file = UTI_OpenFile(NULL, "testfile", NULL, 'w', 0644); + TEST_CHECK(file); + TEST_CHECK(stat("testfile", &st) == 0); + TEST_CHECK((st.st_mode & 0777) == 0644); + fclose(file); + + file = UTI_OpenFile(".", "test", "file", 'W', 0640); + TEST_CHECK(file); + TEST_CHECK(stat("testfile", &st) == 0); + TEST_CHECK((st.st_mode & 0777) == 0640); + fclose(file); + + file = UTI_OpenFile(NULL, "test", "file", 'r', 0); + TEST_CHECK(file); + fclose(file); + + TEST_CHECK(UTI_RenameTempFile(NULL, "testfil", "e", NULL)); + TEST_CHECK(stat("testfil", &st) == 0); + file = UTI_OpenFile(NULL, "testfil", NULL, 'R', 0); + TEST_CHECK(file); + fclose(file); + + TEST_CHECK(UTI_RenameTempFile(NULL, "test", "fil", "file")); + TEST_CHECK(stat("testfile", &st) == 0); + file = UTI_OpenFile(NULL, "testfile", NULL, 'R', 0); + TEST_CHECK(file); + fclose(file); + + TEST_CHECK(UTI_RemoveFile(NULL, "testfile", NULL)); + TEST_CHECK(stat("testfile", &st) < 0); + TEST_CHECK(!UTI_RemoveFile(NULL, "testfile", NULL)); + + for (i = c = 0; i < 100000; i++) { + j = random() % (sizeof (buf) + 1); + UTI_GetRandomBytesUrandom(buf, j); + if (j && buf[j - 1] % 2) + c++; + } + TEST_CHECK(c > 46000 && c < 48000); + + for (i = c = 0; i < 100000; i++) { + j = random() % (sizeof (buf) + 1); + UTI_GetRandomBytes(buf, j); + if (j && buf[j - 1] % 2) + c++; + } + TEST_CHECK(c > 46000 && c < 48000); + + assert(sizeof (buf) >= 16); + TEST_CHECK(UTI_HexToBytes("", buf, sizeof (buf)) == 0); + TEST_CHECK(UTI_HexToBytes("0", buf, sizeof (buf)) == 0); + TEST_CHECK(UTI_HexToBytes("00123456789ABCDEF", buf, sizeof (buf)) == 0); + TEST_CHECK(UTI_HexToBytes("00123456789ABCDEF0", buf, 8) == 0); + TEST_CHECK(UTI_HexToBytes("00123456789ABCDEF0", buf, sizeof (buf)) == 9); + TEST_CHECK(memcmp(buf, "\x00\x12\x34\x56\x78\x9A\xBC\xDE\xF0", 9) == 0); + memcpy(buf, "AB123456780001", 15); + TEST_CHECK(UTI_HexToBytes(buf, buf, sizeof (buf)) == 7); + 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, 15) == 1); + 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(UTI_SplitString(buf, words, 3) == 0); + TEST_CHECK(!words[0]); + TEST_CHECK(snprintf(buf, sizeof (buf), "%s", " ") < sizeof (buf)); + TEST_CHECK(UTI_SplitString(buf, words, 3) == 0); + TEST_CHECK(!words[0]); + TEST_CHECK(snprintf(buf, sizeof (buf), "%s", "a \n ") < sizeof (buf)); + TEST_CHECK(UTI_SplitString(buf, words, 3) == 1); + TEST_CHECK(words[0] == buf + 0); + TEST_CHECK(strcmp(words[0], "a") == 0); + TEST_CHECK(snprintf(buf, sizeof (buf), "%s", " a ") < sizeof (buf)); + TEST_CHECK(UTI_SplitString(buf, words, 3) == 1); + TEST_CHECK(words[0] == buf + 2); + TEST_CHECK(strcmp(words[0], "a") == 0); + TEST_CHECK(snprintf(buf, sizeof (buf), "%s", " \n a") < sizeof (buf)); + TEST_CHECK(UTI_SplitString(buf, words, 3) == 1); + TEST_CHECK(words[0] == buf + 4); + TEST_CHECK(strcmp(words[0], "a") == 0); + TEST_CHECK(snprintf(buf, sizeof (buf), "%s", "a b") < sizeof (buf)); + TEST_CHECK(UTI_SplitString(buf, words, 1) == 2); + TEST_CHECK(snprintf(buf, sizeof (buf), "%s", "a b") < sizeof (buf)); + TEST_CHECK(UTI_SplitString(buf, words, 2) == 2); + TEST_CHECK(words[0] == buf + 0); + TEST_CHECK(words[1] == buf + 4); + TEST_CHECK(strcmp(words[0], "a") == 0); + TEST_CHECK(strcmp(words[1], "b") == 0); + TEST_CHECK(snprintf(buf, sizeof (buf), "%s", " a b ") < sizeof (buf)); + TEST_CHECK(UTI_SplitString(buf, words, 3) == 2); + TEST_CHECK(words[0] == buf + 1); + TEST_CHECK(words[1] == buf + 3); + TEST_CHECK(strcmp(words[0], "a") == 0); + TEST_CHECK(strcmp(words[1], "b") == 0); } diff -Nru chrony-3.5/util.c chrony-4.1/util.c --- chrony-3.5/util.c 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/util.c 2021-05-12 11:06:15.000000000 +0000 @@ -3,7 +3,7 @@ ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 - * Copyright (C) Miroslav Lichvar 2009, 2012-2018 + * Copyright (C) Miroslav Lichvar 2009, 2012-2020 * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -56,7 +56,7 @@ /* ================================================== */ void -UTI_TimevalToTimespec(struct timeval *tv, struct timespec *ts) +UTI_TimevalToTimespec(const struct timeval *tv, struct timespec *ts) { ts->tv_sec = tv->tv_sec; ts->tv_nsec = 1000 * tv->tv_usec; @@ -65,7 +65,7 @@ /* ================================================== */ void -UTI_TimespecToTimeval(struct timespec *ts, struct timeval *tv) +UTI_TimespecToTimeval(const struct timespec *ts, struct timeval *tv) { tv->tv_sec = ts->tv_sec; tv->tv_usec = ts->tv_nsec / 1000; @@ -74,7 +74,7 @@ /* ================================================== */ double -UTI_TimespecToDouble(struct timespec *ts) +UTI_TimespecToDouble(const struct timespec *ts) { return ts->tv_sec + 1.0e-9 * ts->tv_nsec; } @@ -109,7 +109,7 @@ /* ================================================== */ double -UTI_TimevalToDouble(struct timeval *tv) +UTI_TimevalToDouble(const struct timeval *tv) { return tv->tv_sec + 1.0e-6 * tv->tv_usec; } @@ -149,7 +149,7 @@ /* ================================================== */ int -UTI_CompareTimespecs(struct timespec *a, struct timespec *b) +UTI_CompareTimespecs(const struct timespec *a, const struct timespec *b) { if (a->tv_sec < b->tv_sec) return -1; @@ -165,7 +165,7 @@ /* ================================================== */ void -UTI_DiffTimespecs(struct timespec *result, struct timespec *a, struct timespec *b) +UTI_DiffTimespecs(struct timespec *result, const struct timespec *a, const struct timespec *b) { result->tv_sec = a->tv_sec - b->tv_sec; result->tv_nsec = a->tv_nsec - b->tv_nsec; @@ -176,7 +176,7 @@ /* Calculate result = a - b and return as a double */ double -UTI_DiffTimespecsToDouble(struct timespec *a, struct timespec *b) +UTI_DiffTimespecsToDouble(const struct timespec *a, const struct timespec *b) { return ((double)a->tv_sec - (double)b->tv_sec) + 1.0e-9 * (a->tv_nsec - b->tv_nsec); } @@ -184,7 +184,7 @@ /* ================================================== */ void -UTI_AddDoubleToTimespec(struct timespec *start, double increment, struct timespec *end) +UTI_AddDoubleToTimespec(const struct timespec *start, double increment, struct timespec *end) { time_t int_part; @@ -198,7 +198,7 @@ /* Calculate the average and difference (as a double) of two timespecs */ void -UTI_AverageDiffTimespecs(struct timespec *earlier, struct timespec *later, +UTI_AverageDiffTimespecs(const struct timespec *earlier, const struct timespec *later, struct timespec *average, double *diff) { *diff = UTI_DiffTimespecsToDouble(later, earlier); @@ -208,8 +208,8 @@ /* ================================================== */ void -UTI_AddDiffToTimespec(struct timespec *a, struct timespec *b, - struct timespec *c, struct timespec *result) +UTI_AddDiffToTimespec(const struct timespec *a, const struct timespec *b, + const struct timespec *c, struct timespec *result) { double diff; @@ -230,7 +230,7 @@ /* Convert a timespec into a temporary string, largely for diagnostic display */ char * -UTI_TimespecToString(struct timespec *ts) +UTI_TimespecToString(const struct timespec *ts) { char *result; @@ -250,7 +250,7 @@ for diagnostic display */ char * -UTI_Ntp64ToString(NTP_int64 *ntp_ts) +UTI_Ntp64ToString(const NTP_int64 *ntp_ts) { struct timespec ts; UTI_Ntp64ToTimespec(ntp_ts, &ts); @@ -281,10 +281,10 @@ /* ================================================== */ char * -UTI_IPToString(IPAddr *addr) +UTI_IPToString(const IPAddr *addr) { unsigned long a, b, c, d, ip; - uint8_t *ip6; + const uint8_t *ip6; char *result; result = NEXT_BUFFER; @@ -311,6 +311,9 @@ (unsigned int)(ip6[2 * a] << 8 | ip6[2 * a + 1])); #endif break; + case IPADDR_ID: + snprintf(result, BUFFER_LENGTH, "ID#%010"PRIu32, addr->addr.id); + break; default: snprintf(result, BUFFER_LENGTH, "[UNKNOWN]"); } @@ -322,9 +325,10 @@ int UTI_StringToIP(const char *addr, IPAddr *ip) { -#ifdef FEAT_IPV6 struct in_addr in4; +#ifdef FEAT_IPV6 struct in6_addr in6; +#endif if (inet_pton(AF_INET, addr, &in4) > 0) { ip->family = IPADDR_INET4; @@ -333,32 +337,60 @@ return 1; } +#ifdef FEAT_IPV6 if (inet_pton(AF_INET6, addr, &in6) > 0) { ip->family = IPADDR_INET6; ip->_pad = 0; memcpy(ip->addr.in6, in6.s6_addr, sizeof (ip->addr.in6)); return 1; } -#else - unsigned long a, b, c, d, n; +#endif - n = sscanf(addr, "%lu.%lu.%lu.%lu", &a, &b, &c, &d); - if (n == 4) { - ip->family = IPADDR_INET4; + return 0; +} + +/* ================================================== */ + +int +UTI_IsStringIP(const char *string) +{ + IPAddr ip; + + return UTI_StringToIP(string, &ip); +} + +/* ================================================== */ + +int +UTI_StringToIdIP(const char *addr, IPAddr *ip) +{ + if (sscanf(addr, "ID#%"SCNu32, &ip->addr.id) == 1) { + ip->family = IPADDR_ID; ip->_pad = 0; - ip->addr.in4 = ((a & 0xff) << 24) | ((b & 0xff) << 16) | - ((c & 0xff) << 8) | (d & 0xff); return 1; } -#endif return 0; } /* ================================================== */ +int +UTI_IsIPReal(const IPAddr *ip) +{ + switch (ip->family) { + case IPADDR_INET4: + case IPADDR_INET6: + return 1; + default: + return 0; + } +} + +/* ================================================== */ + uint32_t -UTI_IPToRefid(IPAddr *ip) +UTI_IPToRefid(const IPAddr *ip) { static int MD5_hash = -1; unsigned char buf[16]; @@ -368,7 +400,7 @@ return ip->addr.in4; case IPADDR_INET6: if (MD5_hash < 0) - MD5_hash = HSH_GetHashId("MD5"); + MD5_hash = HSH_GetHashId(HSH_MD5); if (MD5_hash < 0 || HSH_Hash(MD5_hash, (const unsigned char *)ip->addr.in6, sizeof (ip->addr.in6), @@ -383,10 +415,10 @@ /* ================================================== */ uint32_t -UTI_IPToHash(IPAddr *ip) +UTI_IPToHash(const IPAddr *ip) { static uint32_t seed = 0; - unsigned char *addr; + const unsigned char *addr; unsigned int i, len; uint32_t hash; @@ -399,6 +431,10 @@ addr = ip->addr.in6; len = sizeof (ip->addr.in6); break; + case IPADDR_ID: + addr = (unsigned char *)&ip->addr.id; + len = sizeof (ip->addr.id); + break; default: return 0; } @@ -417,7 +453,7 @@ /* ================================================== */ void -UTI_IPHostToNetwork(IPAddr *src, IPAddr *dest) +UTI_IPHostToNetwork(const IPAddr *src, IPAddr *dest) { /* Don't send uninitialized bytes over network */ memset(dest, 0, sizeof (IPAddr)); @@ -431,6 +467,9 @@ case IPADDR_INET6: memcpy(dest->addr.in6, src->addr.in6, sizeof (dest->addr.in6)); break; + case IPADDR_ID: + dest->addr.id = htonl(src->addr.id); + break; default: dest->family = htons(IPADDR_UNSPEC); } @@ -439,7 +478,7 @@ /* ================================================== */ void -UTI_IPNetworkToHost(IPAddr *src, IPAddr *dest) +UTI_IPNetworkToHost(const IPAddr *src, IPAddr *dest) { dest->family = ntohs(src->family); dest->_pad = 0; @@ -451,6 +490,9 @@ case IPADDR_INET6: memcpy(dest->addr.in6, src->addr.in6, sizeof (dest->addr.in6)); break; + case IPADDR_ID: + dest->addr.id = ntohl(src->addr.id); + break; default: dest->family = IPADDR_UNSPEC; } @@ -459,7 +501,7 @@ /* ================================================== */ int -UTI_CompareIPs(IPAddr *a, IPAddr *b, IPAddr *mask) +UTI_CompareIPs(const IPAddr *a, const IPAddr *b, const IPAddr *mask) { int i, d; @@ -486,124 +528,29 @@ d = a->addr.in6[i] - b->addr.in6[i]; } return d; + case IPADDR_ID: + return a->addr.id - b->addr.id; } return 0; } /* ================================================== */ -void -UTI_SockaddrToIPAndPort(struct sockaddr *sa, IPAddr *ip, unsigned short *port) -{ - switch (sa->sa_family) { - case AF_INET: - ip->family = IPADDR_INET4; - ip->addr.in4 = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr); - *port = ntohs(((struct sockaddr_in *)sa)->sin_port); - break; -#ifdef FEAT_IPV6 - case AF_INET6: - ip->family = IPADDR_INET6; - memcpy(ip->addr.in6, ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr, - sizeof (ip->addr.in6)); - *port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port); - break; -#endif - default: - ip->family = IPADDR_UNSPEC; - *port = 0; - } -} - -/* ================================================== */ - -int -UTI_IPAndPortToSockaddr(IPAddr *ip, unsigned short port, struct sockaddr *sa) -{ - switch (ip->family) { - case IPADDR_INET4: - memset(sa, 0, sizeof (struct sockaddr_in)); - sa->sa_family = AF_INET; - ((struct sockaddr_in *)sa)->sin_addr.s_addr = htonl(ip->addr.in4); - ((struct sockaddr_in *)sa)->sin_port = htons(port); -#ifdef SIN6_LEN - ((struct sockaddr_in *)sa)->sin_len = sizeof (struct sockaddr_in); -#endif - return sizeof (struct sockaddr_in); -#ifdef FEAT_IPV6 - case IPADDR_INET6: - memset(sa, 0, sizeof (struct sockaddr_in6)); - sa->sa_family = AF_INET6; - memcpy(((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr, ip->addr.in6, - sizeof (ip->addr.in6)); - ((struct sockaddr_in6 *)sa)->sin6_port = htons(port); -#ifdef SIN6_LEN - ((struct sockaddr_in6 *)sa)->sin6_len = sizeof (struct sockaddr_in6); -#endif - return sizeof (struct sockaddr_in6); -#endif - default: - memset(sa, 0, sizeof (struct sockaddr)); - sa->sa_family = AF_UNSPEC; - return 0; - } -} - -/* ================================================== */ - -char *UTI_SockaddrToString(struct sockaddr *sa) +char * +UTI_IPSockAddrToString(const IPSockAddr *sa) { - unsigned short port; - IPAddr ip; - char *result, *sun_path; + char *result; result = NEXT_BUFFER; - - switch (sa->sa_family) { - case AF_INET: -#ifdef AF_INET6 - case AF_INET6: -#endif - UTI_SockaddrToIPAndPort(sa, &ip, &port); - snprintf(result, BUFFER_LENGTH, "%s:%hu", UTI_IPToString(&ip), port); - break; - case AF_UNIX: - sun_path = ((struct sockaddr_un *)sa)->sun_path; - snprintf(result, BUFFER_LENGTH, "%.*s", BUFFER_LENGTH - 1, sun_path); - /* Indicate truncated path */ - if (strlen(sun_path) >= BUFFER_LENGTH) - result[BUFFER_LENGTH - 2] = '>'; - break; - default: - snprintf(result, BUFFER_LENGTH, "[UNKNOWN]"); - } + snprintf(result, BUFFER_LENGTH, + sa->ip_addr.family != IPADDR_INET6 ? "%s:%hu" : "[%s]:%hu", + UTI_IPToString(&sa->ip_addr), sa->port); return result; } /* ================================================== */ -const char * -UTI_SockaddrFamilyToString(int family) -{ - switch (family) { - case AF_INET: - return "IPv4"; -#ifdef AF_INET6 - case AF_INET6: - return "IPv6"; -#endif - case AF_UNIX: - return "Unix"; - case AF_UNSPEC: - return "UNSPEC"; - default: - return "?"; - } -} - -/* ================================================== */ - char * UTI_TimeToLogForm(time_t t) { @@ -625,7 +572,8 @@ /* ================================================== */ void -UTI_AdjustTimespec(struct timespec *old_ts, struct timespec *when, struct timespec *new_ts, double *delta_time, double dfreq, double doffset) +UTI_AdjustTimespec(const struct timespec *old_ts, const struct timespec *when, + struct timespec *new_ts, double *delta_time, double dfreq, double doffset) { double elapsed; @@ -698,7 +646,7 @@ /* ================================================== */ int -UTI_IsZeroNtp64(NTP_int64 *ts) +UTI_IsZeroNtp64(const NTP_int64 *ts) { return !ts->hi && !ts->lo; } @@ -706,7 +654,7 @@ /* ================================================== */ int -UTI_CompareNtp64(NTP_int64 *a, NTP_int64 *b) +UTI_CompareNtp64(const NTP_int64 *a, const NTP_int64 *b) { int32_t diff; @@ -726,7 +674,8 @@ /* ================================================== */ int -UTI_IsEqualAnyNtp64(NTP_int64 *a, NTP_int64 *b1, NTP_int64 *b2, NTP_int64 *b3) +UTI_IsEqualAnyNtp64(const NTP_int64 *a, const NTP_int64 *b1, const NTP_int64 *b2, + const NTP_int64 *b3) { if (b1 && a->lo == b1->lo && a->hi == b1->hi) return 1; @@ -748,7 +697,7 @@ #define NSEC_PER_NTP64 4.294967296 void -UTI_TimespecToNtp64(struct timespec *src, NTP_int64 *dest, NTP_int64 *fuzz) +UTI_TimespecToNtp64(const struct timespec *src, NTP_int64 *dest, const NTP_int64 *fuzz) { uint32_t hi, lo, sec, nsec; @@ -777,7 +726,7 @@ /* ================================================== */ void -UTI_Ntp64ToTimespec(NTP_int64 *src, struct timespec *dest) +UTI_Ntp64ToTimespec(const NTP_int64 *src, struct timespec *dest) { uint32_t ntp_sec, ntp_frac; @@ -809,7 +758,7 @@ #define MIN_ENDOFTIME_DISTANCE (365 * 24 * 3600) int -UTI_IsTimeOffsetSane(struct timespec *ts, double offset) +UTI_IsTimeOffsetSane(const struct timespec *ts, double offset) { double t; @@ -855,7 +804,7 @@ /* ================================================== */ void -UTI_TimespecNetworkToHost(Timespec *src, struct timespec *dest) +UTI_TimespecNetworkToHost(const Timespec *src, struct timespec *dest) { uint32_t sec_low, nsec; #ifdef HAVE_LONG_TIME_T @@ -880,7 +829,7 @@ /* ================================================== */ void -UTI_TimespecHostToNetwork(struct timespec *src, Timespec *dest) +UTI_TimespecHostToNetwork(const struct timespec *src, Timespec *dest) { dest->tv_nsec = htonl(src->tv_nsec); #ifdef HAVE_LONG_TIME_T @@ -979,18 +928,67 @@ /* ================================================== */ +CMC_Algorithm +UTI_CmacNameToAlgorithm(const char *name) +{ + if (strcmp(name, "AES128") == 0) + return CMC_AES128; + else if (strcmp(name, "AES256") == 0) + return CMC_AES256; + return CMC_INVALID; +} + +/* ================================================== */ + +HSH_Algorithm +UTI_HashNameToAlgorithm(const char *name) +{ + if (strcmp(name, "MD5") == 0) + return HSH_MD5; + else if (strcmp(name, "SHA1") == 0) + return HSH_SHA1; + else if (strcmp(name, "SHA256") == 0) + return HSH_SHA256; + else if (strcmp(name, "SHA384") == 0) + return HSH_SHA384; + else if (strcmp(name, "SHA512") == 0) + return HSH_SHA512; + else if (strcmp(name, "SHA3-224") == 0) + return HSH_SHA3_224; + else if (strcmp(name, "SHA3-256") == 0) + return HSH_SHA3_256; + else if (strcmp(name, "SHA3-384") == 0) + return HSH_SHA3_384; + else if (strcmp(name, "SHA3-512") == 0) + return HSH_SHA3_512; + else if (strcmp(name, "TIGER") == 0) + return HSH_TIGER; + else if (strcmp(name, "WHIRLPOOL") == 0) + return HSH_WHIRLPOOL; + return HSH_INVALID; +} + +/* ================================================== */ + int UTI_FdSetCloexec(int fd) { int flags; flags = fcntl(fd, F_GETFD); - if (flags != -1) { - flags |= FD_CLOEXEC; - return !fcntl(fd, F_SETFD, flags); + if (flags == -1) { + DEBUG_LOG("fcntl() failed : %s", strerror(errno)); + return 0; } - return 0; + flags |= FD_CLOEXEC; + + if (fcntl(fd, F_SETFD, flags) < 0) { + DEBUG_LOG("fcntl() failed : %s", strerror(errno)); + return 0; + } + + return 1; } /* ================================================== */ @@ -1035,6 +1033,7 @@ UTI_PathToDir(const char *path) { char *dir, *slash; + size_t dir_len; slash = strrchr(path, '/'); @@ -1044,8 +1043,11 @@ if (slash == path) return Strdup("/"); - dir = Malloc(slash - path + 1); - snprintf(dir, slash - path + 1, "%s", path); + dir_len = slash - path; + + dir = Malloc(dir_len + 1); + memcpy(dir, path, dir_len); + dir[dir_len] = '\0'; return dir; } @@ -1179,6 +1181,158 @@ /* ================================================== */ +static int +join_path(const char *basedir, const char *name, const char *suffix, + char *buffer, size_t length, LOG_Severity severity) +{ + const char *sep; + + if (!basedir) { + basedir = ""; + sep = ""; + } else { + sep = "/"; + } + + if (!suffix) + suffix = ""; + + if (snprintf(buffer, length, "%s%s%s%s", basedir, sep, name, suffix) >= length) { + LOG(severity, "File path %s%s%s%s too long", basedir, sep, name, suffix); + return 0; + } + + return 1; +} + +/* ================================================== */ + +FILE * +UTI_OpenFile(const char *basedir, const char *name, const char *suffix, + char mode, mode_t perm) +{ + const char *file_mode; + char path[PATH_MAX]; + LOG_Severity severity; + int fd, flags; + FILE *file; + + severity = mode >= 'A' && mode <= 'Z' ? LOGS_FATAL : LOGS_ERR; + + if (!join_path(basedir, name, suffix, path, sizeof (path), severity)) + return NULL; + + switch (mode) { + case 'r': + case 'R': + flags = O_RDONLY; + file_mode = "r"; + if (severity != LOGS_FATAL) + severity = LOGS_DEBUG; + break; + case 'w': + case 'W': + flags = O_WRONLY | O_CREAT | O_EXCL; + file_mode = "w"; + break; + case 'a': + case 'A': + flags = O_WRONLY | O_CREAT | O_APPEND | O_NOFOLLOW; + file_mode = "a"; + break; + default: + assert(0); + return NULL; + } + +try_again: + fd = open(path, flags, perm); + if (fd < 0) { + if (errno == EEXIST) { + if (unlink(path) < 0) { + LOG(severity, "Could not remove %s : %s", path, strerror(errno)); + return NULL; + } + DEBUG_LOG("Removed %s", path); + goto try_again; + } + LOG(severity, "Could not open %s : %s", path, strerror(errno)); + return NULL; + } + + UTI_FdSetCloexec(fd); + + file = fdopen(fd, file_mode); + if (!file) { + LOG(severity, "Could not open %s : %s", path, strerror(errno)); + close(fd); + return NULL; + } + + DEBUG_LOG("Opened %s fd=%d mode=%c", path, fd, mode); + + return file; +} + +/* ================================================== */ + +int +UTI_RenameTempFile(const char *basedir, const char *name, + const char *old_suffix, const char *new_suffix) +{ + char old_path[PATH_MAX], new_path[PATH_MAX]; + + if (!join_path(basedir, name, old_suffix, old_path, sizeof (old_path), LOGS_ERR)) + return 0; + + if (!join_path(basedir, name, new_suffix, new_path, sizeof (new_path), LOGS_ERR)) + goto error; + + if (rename(old_path, new_path) < 0) { + LOG(LOGS_ERR, "Could not replace %s with %s : %s", new_path, old_path, strerror(errno)); + goto error; + } + + DEBUG_LOG("Renamed %s to %s", old_path, new_path); + + return 1; + +error: + if (unlink(old_path) < 0) + LOG(LOGS_ERR, "Could not remove %s : %s", old_path, strerror(errno)); + + return 0; +} + +/* ================================================== */ + +int +UTI_RemoveFile(const char *basedir, const char *name, const char *suffix) +{ + char path[PATH_MAX]; + struct stat buf; + + if (!join_path(basedir, name, suffix, path, sizeof (path), LOGS_ERR)) + return 0; + + /* Avoid logging an error message if the file is not accessible */ + if (stat(path, &buf) < 0) { + DEBUG_LOG("Could not remove %s : %s", path, strerror(errno)); + return 0; + } + + if (unlink(path) < 0) { + LOG(LOGS_ERR, "Could not remove %s : %s", path, strerror(errno)); + return 0; + } + + DEBUG_LOG("Removed %s", path); + + return 1; +} + +/* ================================================== */ + void UTI_DropRoot(uid_t uid, gid_t gid) { @@ -1207,9 +1361,7 @@ static FILE *f = NULL; if (!f) - f = fopen(DEV_URANDOM, "r"); - if (!f) - LOG_FATAL("Can't open %s : %s", DEV_URANDOM, strerror(errno)); + f = UTI_OpenFile(NULL, DEV_URANDOM, NULL, 'R', 0); if (fread(buf, 1, len, f) != len) LOG_FATAL("Can't read from %s", DEV_URANDOM); } @@ -1258,3 +1410,77 @@ UTI_GetRandomBytesUrandom(buf, len); #endif } + +/* ================================================== */ + +int +UTI_BytesToHex(const void *buf, unsigned int buf_len, char *hex, unsigned int hex_len) +{ + unsigned int i, l; + + if (hex_len < 1) + return 0; + + hex[0] = '\0'; + + for (i = l = 0; i < buf_len; i++, l += 2) { + if (l + 2 >= hex_len || + snprintf(hex + l, hex_len - l, "%02hhX", ((const char *)buf)[i]) != 2) + return 0; + } + + return 1; +} + +/* ================================================== */ + +unsigned int +UTI_HexToBytes(const char *hex, void *buf, unsigned int len) +{ + char *p, byte[3]; + unsigned int i; + + for (i = 0; i < len && *hex != '\0'; i++) { + byte[0] = *hex++; + if (*hex == '\0') + return 0; + byte[1] = *hex++; + byte[2] = '\0'; + ((char *)buf)[i] = strtol(byte, &p, 16); + + if (p != byte + 2) + return 0; + } + + return *hex == '\0' ? i : 0; +} + +/* ================================================== */ + +int +UTI_SplitString(char *string, char **words, int max_saved_words) +{ + char *s = string; + int i; + + for (i = 0; i < max_saved_words; i++) + words[i] = NULL; + + for (i = 0; ; i++) { + /* Zero white-space characters before the word */ + while (*s != '\0' && isspace((unsigned char)*s)) + *s++ = '\0'; + + if (*s == '\0') + break; + + if (i < max_saved_words) + words[i] = s; + + /* Find the next word */ + while (*s != '\0' && !isspace((unsigned char)*s)) + s++; + } + + return i; +} diff -Nru chrony-3.5/util.h chrony-4.1/util.h --- chrony-3.5/util.h 2019-05-10 10:22:57.000000000 +0000 +++ chrony-4.1/util.h 2021-05-12 11:06:15.000000000 +0000 @@ -32,6 +32,7 @@ #include "addressing.h" #include "ntp.h" #include "candm.h" +#include "cmac.h" #include "hash.h" /* Zero a timespec */ @@ -41,13 +42,13 @@ extern int UTI_IsZeroTimespec(struct timespec *ts); /* Convert a timeval into a timespec */ -extern void UTI_TimevalToTimespec(struct timeval *tv, struct timespec *ts); +extern void UTI_TimevalToTimespec(const struct timeval *tv, struct timespec *ts); /* Convert a timespec into a timeval */ -extern void UTI_TimespecToTimeval(struct timespec *ts, struct timeval *tv); +extern void UTI_TimespecToTimeval(const struct timespec *ts, struct timeval *tv); /* Convert a timespec into a floating point number of seconds */ -extern double UTI_TimespecToDouble(struct timespec *ts); +extern double UTI_TimespecToDouble(const struct timespec *ts); /* Convert a number of seconds expressed in floating point into a timespec */ @@ -58,7 +59,7 @@ extern void UTI_NormaliseTimespec(struct timespec *ts); /* Convert a timeval into a floating point number of seconds */ -extern double UTI_TimevalToDouble(struct timeval *tv); +extern double UTI_TimevalToDouble(const struct timeval *tv); /* Convert a number of seconds expressed in floating point into a timeval */ @@ -70,55 +71,61 @@ /* Returns -1 if a comes earlier than b, 0 if a is the same time as b, and +1 if a comes after b */ -extern int UTI_CompareTimespecs(struct timespec *a, struct timespec *b); +extern int UTI_CompareTimespecs(const struct timespec *a, const struct timespec *b); /* Calculate result = a - b */ -extern void UTI_DiffTimespecs(struct timespec *result, struct timespec *a, struct timespec *b); +extern void UTI_DiffTimespecs(struct timespec *result, + const struct timespec *a, const struct timespec *b); /* Calculate result = a - b and return as a double */ -extern double UTI_DiffTimespecsToDouble(struct timespec *a, struct timespec *b); +extern double UTI_DiffTimespecsToDouble(const struct timespec *a, const struct timespec *b); /* Add a double increment to a timespec to get a new one. 'start' is the starting time, 'end' is the result that we return. This is safe to use if start and end are the same */ -extern void UTI_AddDoubleToTimespec(struct timespec *start, double increment, struct timespec *end); +extern void UTI_AddDoubleToTimespec(const struct timespec *start, double increment, + struct timespec *end); /* Calculate the average and difference (as a double) of two timespecs */ -extern void UTI_AverageDiffTimespecs(struct timespec *earlier, struct timespec *later, struct timespec *average, double *diff); +extern void UTI_AverageDiffTimespecs(const struct timespec *earlier, const struct timespec *later, + struct timespec *average, double *diff); /* Calculate result = a - b + c */ -extern void UTI_AddDiffToTimespec(struct timespec *a, struct timespec *b, struct timespec *c, struct timespec *result); +extern void UTI_AddDiffToTimespec(const struct timespec *a, const struct timespec *b, + const struct timespec *c, struct timespec *result); /* Convert a timespec into a temporary string, largely for diagnostic display */ -extern char *UTI_TimespecToString(struct timespec *ts); +extern char *UTI_TimespecToString(const struct timespec *ts); /* Convert an NTP timestamp into a temporary string, largely for diagnostic display */ -extern char *UTI_Ntp64ToString(NTP_int64 *ts); +extern char *UTI_Ntp64ToString(const NTP_int64 *ts); /* Convert ref_id into a temporary string, for diagnostics */ extern char *UTI_RefidToString(uint32_t ref_id); /* Convert an IP address to string, for diagnostics */ -extern char *UTI_IPToString(IPAddr *ip); +extern char *UTI_IPToString(const IPAddr *ip); extern int UTI_StringToIP(const char *addr, IPAddr *ip); -extern uint32_t UTI_IPToRefid(IPAddr *ip); -extern uint32_t UTI_IPToHash(IPAddr *ip); -extern void UTI_IPHostToNetwork(IPAddr *src, IPAddr *dest); -extern void UTI_IPNetworkToHost(IPAddr *src, IPAddr *dest); -extern int UTI_CompareIPs(IPAddr *a, IPAddr *b, IPAddr *mask); - -extern void UTI_SockaddrToIPAndPort(struct sockaddr *sa, IPAddr *ip, unsigned short *port); -extern int UTI_IPAndPortToSockaddr(IPAddr *ip, unsigned short port, struct sockaddr *sa); -extern char *UTI_SockaddrToString(struct sockaddr *sa); -extern const char *UTI_SockaddrFamilyToString(int family); +extern int UTI_IsStringIP(const char *string); +extern int UTI_StringToIdIP(const char *addr, IPAddr *ip); +extern int UTI_IsIPReal(const IPAddr *ip); +extern uint32_t UTI_IPToRefid(const IPAddr *ip); +extern uint32_t UTI_IPToHash(const IPAddr *ip); +extern void UTI_IPHostToNetwork(const IPAddr *src, IPAddr *dest); +extern void UTI_IPNetworkToHost(const IPAddr *src, IPAddr *dest); +extern int UTI_CompareIPs(const IPAddr *a, const IPAddr *b, const IPAddr *mask); + +extern char *UTI_IPSockAddrToString(const IPSockAddr *sa); extern char *UTI_TimeToLogForm(time_t t); /* Adjust time following a frequency/offset change */ -extern void UTI_AdjustTimespec(struct timespec *old_ts, struct timespec *when, struct timespec *new_ts, double *delta_time, double dfreq, double doffset); +extern void UTI_AdjustTimespec(const struct timespec *old_ts, const struct timespec *when, + struct timespec *new_ts, double *delta_time, + double dfreq, double doffset); /* Get zero NTP timestamp with random bits below precision */ extern void UTI_GetNtp64Fuzz(NTP_int64 *ts, int precision); @@ -130,34 +137,39 @@ extern void UTI_ZeroNtp64(NTP_int64 *ts); /* Check if an NTP timestamp is zero */ -extern int UTI_IsZeroNtp64(NTP_int64 *ts); +extern int UTI_IsZeroNtp64(const NTP_int64 *ts); /* Compare two NTP timestamps. Returns -1 if a is before b, 0 if a is equal to b, and 1 if a is after b. */ -extern int UTI_CompareNtp64(NTP_int64 *a, NTP_int64 *b); +extern int UTI_CompareNtp64(const NTP_int64 *a, const NTP_int64 *b); /* Compare an NTP timestamp with up to three other timestamps. Returns 0 if a is not equal to any of b1, b2, and b3, 1 otherwise. */ -extern int UTI_IsEqualAnyNtp64(NTP_int64 *a, NTP_int64 *b1, NTP_int64 *b2, NTP_int64 *b3); +extern int UTI_IsEqualAnyNtp64(const NTP_int64 *a, const NTP_int64 *b1, + const NTP_int64 *b2, const NTP_int64 *b3); /* Convert a timespec into an NTP timestamp */ -extern void UTI_TimespecToNtp64(struct timespec *src, NTP_int64 *dest, NTP_int64 *fuzz); +extern void UTI_TimespecToNtp64(const struct timespec *src, NTP_int64 *dest, + const NTP_int64 *fuzz); /* Convert an NTP timestamp into a timespec */ -extern void UTI_Ntp64ToTimespec(NTP_int64 *src, struct timespec *dest); +extern void UTI_Ntp64ToTimespec(const NTP_int64 *src, struct timespec *dest); /* Check if time + offset is sane */ -extern int UTI_IsTimeOffsetSane(struct timespec *ts, double offset); +extern int UTI_IsTimeOffsetSane(const struct timespec *ts, double offset); /* Get 2 raised to power of a signed integer */ extern double UTI_Log2ToDouble(int l); -extern void UTI_TimespecNetworkToHost(Timespec *src, struct timespec *dest); -extern void UTI_TimespecHostToNetwork(struct timespec *src, Timespec *dest); +extern void UTI_TimespecNetworkToHost(const Timespec *src, struct timespec *dest); +extern void UTI_TimespecHostToNetwork(const struct timespec *src, Timespec *dest); extern double UTI_FloatNetworkToHost(Float x); extern Float UTI_FloatHostToNetwork(double x); +extern CMC_Algorithm UTI_CmacNameToAlgorithm(const char *name); +extern HSH_Algorithm UTI_HashNameToAlgorithm(const char *name); + /* Set FD_CLOEXEC on descriptor */ extern int UTI_FdSetCloexec(int fd); @@ -176,6 +188,25 @@ permissions and its uid/gid must match the specified values. */ extern int UTI_CheckDirPermissions(const char *path, mode_t perm, uid_t uid, gid_t gid); +/* Open a file. The full path of the file is constructed from the basedir + (may be NULL), '/' (if basedir is not NULL), name, and suffix (may be NULL). + Created files have specified permissions (umasked). Returns NULL on error. + The following modes are supported (if the mode is an uppercase character, + errors are fatal): + r/R - open an existing file for reading + w/W - open a new file for writing (remove existing file) + a/A - open an existing file for appending (create if does not exist) */ +extern FILE *UTI_OpenFile(const char *basedir, const char *name, const char *suffix, + char mode, mode_t perm); + +/* Rename a temporary file by changing its suffix. The paths are constructed as + in UTI_OpenFile(). If the renaming fails, the file will be removed. */ +extern int UTI_RenameTempFile(const char *basedir, const char *name, + const char *old_suffix, const char *new_suffix); + +/* Remove a file. The path is constructed as in UTI_OpenFile(). */ +extern int UTI_RemoveFile(const char *basedir, const char *name, const char *suffix); + /* Set process user/group IDs and drop supplementary groups */ extern void UTI_DropRoot(uid_t uid, gid_t gid); @@ -187,6 +218,18 @@ generating long-term keys */ extern void UTI_GetRandomBytes(void *buf, unsigned int len); +/* Print data in hexadecimal format */ +extern int UTI_BytesToHex(const void *buf, unsigned int buf_len, char *hex, unsigned int hex_len); + +/* Parse a string containing data in hexadecimal format. In-place conversion + is supported. */ +extern unsigned int UTI_HexToBytes(const char *hex, void *buf, unsigned int len); + +/* Split a string into words separated by whitespace characters. It returns + the number of words found in the string, but saves only up to the specified + number of pointers to the words. */ +extern int UTI_SplitString(char *string, char **words, int max_saved_words); + /* Macros to get maximum and minimum of two values */ #ifdef MAX #undef MAX diff -Nru chrony-3.5/version.txt chrony-4.1/version.txt --- chrony-3.5/version.txt 2019-05-14 10:59:59.000000000 +0000 +++ chrony-4.1/version.txt 2021-05-13 10:48:10.000000000 +0000 @@ -1 +1 @@ -3.5 +4.1