Compare commits

..

2 Commits

Author SHA1 Message Date
Miroslav Lichvar
04328ceead doc: update NEWS 2020-08-19 16:24:04 +02:00
Miroslav Lichvar
f00fed2009 main: create new file when writing pidfile
When writing the pidfile, open the file with the O_CREAT|O_EXCL flags
to avoid following a symlink and writing the PID to an unexpected file,
when chronyd still has the root privileges.

The Linux open(2) man page warns about O_EXCL not working as expected on
NFS versions before 3 and Linux versions before 2.6. Saving pidfiles on
a distributed filesystem like NFS is not generally expected, but if
there is a reason to do that, these old kernel and NFS versions are not
considered to be supported for saving files by chronyd.

This is a minimal backport specific to this issue of the following
commits:
- commit 2fc8edacb8 ("use PATH_MAX")
- commit f4c6a00b2a ("logging: call exit() in LOG_Message()")
- commit 7a4c396bba ("util: add functions for common file operations")
- commit e18903a6b5 ("switch to new util file functions")

Reported-by: Matthias Gerstner <mgerstner@suse.de>
2020-08-06 11:46:04 +02:00
125 changed files with 2599 additions and 12247 deletions

View File

@@ -21,42 +21,44 @@
#
# Makefile template
SYSCONFDIR = @SYSCONFDIR@
BINDIR = @BINDIR@
SBINDIR = @SBINDIR@
LOCALSTATEDIR = @LOCALSTATEDIR@
CHRONYVARDIR = @CHRONYVARDIR@
DESTDIR =
SYSCONFDIR=@SYSCONFDIR@
BINDIR=@BINDIR@
SBINDIR=@SBINDIR@
LOCALSTATEDIR=@LOCALSTATEDIR@
CHRONYVARDIR=@CHRONYVARDIR@
CC = @CC@
CFLAGS = @CFLAGS@
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
EXTRA_OBJS = @EXTRA_OBJS@
DESTDIR=
HASH_OBJ = @HASH_OBJ@
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \
reference.o regress.o rtc.o samplefilt.o sched.o socket.o sources.o sourcestats.o \
stubs.o smooth.o sys.o sys_null.o tempcomp.o util.o $(EXTRA_OBJS)
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)
EXTRA_CLI_OBJS = @EXTRA_CLI_OBJS@
EXTRA_OBJS=@EXTRA_OBJECTS@
CLI_OBJS = array.o client.o cmdparse.o getdate.o memory.o nameserv.o \
pktlength.o socket.o util.o $(EXTRA_CLI_OBJS)
pktlength.o util.o $(HASH_OBJ)
ALL_OBJS = $(OBJS) $(CLI_OBJS)
ALL_OBJS = $(OBJS) $(EXTRA_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)
$(CC) $(CFLAGS) -o chronyd $(OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS)
chronyd : $(OBJS) $(EXTRA_OBJS)
$(CC) $(CFLAGS) -o chronyd $(OBJS) $(EXTRA_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS)
chronyc : $(CLI_OBJS)
$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS)
@@ -68,7 +70,6 @@ distclean : clean
-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
@@ -120,7 +121,7 @@ check : chronyd chronyc
cd test/system && ./run
print-chronyd-objects :
@echo $(OBJS)
@echo $(OBJS) $(EXTRA_OBJS)
Makefile : Makefile.in configure
@echo

31
NEWS
View File

@@ -1,30 +1,9 @@
New in version 4.0
==================
New in version 3.5.1
====================
Enhancements
------------
* Add support for Network Time Security (NTS) authentication
* Add support for AES-CMAC keys (AES128, AES256) with Nettle
* Add support for maxsamples of 1 for faster update with -q/-Q option
* Add -L option to limit log messages by severity
* Avoid replacing NTP sources with sources that have unreachable address
* Improve pools to repeat name resolution to get "maxsources" sources
* Improve NTP loop test to prevent synchronisation to itself
* Update clock synchronisation status and leap status more frequently
* Update seccomp filter
* Add "add pool" command
* Add -N option and sourcename command to print original names of sources
* Add -a option to sources/sourcestats command to print unresolved sources
* Add reset command to drop all measurements
Bug fixes
---------
* Handle RTCs that don't support interrupts
* Respond to command requests with correct address on multihomed hosts
Removed features
----------------
* Drop support for RIPEMD keys (RMD128, RMD160, RMD256, RMD320)
Security fixes
--------------
* Create new file when writing pidfile (CVE-2020-14367)
New in version 3.5
==================

1
README
View File

@@ -112,7 +112,6 @@ Benny Lyne Amorsen <benny@amorsen.dk>
Andrew Bishop <amb@gedanken.demon.co.uk>
Vincent Blut <vincent.debian@free.fr>
Stephan I. Boettcher <stephan@nevis1.columbia.edu>
David Bohman <debohman@gmail.com>
Goswin Brederlow <brederlo@informatik.uni-tuebingen.de>
Leigh Brown <leigh@solinno.co.uk>
Erik Bryer <ebryer@spots.ab.ca>

View File

@@ -30,19 +30,16 @@
#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;
@@ -50,10 +47,8 @@ typedef struct {
typedef struct {
IPAddr ip_addr;
uint16_t port;
} IPSockAddr;
typedef IPSockAddr NTP_Remote_Address;
unsigned short port;
} NTP_Remote_Address;
#define INVALID_IF_INDEX -1

View File

@@ -247,8 +247,6 @@ set_subnet_(ADF_AuthTable table,
set_subnet(&table->base6, ip6, 4, 0, new_state, delete_children) == ADF_SUCCESS)
return ADF_SUCCESS;
break;
default:
break;
}
return ADF_BADSUBNET;
@@ -361,9 +359,9 @@ ADF_IsAllowed(ADF_AuthTable table,
case IPADDR_INET6:
split_ip6(ip_addr, ip6);
return check_ip_in_node(&table->base6, ip6);
default:
return 0;
}
return 0;
}
/* ================================================== */

31
candm.h
View File

@@ -101,10 +101,7 @@
#define REQ_ADD_PEER3 61
#define REQ_SHUTDOWN 62
#define REQ_ONOFFLINE 63
#define REQ_ADD_SOURCE 64
#define REQ_NTP_SOURCE_NAME 65
#define REQ_RESET 66
#define N_REQUEST_TYPES 67
#define N_REQUEST_TYPES 64
/* Structure used to exchange timespecs independent of time_t size */
typedef struct {
@@ -248,11 +245,6 @@ typedef struct {
int32_t EOR;
} REQ_Ac_Check;
/* Source types in NTP source requests */
#define REQ_ADDSRC_SERVER 1
#define REQ_ADDSRC_PEER 2
#define REQ_ADDSRC_POOL 3
/* Flags used in NTP source requests */
#define REQ_ADDSRC_ONLINE 0x1
#define REQ_ADDSRC_AUTOOFFLINE 0x2
@@ -263,11 +255,9 @@ typedef struct {
#define REQ_ADDSRC_REQUIRE 0x40
#define REQ_ADDSRC_INTERLEAVED 0x80
#define REQ_ADDSRC_BURST 0x100
#define REQ_ADDSRC_NTS 0x200
typedef struct {
uint32_t type;
int8_t name[256];
IPAddr ip_addr;
uint32_t port;
int32_t minpoll;
int32_t maxpoll;
@@ -279,7 +269,6 @@ typedef struct {
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;
@@ -346,11 +335,6 @@ typedef struct {
int32_t EOR;
} REQ_NTPData;
typedef struct {
IPAddr ip_addr;
int32_t EOR;
} REQ_NTPSourceName;
/* ================================================== */
#define PKT_TYPE_CMD_REQUEST 1
@@ -453,7 +437,6 @@ typedef struct {
REQ_ReselectDistance reselect_distance;
REQ_SmoothTime smoothtime;
REQ_NTPData ntp_data;
REQ_NTPData ntp_source_name;
} data; /* Command specific parameters */
/* Padding used to prevent traffic amplification. It only defines the
@@ -490,8 +473,7 @@ typedef struct {
#define RPY_NTP_DATA 16
#define RPY_MANUAL_TIMESTAMP2 17
#define RPY_MANUAL_LIST2 18
#define RPY_NTP_SOURCE_NAME 19
#define N_REPLY_TYPES 20
#define N_REPLY_TYPES 19
/* Status codes */
#define STT_SUCCESS 0
@@ -515,7 +497,6 @@ typedef struct {
#define STT_INVALIDAF 17
#define STT_BADPKTVERSION 18
#define STT_BADPKTLENGTH 19
#define STT_INVALIDNAME 21
typedef struct {
int32_t EOR;
@@ -707,11 +688,6 @@ typedef struct {
int32_t EOR;
} RPY_NTPData;
typedef struct {
int8_t name[256];
int32_t EOR;
} RPY_NTPSourceName;
typedef struct {
uint8_t version;
uint8_t pkt_type;
@@ -741,7 +717,6 @@ typedef struct {
RPY_Activity activity;
RPY_Smoothing smoothing;
RPY_NTPData ntp_data;
RPY_NTPSourceName ntp_source_name;
} data; /* Reply specific parameters */
} CMD_Reply;

446
client.c
View File

@@ -33,14 +33,12 @@
#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
@@ -54,15 +52,16 @@
/* ================================================== */
struct Address {
SCK_AddressType type;
union {
IPSockAddr ip;
char *path;
} addr;
union sockaddr_all {
struct sockaddr_in in4;
#ifdef FEAT_IPV6
struct sockaddr_in6 in6;
#endif
struct sockaddr_un un;
struct sockaddr sa;
};
static ARR_Instance server_addresses;
static ARR_Instance sockaddrs;
static int sock_fd = -1;
@@ -72,15 +71,13 @@ 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. */
LOG_Severity log_min_severity = LOGS_INFO;
int log_debug_enabled = 0;
void LOG_Message(LOG_Severity severity,
#if DEBUG > 0
@@ -90,9 +87,6 @@ void LOG_Message(LOG_Severity severity,
{
va_list ap;
if (severity < log_min_severity)
return;
va_start(ap, format);
vfprintf(stderr, format, ap);
putc('\n', stderr);
@@ -151,15 +145,15 @@ read_line(void)
/* ================================================== */
static ARR_Instance
get_addresses(const char *hostnames, int port)
get_sockaddrs(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 (*addr));
addrs = ARR_CreateInstance(sizeof (union sockaddr_all));
s1 = Strdup(hostnames);
/* Parse the comma-separated list of hostnames */
@@ -170,9 +164,11 @@ get_addresses(const char *hostnames, int port)
/* hostname starting with / is considered a path of Unix domain socket */
if (hostname[0] == '/') {
addr = ARR_GetNewElement(addrs);
addr->type = SCK_ADDR_UNIX;
addr->addr.path = Strdup(hostname);
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;
} else {
if (DNS_Name2IPAddress(hostname, ip_addrs, DNS_MAX_ADDRESSES) != DNS_Success) {
DEBUG_LOG("Could not get IP address for %s", hostname);
@@ -180,10 +176,8 @@ get_addresses(const char *hostnames, int port)
}
for (i = 0; i < DNS_MAX_ADDRESSES && ip_addrs[i].family != IPADDR_UNSPEC; i++) {
addr = ARR_GetNewElement(addrs);
addr->type = SCK_ADDR_IP;
addr->addr.ip.ip_addr = ip_addrs[i];
addr->addr.ip.port = port;
addr = (union sockaddr_all *)ARR_GetNewElement(addrs);
UTI_IPAndPortToSockaddr(&ip_addrs[i], port, (struct sockaddr *)addr);
DEBUG_LOG("Resolved %s to %s", hostname, UTI_IPToString(&ip_addrs[i]));
}
}
@@ -193,60 +187,70 @@ get_addresses(const char *hostnames, int port)
return addrs;
}
/* ================================================== */
static void
free_addresses(ARR_Instance addresses)
{
struct Address *addr;
unsigned int i;
for (i = 0; i < ARR_GetSize(addresses); i++) {
addr = ARR_GetElement(addresses, i);
if (addr->type == SCK_ADDR_UNIX)
Free(addr->addr.path);
}
ARR_DestroyInstance(addresses);
}
/* ================================================== */
/* Initialise the socket used to talk to the daemon */
static int
open_socket(struct Address *addr)
prepare_socket(union sockaddr_all *addr)
{
char *dir, *local_addr;
size_t local_addr_len;
socklen_t addr_len;
char *dir;
switch (addr->type) {
case SCK_ADDR_IP:
sock_fd = SCK_OpenUdpSocket(&addr->addr.ip, NULL, 0);
switch (addr->sa.sa_family) {
case AF_UNIX:
addr_len = sizeof (addr->un);
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);
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);
}
if (sock_fd < 0)
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->sa.sa_family == AF_UNIX) {
struct sockaddr_un sa_un;
/* 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);
sa_un.sun_family = AF_UNIX;
unlink(sa_un.sun_path);
/* 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;
}
/* 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;
}
}
if (connect(sock_fd, &addr->sa, addr_len) < 0) {
DEBUG_LOG("Could not connect socket : %s", strerror(errno));
return 0;
}
return 1;
}
@@ -256,11 +260,20 @@ open_socket(struct Address *addr)
static void
close_io(void)
{
union sockaddr_all addr;
socklen_t addr_len = sizeof (addr);
if (sock_fd < 0)
return;
SCK_RemoveSocket(sock_fd);
SCK_CloseSocket(sock_fd);
/* 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);
sock_fd = -1;
}
@@ -270,7 +283,7 @@ static int
open_io(void)
{
static unsigned int address_index = 0;
struct Address *addr;
union sockaddr_all *addr;
/* If a socket is already opened, close it and try the next address */
if (sock_fd >= 0) {
@@ -279,10 +292,11 @@ open_io(void)
}
/* Find an address for which a socket can be opened and connected */
for (; address_index < ARR_GetSize(server_addresses); address_index++) {
addr = ARR_GetElement(server_addresses, address_index);
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));
if (open_socket(addr))
if (prepare_socket(addr))
return 1;
close_io();
@@ -320,9 +334,6 @@ bits_to_mask(int bits, int family, IPAddr *mask)
for (; i < 16; i++)
mask->addr.in6[i] = 0x0;
break;
case IPADDR_ID:
mask->family = IPADDR_UNSPEC;
break;
default:
assert(0);
}
@@ -330,20 +341,6 @@ bits_to_mask(int bits, int family, IPAddr *mask)
/* ================================================== */
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)
{
@@ -370,7 +367,7 @@ read_mask_address(char *line, IPAddr *mask, IPAddr *address)
}
}
} else {
if (parse_source_address(p, address)) {
if (DNS_Name2IPAddress(p, address, 1) == DNS_Success) {
bits_to_mask(-1, address->family, mask);
return 1;
} else {
@@ -450,7 +447,7 @@ read_address_integer(char *line, IPAddr *address, int *value)
LOG(LOGS_ERR, "Invalid syntax for address value");
ok = 0;
} else {
if (!parse_source_address(hostname, address)) {
if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) {
LOG(LOGS_ERR, "Could not get address for hostname");
ok = 0;
} else {
@@ -478,7 +475,7 @@ read_address_double(char *line, IPAddr *address, double *value)
LOG(LOGS_ERR, "Invalid syntax for address value");
ok = 0;
} else {
if (!parse_source_address(hostname, address)) {
if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) {
LOG(LOGS_ERR, "Could not get address for hostname");
ok = 0;
} else {
@@ -1072,39 +1069,20 @@ process_cmd_doffset(CMD_Request *msg, char *line)
/* ================================================== */
static int
process_cmd_add_source(CMD_Request *msg, char *line)
process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
{
CPS_NTP_Source data;
IPAddr ip_addr;
int result = 0, status, type;
const char *opt_name, *word;
int result = 0, status;
const char *opt_name;
msg->command = htons(REQ_ADD_SOURCE);
word = line;
line = CPS_SplitWord(line);
if (!strcmp(word, "server")) {
type = REQ_ADDSRC_SERVER;
} else if (!strcmp(word, "peer")) {
type = REQ_ADDSRC_PEER;
} else if (!strcmp(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:
/* 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) {
if (DNS_Name2IPAddress(data.name, &ip_addr, 1) != DNS_Success) {
LOG(LOGS_ERR, "Invalid host/IP address");
break;
}
@@ -1115,12 +1093,8 @@ process_cmd_add_source(CMD_Request *msg, char *line)
break;
}
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((unsigned long) data.port);
UTI_IPHostToNetwork(&ip_addr, &msg->data.ntp_source.ip_addr);
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);
@@ -1131,7 +1105,6 @@ process_cmd_add_source(CMD_Request *msg, char *line)
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 =
@@ -1145,7 +1118,6 @@ process_cmd_add_source(CMD_Request *msg, char *line)
(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.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) |
@@ -1163,6 +1135,24 @@ process_cmd_add_source(CMD_Request *msg, char *line)
/* ================================================== */
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)
{
@@ -1178,7 +1168,7 @@ process_cmd_delete(CMD_Request *msg, char *line)
LOG(LOGS_ERR, "Invalid syntax for address");
ok = 0;
} else {
if (!parse_source_address(hostname, &address)) {
if (DNS_Name2IPAddress(hostname, &address, 1) != DNS_Success) {
LOG(LOGS_ERR, "Could not get address for hostname");
ok = 0;
} else {
@@ -1207,17 +1197,16 @@ give_help(void)
"Wait until synchronised in specified limits\0"
"\0\0"
"Time sources:\0\0"
"sources [-a] [-v]\0Display information about current sources\0"
"sourcestats [-a] [-v]\0Display statistics about collected measurements\0"
"sources [-v]\0Display information about current sources\0"
"sourcestats [-v]\0Display statistics about collected measurements\0"
"reselect\0Force reselecting synchronisation source\0"
"reselectdist <dist>\0Modify reselection distance\0"
"\0\0"
"NTP sources:\0\0"
"activity\0Check how many NTP sources are online/offline\0"
"ntpdata [<address>]\0Display information about last valid measurement\0"
"add server <name> [options]\0Add new NTP server\0"
"add pool <name> [options]\0Add new pool of NTP servers\0"
"add peer <name> [options]\0Add new NTP peer\0"
"add server <address> [options]\0Add new NTP server\0"
"add peer <address> [options]\0Add new NTP peer\0"
"delete <address>\0Remove server or peer\0"
"burst <n-good>/<n-max> [<mask>/<address>]\0Start rapid set of measurements\0"
"maxdelay <address> <delay>\0Modify maximum valid sample delay\0"
@@ -1232,7 +1221,6 @@ give_help(void)
"\0according to network configuration\0"
"polltarget <address> <target>\0Modify poll target\0"
"refresh\0Refresh IP addresses\0"
"sourcename <address>\0Display original name\0"
"\0\0"
"Manual time input:\0\0"
"manual off|on|reset\0Disable/enable/reset settime command\0"
@@ -1267,9 +1255,8 @@ give_help(void)
"\0\0"
"Other daemon commands:\0\0"
"cyclelogs\0Close and re-open log files\0"
"dump\0Dump measurements and NTS keys/cookies\0"
"rekey\0Re-read keys\0"
"reset\0Drop all measurements\0"
"dump\0Dump all measurements to save files\0"
"rekey\0Re-read keys from key file\0"
"shutdown\0Stop daemon\0"
"\0\0"
"Client commands:\0\0"
@@ -1316,16 +1303,16 @@ command_name_generator(const char *text, int state)
"deny", "dns", "dump", "exit", "help", "keygen", "local", "makestep",
"manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
"maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline",
"polltarget", "quit", "refresh", "rekey", "reselect", "reselectdist", "reset",
"polltarget", "quit", "refresh", "rekey", "reselect", "reselectdist",
"retries", "rtcdata", "serverstats", "settime", "shutdown", "smoothing",
"smoothtime", "sourcename", "sources", "sourcestats",
"smoothtime", "sources", "sourcestats",
"timeout", "tracking", "trimrtc", "waitsync", "writertc",
NULL
};
const char *add_options[] = { "peer", "pool", "server", NULL };
const char *add_options[] = { "peer", "server", NULL };
const char *manual_options[] = { "on", "off", "delete", "list", "reset", NULL };
const char *sources_options[] = { "-a", "-v", NULL };
const char *sourcestats_options[] = { "-a", "-v", NULL };
const char *sources_options[] = { "-v", NULL };
const char *sourcestats_options[] = { "-v", NULL };
static int list_index, len;
names[TAB_COMPLETE_BASE_CMDS] = base_commands;
@@ -1439,8 +1426,12 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
return 0;
}
if (SCK_Send(sock_fd, (void *)request, command_length, 0) < 0)
if (send(sock_fd, (void *)request, command_length, 0) < 0) {
DEBUG_LOG("Could not send %d bytes : %s", command_length, strerror(errno));
return 0;
}
DEBUG_LOG("Sent %d bytes", command_length);
}
UTI_TimevalToTimespec(&tv, &ts_now);
@@ -1476,11 +1467,16 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
/* Timeout must have elapsed, try a resend? */
new_attempt = 1;
} else {
recv_status = SCK_Receive(sock_fd, reply, sizeof (*reply), 0);
recv_status = recv(sock_fd, (void *)reply, sizeof(CMD_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 */
@@ -1611,9 +1607,6 @@ request_reply(CMD_Request *request, CMD_Reply *reply, int requested_reply, int v
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");
}
@@ -2045,40 +2038,12 @@ print_info_field(const char *format, ...)
/* ================================================== */
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", 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(buf[i]))
return 0;
}
return 1;
}
/* ================================================== */
static void
format_name(char *buf, int size, int trunc_dns, int ref, uint32_t ref_id,
int source, IPAddr *ip_addr)
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 {
@@ -2092,42 +2057,12 @@ format_name(char *buf, int size, int trunc_dns, int ref, uint32_t ref_id,
/* ================================================== */
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
process_cmd_sourcename(char *line)
check_for_verbose_flag(char *line)
{
IPAddr ip_addr;
char name[256];
if (!UTI_StringToIP(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;
if (!csv_mode && !strcmp(line, "-v"))
return 1;
return 0;
}
/* ================================================== */
@@ -2139,16 +2074,18 @@ process_cmd_sources(char *line)
CMD_Reply reply;
IPAddr ip_addr;
uint32_t i, mode, n_sources;
char name[256], mode_ch, state_ch;
int all, verbose;
char name[50], mode_ch, state_ch;
int verbose;
parse_sources_options(line, &all, &verbose);
/* Check whether to output verbose headers */
verbose = check_for_verbose_flag(line);
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");
@@ -2174,12 +2111,9 @@ process_cmd_sources(char *line)
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, 1, &ip_addr);
ip_addr.addr.in4, &ip_addr);
switch (mode) {
case RPY_SD_MD_CLIENT:
@@ -2246,17 +2180,18 @@ process_cmd_sourcestats(char *line)
CMD_Request request;
CMD_Reply reply;
uint32_t i, n_sources;
int all, verbose;
char name[256];
int verbose = 0;
char name[50];
IPAddr ip_addr;
parse_sources_options(line, &all, &verbose);
verbose = check_for_verbose_flag(line);
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");
@@ -2281,11 +2216,8 @@ process_cmd_sourcestats(char *line)
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), 1, &ip_addr);
ntohl(reply.data.sourcestats.ref_id), &ip_addr);
print_report("%-25s %3U %3U %I %+P %P %+S %S\n",
name,
@@ -2311,7 +2243,7 @@ process_cmd_tracking(char *line)
CMD_Reply reply;
IPAddr ip_addr;
uint32_t ref_id;
char name[256];
char name[50];
struct timespec ref_time;
request.command = htons(REQ_TRACKING);
@@ -2322,7 +2254,7 @@ process_cmd_tracking(char *line)
UTI_IPNetworkToHost(&reply.data.tracking.ip_addr, &ip_addr);
format_name(name, sizeof (name), sizeof (name),
ip_addr.family == IPADDR_UNSPEC, ref_id, 1, &ip_addr);
ip_addr.family == IPADDR_UNSPEC, ref_id, &ip_addr);
UTI_TimespecNetworkToHost(&reply.data.tracking.ref_time, &ref_time);
@@ -2382,7 +2314,7 @@ process_cmd_ntpdata(char *line)
for (i = 0; i < n_sources; i++) {
if (specified_addr) {
if (!parse_source_address(line, &remote_addr)) {
if (DNS_Name2IPAddress(line, &remote_addr, 1) != DNS_Success) {
LOG(LOGS_ERR, "Could not get address for hostname");
return 0;
}
@@ -2397,8 +2329,6 @@ process_cmd_ntpdata(char *line)
continue;
UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &remote_addr);
if (!UTI_IsIPReal(&remote_addr))
continue;
}
request.command = htons(REQ_NTP_DATA);
@@ -2620,7 +2550,7 @@ process_cmd_clients(char *line)
if (ip.family == IPADDR_UNSPEC)
continue;
format_name(name, sizeof (name), 25, 0, 0, 0, &ip);
format_name(name, sizeof (name), 25, 0, 0, &ip);
print_report("%-25s %6U %5U %C %C %I %6U %5U %C %I\n",
name,
@@ -2836,14 +2766,6 @@ process_cmd_shutdown(CMD_Request *msg, char *line)
/* ================================================== */
static void
process_cmd_reset(CMD_Request *msg, char *line)
{
msg->command = htons(REQ_RESET);
}
/* ================================================== */
static int
process_cmd_waitsync(char *line)
{
@@ -2959,39 +2881,28 @@ process_cmd_retries(const char *line)
static int
process_cmd_keygen(char *line)
{
char hash_name[17];
unsigned char key[512];
char type[17];
unsigned int i, cmac_length, length, id = 1, bits = 160;
unsigned int i, length, id = 1, bits = 160;
#ifdef FEAT_SECHASH
snprintf(type, sizeof (type), "SHA1");
snprintf(hash_name, sizeof (hash_name), "SHA1");
#else
snprintf(type, sizeof (type), "MD5");
snprintf(hash_name, sizeof (hash_name), "MD5");
#endif
if (sscanf(line, "%u %16s %u", &id, type, &bits))
if (sscanf(line, "%u %16s %u", &id, hash_name, &bits))
;
#ifdef HAVE_CMAC
cmac_length = CMC_GetKeyLength(type);
#else
cmac_length = 0;
#endif
if (HSH_GetHashId(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);
length = CLAMP(10, (bits + 7) / 8, sizeof (key));
if (HSH_GetHashId(hash_name) < 0) {
LOG(LOGS_ERR, "Unknown hash function %s", hash_name);
return 0;
}
length = CLAMP(10, length, sizeof (key));
UTI_GetRandomBytesUrandom(key, length);
printf("%u %s HEX:", id, type);
printf("%u %s HEX:", id, hash_name);
for (i = 0; i < length; i++)
printf("%02hhX", key[i]);
printf("\n");
@@ -3030,8 +2941,10 @@ process_line(char *line)
} else if (!strcmp(command, "activity")) {
do_normal_submit = 0;
ret = process_cmd_activity(line);
} else if (!strcmp(command, "add")) {
do_normal_submit = process_cmd_add_source(&tx_message, 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, "allow")) {
if (!strncmp(line, "all", 3)) {
do_normal_submit = process_cmd_allowall(&tx_message, CPS_SplitWord(line));
@@ -3138,8 +3051,6 @@ process_line(char *line)
process_cmd_reselect(&tx_message, line);
} else if (!strcmp(command, "reselectdist")) {
do_normal_submit = process_cmd_reselectdist(&tx_message, line);
} else if (!strcmp(command, "reset")) {
process_cmd_reset(&tx_message, line);
} else if (!strcmp(command, "retries")) {
ret = process_cmd_retries(line);
do_normal_submit = 0;
@@ -3159,9 +3070,6 @@ process_line(char *line)
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);
@@ -3263,7 +3171,7 @@ display_gpl(void)
static void
print_help(const char *progname)
{
printf("Usage: %s [-h HOST] [-p PORT] [-n] [-N] [-c] [-d] [-4|-6] [-m] [COMMAND]\n",
printf("Usage: %s [-h HOST] [-p PORT] [-n] [-c] [-d] [-4|-6] [-m] [COMMAND]\n",
progname);
}
@@ -3300,7 +3208,7 @@ main(int argc, char **argv)
optind = 1;
/* Parse short command-line options */
while ((opt = getopt(argc, argv, "+46acdf:h:mnNp:v")) != -1) {
while ((opt = getopt(argc, argv, "+46acdf:h:mnp:v")) != -1) {
switch (opt) {
case '4':
case '6':
@@ -3314,9 +3222,7 @@ main(int argc, char **argv)
csv_mode = 1;
break;
case 'd':
#if DEBUG > 0
log_min_severity = LOGS_DEBUG;
#endif
log_debug_enabled = 1;
break;
case 'h':
hostnames = optarg;
@@ -3327,9 +3233,6 @@ main(int argc, char **argv)
case 'n':
no_dns = 1;
break;
case 'N':
source_names = 1;
break;
case 'p':
port = atoi(optarg);
break;
@@ -3358,8 +3261,7 @@ main(int argc, char **argv)
UTI_SetQuitSignalsHandler(signal_handler, 0);
SCK_Initialise();
server_addresses = get_addresses(hostnames, port);
sockaddrs = get_sockaddrs(hostnames, port);
if (!open_io())
LOG_FATAL("Could not open connection to daemon");
@@ -3379,8 +3281,8 @@ main(int argc, char **argv)
}
close_io();
free_addresses(server_addresses);
SCK_Finalise();
ARR_DestroyInstance(sockaddrs);
return !ret;
}

41
cmac.h
View File

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

View File

@@ -1,115 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2019
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Support for AES128 and AES256 CMAC in Nettle.
*/
#include "config.h"
#include "sysincl.h"
#include <nettle/cmac.h>
#include "cmac.h"
#include "memory.h"
struct CMC_Instance_Record {
int key_length;
union {
struct cmac_aes128_ctx aes128;
struct cmac_aes256_ctx aes256;
} context;
};
/* ================================================== */
unsigned int
CMC_GetKeyLength(const char *cipher)
{
if (strcmp(cipher, "AES128") == 0)
return AES128_KEY_SIZE;
else if (strcmp(cipher, "AES256") == 0)
return AES256_KEY_SIZE;
return 0;
}
/* ================================================== */
CMC_Instance
CMC_CreateInstance(const char *cipher, const unsigned char *key, unsigned int length)
{
CMC_Instance inst;
if (length == 0 || length != CMC_GetKeyLength(cipher))
return NULL;
inst = MallocNew(struct CMC_Instance_Record);
inst->key_length = length;
switch (length) {
case AES128_KEY_SIZE:
cmac_aes128_set_key(&inst->context.aes128, key);
break;
case AES256_KEY_SIZE:
cmac_aes256_set_key(&inst->context.aes256, key);
break;
default:
assert(0);
}
return inst;
}
/* ================================================== */
unsigned int
CMC_Hash(CMC_Instance inst, const unsigned char *in, unsigned int in_len,
unsigned char *out, unsigned int out_len)
{
if (out_len > CMAC128_DIGEST_SIZE)
out_len = CMAC128_DIGEST_SIZE;
switch (inst->key_length) {
case AES128_KEY_SIZE:
cmac_aes128_update(&inst->context.aes128, in_len, in);
cmac_aes128_digest(&inst->context.aes128, out_len, out);
break;
case AES256_KEY_SIZE:
cmac_aes256_update(&inst->context.aes256, in_len, in);
cmac_aes256_digest(&inst->context.aes256, out_len, out);
break;
default:
assert(0);
}
return out_len;
}
/* ================================================== */
void
CMC_DestroyInstance(CMC_Instance inst)
{
Free(inst);
}

448
cmdmon.c
View File

@@ -38,13 +38,11 @@
#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"
@@ -55,12 +53,21 @@
/* ================================================== */
#define INVALID_SOCK_FD (-5)
union sockaddr_all {
struct sockaddr_in in4;
#ifdef FEAT_IPV6
struct sockaddr_in6 in6;
#endif
struct sockaddr_un un;
struct sockaddr sa;
};
/* 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 whether this module has been initialised or not */
static int initialised = 0;
@@ -133,9 +140,6 @@ static const char permissions[] = {
PERMIT_AUTH, /* ADD_PEER3 */
PERMIT_AUTH, /* SHUTDOWN */
PERMIT_AUTH, /* ONOFFLINE */
PERMIT_AUTH, /* ADD_SOURCE */
PERMIT_OPEN, /* NTP_SOURCE_NAME */
PERMIT_AUTH, /* RESET */
};
/* ================================================== */
@@ -151,46 +155,99 @@ static void read_from_cmd_socket(int sock_fd, int event, void *anything);
/* ================================================== */
static int
open_socket(int family)
prepare_socket(int family, int port_number)
{
IPSockAddr local_addr;
const char *local_path;
int sock_fd, port;
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
#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");
}
#endif
}
#endif
}
memset(&my_addr, 0, sizeof (my_addr));
switch (family) {
case IPADDR_INET4:
case IPADDR_INET6:
port = CNF_GetCommandPort();
if (port == 0 || !SCK_IsFamilySupported(family))
return INVALID_SOCK_FD;
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(family, &local_addr.ip_addr);
if (local_addr.ip_addr.family != family)
SCK_GetLoopbackIPAddress(family, &local_addr.ip_addr);
local_addr.port = port;
sock_fd = SCK_OpenUdpSocket(NULL, &local_addr, 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;
}
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;
case IPADDR_UNSPEC:
local_path = CNF_GetBindCommandPath();
#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);
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;
}
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);
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);
@@ -235,22 +292,37 @@ do_size_checks(void)
void
CAM_Initialise(int family)
{
int port_number;
assert(!initialised);
assert(sizeof (permissions) / sizeof (permissions[0]) == N_REQUEST_TYPES);
do_size_checks();
initialised = 1;
sock_fdu = INVALID_SOCK_FD;
sock_fd4 = INVALID_SOCK_FD;
sock_fd6 = INVALID_SOCK_FD;
sock_fdu = -1;
port_number = CNF_GetCommandPort();
if (family == IPADDR_UNSPEC || family == IPADDR_INET4)
sock_fd4 = open_socket(IPADDR_INET4);
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
if (family == IPADDR_UNSPEC || family == IPADDR_INET6)
sock_fd6 = open_socket(IPADDR_INET6);
if (port_number && sock_fd4 < 0
#ifdef FEAT_IPV6
&& sock_fd6 < 0
#endif
) {
LOG_FATAL("Could not open any command socket");
}
access_auth_table = ADF_CreateTable();
}
/* ================================================== */
@@ -258,24 +330,24 @@ CAM_Initialise(int family)
void
CAM_Finalise(void)
{
if (sock_fdu != INVALID_SOCK_FD) {
if (sock_fdu >= 0) {
SCH_RemoveFileHandler(sock_fdu);
SCK_RemoveSocket(sock_fdu);
SCK_CloseSocket(sock_fdu);
sock_fdu = INVALID_SOCK_FD;
close(sock_fdu);
unlink(CNF_GetBindCommandPath());
}
if (sock_fd4 != INVALID_SOCK_FD) {
sock_fdu = -1;
if (sock_fd4 >= 0) {
SCH_RemoveFileHandler(sock_fd4);
SCK_CloseSocket(sock_fd4);
sock_fd4 = INVALID_SOCK_FD;
close(sock_fd4);
}
if (sock_fd6 != INVALID_SOCK_FD) {
sock_fd4 = -1;
#ifdef FEAT_IPV6
if (sock_fd6 >= 0) {
SCH_RemoveFileHandler(sock_fd6);
SCK_CloseSocket(sock_fd6);
sock_fd6 = INVALID_SOCK_FD;
close(sock_fd6);
}
sock_fd6 = -1;
#endif
ADF_DestroyTable(access_auth_table);
@@ -290,18 +362,50 @@ CAM_OpenUnixSocket(void)
/* This is separated from CAM_Initialise() as it needs to be called when
the process has already dropped the root privileges */
if (CNF_GetBindCommandPath()[0])
sock_fdu = open_socket(IPADDR_UNSPEC);
sock_fdu = prepare_socket(AF_UNIX, 0);
}
/* ================================================== */
static void
transmit_reply(int sock_fd, SCK_Message *message)
transmit_reply(CMD_Reply *msg, union sockaddr_all *where_to)
{
message->length = PKL_ReplyLength((CMD_Reply *)message->data);
int status;
int tx_message_length;
int sock_fd;
socklen_t addrlen;
if (!SCK_SendMessage(sock_fd, message, 0))
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);
}
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));
return;
}
DEBUG_LOG("Sent %d bytes to %s fd %d", status,
UTI_SockaddrToString(&where_to->sa), sock_fd);
}
/* ================================================== */
@@ -310,8 +414,6 @@ static void
handle_dump(CMD_Request *rx_message, CMD_Reply *tx_message)
{
SRC_DumpSources();
NSR_DumpAuthData();
NKS_DumpKeys();
}
/* ================================================== */
@@ -620,7 +722,6 @@ static void
handle_rekey(CMD_Request *rx_message, CMD_Reply *tx_message)
{
KEY_Reload();
NKS_ReloadKeys();
}
/* ================================================== */
@@ -682,41 +783,14 @@ handle_cmdaccheck(CMD_Request *rx_message, CMD_Reply *tx_message)
/* ================================================== */
static void
handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_message)
{
NTP_Source_Type type;
NTP_Remote_Address rem_addr;
SourceParameters params;
NSR_Status status;
char *name;
int pool, 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 = (unsigned short)(ntohl(rx_message->data.ntp_source.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));
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);
@@ -728,7 +802,6 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
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.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);
@@ -744,31 +817,25 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
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.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_AddSourceByName(name, port, pool, type, &params);
status = NSR_AddSource(&rem_addr, type, &params);
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_InvalidName:
tx_message->status = htons(STT_INVALIDNAME);
break;
case NSR_InvalidAF:
tx_message->status = htons(STT_INVALIDAF);
break;
case NSR_NoSuchSource:
assert(0);
break;
@@ -796,8 +863,6 @@ handle_del_source(CMD_Request *rx_message, CMD_Reply *tx_message)
case NSR_TooManySources:
case NSR_AlreadyInUse:
case NSR_InvalidAF:
case NSR_InvalidName:
case NSR_UnresolvedName:
assert(0);
break;
}
@@ -1196,130 +1261,98 @@ handle_shutdown(CMD_Request *rx_message, CMD_Reply *tx_message)
SCH_QuitProgram();
}
/* ================================================== */
static void
handle_ntp_source_name(CMD_Request *rx_message, CMD_Reply *tx_message)
{
IPAddr addr;
char *name;
UTI_IPNetworkToHost(&rx_message->data.ntp_data.ip_addr, &addr);
name = NSR_GetName(&addr);
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_reset(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);
}
/* ================================================== */
/* 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 status, read_length, expected_length, rx_message_length;
int localhost, allowed, log_index;
unsigned short rx_command;
union sockaddr_all where_from;
socklen_t from_length;
IPAddr remote_ip;
unsigned short remote_port, rx_command;
struct timespec now, cooked_now;
sck_message = SCK_ReceiveMessage(sock_fd, 0);
if (!sck_message)
return;
rx_message_length = sizeof(rx_message);
from_length = sizeof(where_from);
read_length = sck_message->length;
status = recvfrom(sock_fd, (char *)&rx_message, rx_message_length, 0,
&where_from.sa, &from_length);
if (status < 0) {
LOG(LOGS_WARN, "Error [%s] reading from control socket %d",
strerror(errno), sock_fd);
return;
}
if (from_length > sizeof (where_from) ||
from_length <= sizeof (where_from.sa.sa_family)) {
DEBUG_LOG("Read command packet without source address");
return;
}
read_length = status;
/* Get current time cheaply */
SCH_GetLastEventTime(&cooked_now, NULL, &now);
/* 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);
UTI_SockaddrToIPAndPort(&where_from.sa, &remote_ip, &remote_port);
/* 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;
case SCK_ADDR_UNIX:
#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;
assert(sock_fd == sock_fdu);
remote_ip.family = IPADDR_UNSPEC;
localhost = 1;
break;
default:
DEBUG_LOG("Unexpected address type");
return;
assert(0);
}
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! */
return;
}
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;
}
memcpy(&rx_message, sck_message->data, read_length);
if (rx_message.pkt_type != PKT_TYPE_CMD_REQUEST ||
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_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;
}
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;
@@ -1334,7 +1367,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) {
tx_message.status = htons(STT_BADPKTVERSION);
transmit_reply(sock_fd, sck_message);
transmit_reply(&tx_message, &where_from);
}
return;
}
@@ -1344,7 +1377,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
DEBUG_LOG("Command packet has invalid command %d", rx_command);
tx_message.status = htons(STT_INVALID);
transmit_reply(sock_fd, sck_message);
transmit_reply(&tx_message, &where_from);
return;
}
@@ -1353,12 +1386,21 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
expected_length);
tx_message.status = htons(STT_BADPKTLENGTH);
transmit_reply(sock_fd, sck_message);
transmit_reply(&tx_message, &where_from);
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);
@@ -1366,7 +1408,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
/* 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 (remote_ip.family == IPADDR_UNSPEC) {
if (where_from.sa.sa_family == AF_UNIX) {
assert(sock_fd == sock_fdu);
allowed = 1;
} else {
@@ -1505,8 +1547,12 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_cmdaccheck(&rx_message, &tx_message);
break;
case REQ_ADD_SOURCE:
handle_add_source(&rx_message, &tx_message);
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);
break;
case REQ_DEL_SOURCE:
@@ -1609,14 +1655,6 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_onoffline(&rx_message, &tx_message);
break;
case REQ_NTP_SOURCE_NAME:
handle_ntp_source_name(&rx_message, &tx_message);
break;
case REQ_RESET:
handle_reset(&rx_message, &tx_message);
break;
default:
DEBUG_LOG("Unhandled command %d", rx_command);
tx_message.status = htons(STT_FAILED);
@@ -1634,7 +1672,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
static int do_it=1;
if (do_it) {
transmit_reply(sock_fd, sck_message);
transmit_reply(&tx_message, &where_from);
}
#if 0

View File

@@ -62,8 +62,6 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.filter_length = 0;
src->params.interleaved = 0;
src->params.sel_options = 0;
src->params.nts = 0;
src->params.nts_port = SRC_DEFAULT_NTSPORT;
src->params.authkey = INACTIVE_AUTHKEY;
src->params.max_delay = SRC_DEFAULT_MAXDELAY;
src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
@@ -142,11 +140,6 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} 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;
@@ -268,7 +261,7 @@ CPS_SplitWord(char *line)
/* ================================================== */
int
CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key)
CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key)
{
char *s1, *s2, *s3, *s4;
@@ -285,10 +278,10 @@ CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key)
return 0;
if (*s3) {
*type = s2;
*hash = s2;
*key = s3;
} else {
*type = "MD5";
*hash = "MD5";
*key = s2;
}

View File

@@ -49,6 +49,6 @@ extern void CPS_NormalizeLine(char *line);
extern char *CPS_SplitWord(char *line);
/* Parse a key from keyfile */
extern int CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key);
extern int CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key);
#endif /* GOT_CMDPARSE_H */

153
conf.c
View File

@@ -223,24 +223,6 @@ static char *leapsec_tz = NULL;
/* 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 char *nts_server_cert_file = NULL;
static char *nts_server_key_file = NULL;
static int nts_server_port = 11443;
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 char *nts_trusted_cert_file = NULL;
/* 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;
@@ -408,11 +390,6 @@ CNF_Finalise(void)
Free(mail_user_on_change);
Free(tempcomp_sensor_file);
Free(tempcomp_point_file);
Free(nts_dump_dir);
Free(nts_ntp_server);
Free(nts_server_cert_file);
Free(nts_server_key_file);
Free(nts_trusted_cert_file);
}
/* ================================================== */
@@ -425,7 +402,14 @@ CNF_ReadFile(const char *filename)
char line[2048];
int i;
in = UTI_OpenFile(NULL, filename, NULL, 'R', 0);
in = fopen(filename, "r");
if (!in) {
LOG_FATAL("Could not open configuration file %s : %s",
filename, strerror(errno));
return;
}
DEBUG_LOG("Reading %s", filename);
for (i = 1; fgets(line, sizeof(line), in); i++) {
CNF_ParseLine(filename, i, line);
@@ -536,8 +520,6 @@ CNF_ParseLine(const char *filename, int number, char *line)
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")) {
@@ -548,33 +530,10 @@ CNF_ParseLine(const char *filename, int number, char *line)
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, "ntstrustedcerts")) {
parse_string(p, &nts_trusted_cert_file);
} 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_string(p, &nts_server_cert_file);
} else if (!strcasecmp(command, "ntsserverkey")) {
parse_string(p, &nts_server_key_file);
} else if (!strcasecmp(command, "peer")) {
parse_source(p, NTP_PEER, 0);
} else if (!strcasecmp(command, "pidfile")) {
@@ -2075,99 +2034,3 @@ CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface)
*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;
}
/* ================================================== */
char *
CNF_GetNtsServerCertFile(void)
{
return nts_server_cert_file;
}
/* ================================================== */
char *
CNF_GetNtsServerKeyFile(void)
{
return nts_server_key_file;
}
/* ================================================== */
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;
}
/* ================================================== */
char *
CNF_GetNtsTrustedCertFile(void)
{
return nts_trusted_cert_file;
}
/* ================================================== */
int
CNF_GetNoSystemCert(void)
{
return no_system_cert;
}
/* ================================================== */
int
CNF_GetNoCertTimeCheck(void)
{
return no_cert_time_check;
}

13
conf.h
View File

@@ -139,17 +139,4 @@ typedef struct {
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
extern char *CNF_GetNtsDumpDir(void);
extern char *CNF_GetNtsNtpServer(void);
extern char *CNF_GetNtsServerCertFile(void);
extern char *CNF_GetNtsServerKeyFile(void);
extern int CNF_GetNtsServerPort(void);
extern int CNF_GetNtsServerProcesses(void);
extern int CNF_GetNtsServerConnections(void);
extern int CNF_GetNtsRefresh(void);
extern int CNF_GetNtsRotate(void);
extern char *CNF_GetNtsTrustedCertFile(void);
extern int CNF_GetNoSystemCert(void);
extern int CNF_GetNoCertTimeCheck(void);
#endif /* GOT_CONF_H */

145
configure vendored
View File

@@ -55,34 +55,6 @@ test_code () {
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 <<EOF
@@ -117,8 +89,6 @@ For better control, use the options below.
--without-nettle Don't use nettle even if it is available
--without-nss Don't use NSS even if it is available
--without-tomcrypt Don't use libtomcrypt even if it is available
--disable-nts Disable NTS support
--without-gnutls Don't use gnutls even if it is available
--disable-cmdmon Disable command and monitoring support
--disable-ntp Disable NTP support
--disable-refclock Disable reference clock support
@@ -167,11 +137,6 @@ Some influential environment variables:
headers in a nonstandard directory <include dir>
LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
nonstandard directory <lib dir>
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.
@@ -189,6 +154,13 @@ add_def () {
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
@@ -214,11 +186,11 @@ OPERATINGSYSTEM=`uname -s`
VERSION=`uname -r`
MACHINE=`uname -m`
LIBS=""
EXTRA_LIBS=""
EXTRA_CLI_LIBS=""
EXTRA_OBJECTS=""
EXTRA_CLI_OBJECTS=""
EXTRA_DEFS=""
SYSDEFS=""
feat_debug=0
feat_cmdmon=1
@@ -231,8 +203,6 @@ 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
@@ -403,12 +373,6 @@ do
--without-tomcrypt )
try_tomcrypt=0
;;
--disable-nts )
feat_nts=0
;;
--without-gnutls )
try_gnutls=0
;;
--host-system=* )
OPERATINGSYSTEM=`echo $option | sed -e 's/^.*=//;'`
;;
@@ -468,7 +432,8 @@ case $OPERATINGSYSTEM in
;;
Darwin)
EXTRA_OBJECTS="sys_macosx.o"
LIBS="$LIBS -lresolv"
EXTRA_LIBS="-lresolv"
EXTRA_CLI_LIBS="-lresolv"
add_def MACOSX
if [ $feat_droproot = "1" ]; then
add_def FEAT_PRIVDROP
@@ -487,7 +452,8 @@ case $OPERATINGSYSTEM in
;;
SunOS)
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o sys_posix.o"
LIBS="$LIBS -lsocket -lnsl -lresolv"
EXTRA_LIBS="-lsocket -lnsl -lresolv"
EXTRA_CLI_LIBS="-lsocket -lnsl -lresolv"
try_setsched=1
try_lockmem=1
add_def SOLARIS
@@ -519,7 +485,7 @@ fi
if [ $feat_ntp = "1" ]; then
add_def FEAT_NTP
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_auth.o ntp_core.o ntp_ext.o ntp_io.o ntp_sources.o"
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_core.o ntp_io.o ntp_sources.o"
if [ $feat_ntp_signd = "1" ]; then
add_def FEAT_SIGND
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_signd.o"
@@ -587,16 +553,6 @@ if [ "x$MYCC" = "xgcc" ] || [ "x$MYCC" = "xclang" ]; then
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];'
@@ -636,9 +592,11 @@ then
fi
MATHCODE='return (int) pow(2.0, log(sqrt((double)argc)));'
if ! test_code 'math' 'math.h' '' '' "$MATHCODE"; then
if test_code 'math' 'math.h' '' '' "$MATHCODE"; then
LIBS=""
else
if test_code 'math in -lm' 'math.h' '' '-lm' "$MATHCODE"; then
LIBS="$LIBS -lm"
LIBS="-lm"
else
echo "error: could not compile/link a program which uses sqrt(), log(), pow()"
exit 1
@@ -653,7 +611,7 @@ then
fi
if [ $feat_ipv6 = "1" ] && \
test_code 'IPv6 support' 'arpa/inet.h sys/socket.h netinet/in.h' '' "$LIBS" '
test_code 'IPv6 support' 'arpa/inet.h sys/socket.h netinet/in.h' '' "$EXTRA_LIBS" '
struct sockaddr_in6 n;
char p[100];
n.sin6_addr = in6addr_any;
@@ -689,16 +647,15 @@ if [ $try_clock_gettime = "1" ]; then
fi
fi
if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$LIBS" \
if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$EXTRA_LIBS" \
'return getaddrinfo(0, 0, 0, 0);'
then
add_def HAVE_GETADDRINFO
fi
if [ $feat_asyncdns = "1" ] && \
test_code 'pthread' 'pthread.h' '-pthread' '' '
pthread_t thread;
return (int)pthread_create(&thread, NULL, (void *)1, NULL);'
test_code 'pthread' 'pthread.h' '-pthread' '' \
'return (int)pthread_create((void *)1, NULL, (void *)1, NULL);'
then
add_def FEAT_ASYNCDNS
add_def USE_PTHREAD_ASYNCDNS
@@ -719,11 +676,11 @@ 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' '' "$LIBS" "$RECVMMSG_CODE"; then
if test_code 'recvmmsg()' 'sys/socket.h' '' "$EXTRA_LIBS" "$RECVMMSG_CODE"; then
add_def HAVE_RECVMMSG
else
if test_code 'recvmmsg() with _GNU_SOURCE' 'sys/socket.h' '-D_GNU_SOURCE' \
"$LIBS" "$RECVMMSG_CODE"
"$EXTRA_LIBS" "$RECVMMSG_CODE"
then
add_def _GNU_SOURCE
add_def HAVE_RECVMMSG
@@ -774,7 +731,6 @@ if [ "x$timepps_h" != "x" ] && \
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
@@ -861,7 +817,6 @@ if [ $try_lockmem = "1" ] && \
'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
@@ -924,16 +879,9 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ];
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
@@ -946,6 +894,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nss = "1" ]; th
then
HASH_OBJ="hash_nss.o"
HASH_LINK="$test_link"
LIBS="$LIBS $HASH_LINK"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_SECHASH
fi
@@ -957,44 +906,12 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]
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" ] && \
echo "$HASH_LINK" | grep 'nettle' > /dev/null; then
test_cflags="`pkg_config --cflags gnutls`"
test_link="`pkg_config --libs gnutls`"
if test_code 'gnutls' 'gnutls/gnutls.h' \
"$test_cflags" "$test_link" '
return gnutls_init(NULL, 0) +
gnutls_priority_init2(NULL, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
gnutls_prf_rfc5705(NULL, 0, "", 0, "", 16, NULL);' &&
test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \
'aes128_set_encrypt_key(NULL, NULL);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o"
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ntp_auth.o nts_ntp_client.o nts_ntp_server.o"
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
LIBS="$LIBS $test_link"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_NTS
add_def HAVE_SIV
if test_code 'SIV in nettle' \
'nettle/siv-cmac.h' "" "$LIBS" \
'siv_cmac_aes128_set_key(NULL, NULL);'
then
add_def HAVE_NETTLE_SIV_CMAC
fi
fi
fi
if [ $use_pthread = "1" ]; then
MYCFLAGS="$MYCFLAGS -pthread"
fi
@@ -1064,7 +981,7 @@ add_def MAIL_PROGRAM "\"$mail_program\""
common_features="`get_features SECHASH IPV6 DEBUG`"
chronyc_features="`get_features READLINE`"
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SIGND ASYNCDNS NTS`"
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SIGND ASYNCDNS`"
add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\""
add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\""
echo "Features : $chronyd_features $chronyc_features $common_features"
@@ -1080,15 +997,15 @@ add_def CHRONY_VERSION "\"${CHRONY_VERSION}\""
for f in Makefile doc/Makefile test/unit/Makefile
do
echo Creating $f
sed -e "s%@EXTRA_OBJS@%${EXTRA_OBJECTS}%;\
s%@EXTRA_CLI_OBJS@%${EXTRA_CLI_OBJECTS}%;\
sed -e "s%@EXTRA_OBJECTS@%${EXTRA_OBJECTS}%;\
s%@CC@%${MYCC}%;\
s%@CFLAGS@%${MYCFLAGS}%;\
s%@CPPFLAGS@%${MYCPPFLAGS}%;\
s%@LDFLAGS@%${MYLDFLAGS}%;\
s%@LIBS@%${LIBS}%;\
s%@LDFLAGS@%${MYLDFLAGS}%;\
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}%;\

View File

@@ -96,7 +96,7 @@ interval in order to allow a burst with two requests.
*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 key specified in the key file,
The MAC is generated as a function of a password specified in the key file,
which is specified by the <<keyfile,*keyfile*>> directive.
+
The *key* option specifies which key (with an ID in the range 1 through 2^32-1)
@@ -107,12 +107,6 @@ otherwise no relationship between the computers will be possible.
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.
*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
@@ -200,13 +194,14 @@ authenticated source to be safely combined with unauthenticated sources in
order to improve the accuracy of the clock. They can be selected and used for
synchronisation only if they agree with the trusted and required source.
*xleave*:::
This option enables an interleaved mode which enables the server to
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 is running *chronyd* with software (kernel) or hardware
server or the peer is running *chronyd* with software (kernel) or hardware
timestamping). This can significantly improve the accuracy of the measurements.
+
The interleaved mode is compatible with servers that support only the basic
mode. Note that even
mode, but peers must both support and have enabled the interleaved mode,
otherwise the synchronisation will work only in one direction. 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.
@@ -226,9 +221,6 @@ intervals. The default is 8 and a useful range is from 6 to 60.
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 11443.
*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
@@ -251,8 +243,8 @@ 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.
+
If the *presend* option is used together with the *xleave* option, *chronyd*
will send two extra packets instead of one.
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.
*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
@@ -277,18 +269,16 @@ which might change over time.
+
All options valid in the <<server,*server*>> 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.
+
*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.
+
::
When an NTP source is unreachable,
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,
marked as a falseticker, or has a distance larger than the limit set by the
<<maxdistance,*maxdistance*>> directive, *chronyd* will try to replace the
source with a newly resolved address of the name.
source with a newly resolved address from the pool.
+
An example of the *pool* directive is
+
@@ -305,12 +295,6 @@ is mainly useful when the NTP implementation of the peer (e.g. *ntpd*) supports
ephemeral symmetric associations and does not need to be configured with an
address of this host. *chronyd* does not support ephemeral associations.
+
The following options of the *server* directive do not work in the *peer*
directive: *iburst*, *burst*, *nts*, *presend*.
+
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.
+
@@ -671,10 +655,6 @@ The *maxsamples* directive sets the default maximum number of samples that
individual sources in the <<server,*server*>> and <<refclock,*refclock*>>
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
@@ -689,54 +669,6 @@ changes in the frequency and offset of the clock. The offsets in the
<<chronyc.adoc#sourcestats,*sourcestats*>> 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.
+
An example of the directive is:
+
----
ntsdumpdir @CHRONYVARDIR@
----
+
This directory is used also by the <<ntsdumpdir2,NTS server>> 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).
[[ntstrustedcerts]]*ntstrustedcerts* _file_::
This directive specifies a file containing certificates (in the PEM format) of
trusted certificate authorities (CA) that should be used to verify certificates
of NTS servers in addition to the system's default trusted CAs (if the
*nosystemcert* directive is not present).
[[nosystemcert]]*nosystemcert*::
This directive disables the system's default trusted CAs.
[[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, e.g. if an NTP server was ever
compromised, its certificate could be used in an attack after the expiration
time. The default value is 0, which means the time checks are always enabled.
An example of the directive is:
+
----
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
[[combinelimit]]*combinelimit* _limit_::
@@ -922,7 +854,7 @@ slightly different rates when it is necessary to keep them close together, the
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 by slewing instead of stepping. The clients
the served time is corrected slowly be 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
@@ -1398,77 +1330,6 @@ An example of the directive is:
ntpsigndsocket /var/lib/samba/ntp_signd
----
[[ntsport]]*ntsport* _port_::
This directive specifies the TCP port on which *chronyd* will provide the NTS
Key Establishment (NTS-KE) service. The default port is 11443.
+
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.
[[ntsserverkey]]*ntsserverkey* _file_::
This directive specifies a file containing a private key in the PEM format
for *chronyd* to operate as an NTS server.
[[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.
[[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 <<ntsdumpdir1,NTS client>> to save NTS cookies.
[[ntsntpserver]]*ntsntpserver* _hostname_::
This directive specifies the hostname 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
<<ntsrotate,*ntsrotate*>> 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. The
server keeps two previous keys to give the clients time to get new cookies
encrypted by the latest key. The default interval is 604800 seconds (1 week).
+
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 <<chronyc.adoc#rekey,*rekey*>> 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
<<ntsntpname,*ntsntpname*>> 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
@@ -2152,10 +2013,8 @@ include @SYSCONFDIR@/chrony.d/*.conf
----
[[keyfile]]*keyfile* _file_::
This directive is used to specify the location of the file containing symmetric
keys which are shared between NTP servers and clients, or peers, in order to
authenticate NTP packets with a message authentication code (MAC) using a
cryptographic hash function or cipher.
This directive is used to specify the location of the file containing ID-key
pairs for authentication of NTP packets.
+
The format of the directive is shown in the example below:
+
@@ -2170,41 +2029,31 @@ format of the file is shown below:
10 tulip
11 hyacinth
20 MD5 ASCII:crocus
25 SHA1 HEX:933F62BE1D604E68A81B557F18CFA200483F5B70
30 AES128 HEX:7EA62AE64D190114D46D5A082F948EC1
31 AES256 HEX:37DDCBC67BB902BCB8E995977FAB4D2B5642F5B32EBCEEE421921D97E5CBFE39
25 SHA1 HEX:1dc764e0791b11fa67efc7ecbc4b0d73f68a070c
...
----
+
Each line consists of an ID, optional type, and key.
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.
+
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 of the following hash functions and ciphers may
also be available:
*SHA3-224*, *SHA3-256*, *SHA3-384*, *SHA3-512*, *TIGER*, *WHIRLPOOL*, *AES128*,
*AES256*.
*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*.
+
The key can be specified as a string of ASCII characters not containing white
The password can be specified as a string of 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).
+
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 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.
+
The <<chronyc.adoc#keygen,*keygen*>> command of *chronyc* can be used to
generate random keys for the key file. By default, it generates 160-bit MD5 or
@@ -2373,7 +2222,7 @@ actually connected to the Internet.
=== Isolated networks
This section shows how to configure *chronyd* for computers that never have
network connectivity to any computer which ultimately derives its time from a
network conectivity 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

View File

@@ -51,8 +51,7 @@ running under a non-root user), it will try to connect to 127.0.0.1 and then
Only the following monitoring commands, which do not affect the behaviour of
*chronyd*, are allowed from the network: *activity*, *manual list*,
*rtcdata*, *smoothing*, *sourcename*, *sources*, *sourcestats*, *tracking*,
*waitsync*. The
*rtcdata*, *smoothing*, *sources*, *sourcestats*, *tracking*, *waitsync*. The
set of hosts from which *chronyd* will accept these commands can be configured
with the <<chrony.conf.adoc#cmdallow,*cmdallow*>> directive in the *chronyd*'s
configuration file or the <<cmdallow,*cmdallow*>> command in *chronyc*. By
@@ -79,18 +78,11 @@ With this option hostnames will be resolved only to IPv6 addresses.
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 the original names of NTP sources that were
specified in the configuration file, or *chronyc* commands, and are internally
used by *chronyd*. Without the *-n* and *-N* option, the names of NTP sources
are obtained from reverse DNS lookups and can be different from the original
names.
*-c*::
This option enables printing of reports in a comma-separated values (CSV)
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.
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.
*-d*::
This option enables printing of debugging messages if *chronyc* was compiled
@@ -292,18 +284,15 @@ milliseconds.
=== Time sources
[[sources]]*sources* [*-a*] [*-v*]::
[[sources]]*sources* [*-v*]::
This command displays information about the current time sources that *chronyd*
is accessing.
+
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,
The optional argument *-v* can be specified, meaning _verbose_. 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
@@ -364,21 +353,18 @@ since. The number following the _+/-_ indicator shows the margin of error in
the measurement. Positive offsets indicate that the local clock is ahead of
the source.
[[sourcestats]]*sourcestats* [*-a*] [*-v*]::
[[sourcestats]]*sourcestats* [*-v*]::
The *sourcestats* command displays information about the drift rate and offset
estimation process for each of the sources currently being examined by
*chronyd*.
+
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,
The optional argument *-v* can be specified, meaning _verbose_. 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
@@ -456,9 +442,8 @@ not visible in the *sources* and *sourcestats* reports.
[[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
(with a known address) 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 if
no address was specified. An example of the output is shown below.
+
----
Remote address : 203.0.113.15 (CB00710F)
@@ -541,13 +526,15 @@ The number of all packets received from the source.
*Total valid RX*:::
The number of valid packets received from the source.
[[add_peer]]*add peer* _name_ [_option_]...::
[[add_peer]]*add peer* _address_ [_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 identical to that for the
parameters and options is similar to that for the
<<chrony.conf.adoc#peer,*peer*>> 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.
+
@@ -555,27 +542,15 @@ An example of using this command is shown below.
add peer foo.example.net minpoll 6 maxpoll 10 key 25
----
[[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 <<chrony.conf.adoc#pool,*pool*>>
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_]...::
[[add_server]]*add server* _address_ [_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 identical to that for the <<chrony.conf.adoc#server,*server*>>
options is similar to that for the <<chrony.conf.adoc#server,*server*>>
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:
+
@@ -713,8 +688,7 @@ the loaded periods. The *offline* and *online* commands can be used to achieve
this.
+
There are four forms of the *offline* command. The first form is a wildcard,
meaning all sources (including sources that do not have a known address yet).
The second form allows an IP address mask and a masked
meaning all sources. 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.
+
@@ -751,11 +725,10 @@ The syntax is identical to that of the <<offline,*offline*>> command.
[[onoffline]]
*onoffline*::
The *onoffline* command tells *chronyd* to switch all sources that have a known
address to the online or
The *onoffline* command tells *chronyd* to switch all sources to the online or
offline status according to the current network configuration. A source is
considered online if it is possible to send requests to it, i.e. a network
route to the source is present.
considered online if it is possible to send requests to it, i.e. a route to the
network is present.
[[polltarget]]*polltarget* _address_ _polltarget_::
The *polltarget* command is used to modify the poll target for one of the
@@ -771,15 +744,6 @@ Sources that stop responding will be replaced with newly resolved addresses
automatically after 8 polling intervals, but this command can still be useful
to replace them immediately and not wait until they are marked as unreachable.
[[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]]
@@ -1164,27 +1128,15 @@ purged. An example of how to do this is shown below.
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 <<chrony.conf.adoc#dumpdir,*dumpdir*>>
directive and also write server NTS keys and client NTS cookies to the
directory specified by the <<chrony.conf.adoc#ntsdumpdir1,*ntsdumpdir*>>
directive. Note that *chronyd* does this automatically when it exits. This
command is mainly useful for inspection whilst *chronyd* is running.
command is mainly useful for inspection of the history whilst *chronyd* is
running.
[[rekey]]*rekey*::
The *rekey* command causes *chronyd* to re-read the key file specified in the
configuration file by the <<chrony.conf.adoc#keyfile,*keyfile*>> directive. It
also re-reads the server NTS keys if
<<chrony.conf.adoc#ntsdumpdir2,*ntsdumpdir*>> is specified and
<<chrony.conf.adoc#ntsrotate,automatic rotation>> is disabled in the
configuration file.
configuration file by the <<chrony.conf.adoc#keyfile,*keyfile*>> directive.
[[reset]]*reset*::
The *reset* command causes *chronyd* to drop all measurements and switch to the
unsynchronised state. This command can help *chronyd* with recovery when the
measurements are known to be no longer valid or accurate, e.g. due to moving
the computer to a different network, or resuming the computer from a low-power
state (which resets the system clock).
[[shutdown]]*shutdown*::
[[rekey]]*shutdown*::
The *shutdown* command causes *chronyd* to exit. This is equivalent to sending
the process the SIGTERM signal.
@@ -1236,10 +1188,10 @@ generated from the _/dev/urandom_ device and it is printed to standard output.
+
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 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).
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).
+
An example is:
+

View File

@@ -41,7 +41,7 @@ If no configuration directives are specified on the command line, *chronyd*
will read them from a configuration file. The compiled-in default location of
the file is _@SYSCONFDIR@/chrony.conf_.
Informational messages, warnings, and errors will be logged to syslog.
Information messages and warnings will be logged to syslog.
== OPTIONS
@@ -55,26 +55,20 @@ IPv6 sockets will be created.
*-f* _file_::
This option can be used to specify an alternate location for the configuration
file. The default value is _@SYSCONFDIR@/chrony.conf_.
file (default _@SYSCONFDIR@/chrony.conf_).
*-n*::
When run in this mode, the program will not detach itself from the terminal.
*-d*::
When run in this mode, the program will not detach itself from the terminal,
and all messages will be written to the terminal instead of syslog. If
*chronyd* was compiled with enabled support for debugging, this option can be
used twice to enable debug messages.
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.
*-l* _file_::
This option enables writing of log messages to a file instead of syslog or the
terminal.
*-L* _level_::
This option specifies the minimum severity level of messages to be written to
the log file, syslog, or terminal. The following levels can be specified:
0 (informational), 1 (warning), 2 (non-fatal error), and 3 (fatal error). The
default value is 0.
This option specifies a file which should be used for logging instead of syslog
or terminal.
*-q*::
When run in this mode, *chronyd* will set the system clock once and exit. It
@@ -131,8 +125,7 @@ running, but still allow it to adjust the frequency of the system clock.
*-u* _user_::
This option sets the name of the system user to which *chronyd* will switch
after start in order to drop root privileges. It overrides the
<<chrony.conf.adoc#user,*user*>> directive. The default value is
_@DEFAULT_USER@_.
<<chrony.conf.adoc#user,*user*>> directive (default _@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.
@@ -143,8 +136,7 @@ range of privileged system calls on behalf of the parent.
This option configures a system call filter when *chronyd* is compiled with
support for the Linux secure computing (seccomp) facility. In level 1 the
process is killed when a forbidden system call is made, in level -1 the SIGSYS
signal is thrown instead and in level 0 the filter is disabled. The default
value is 0.
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
@@ -156,9 +148,9 @@ killed even in normal operation.
*-P* _priority_::
On Linux, this option will select the SCHED_FIFO real-time scheduler at the
specified priority (which must be between 0 and 100). On macOS, this option
must have either a value of 0 to disable the thread time
must have either a value of 0 (the default) to disable the thread time
constraint policy or 1 for the policy to be enabled. Other systems do not
support this option. The default value is 0.
support this option.
*-m*::
This option will lock *chronyd* into RAM so that it will never be paged out.

View File

@@ -244,18 +244,6 @@ specified on the command line. For example:
# chronyd -q 'pool pool.ntp.org iburst'
----
The command above would normally take about 5 seconds if the servers were
well synchronised and responding to all requests. If not synchronised or
responding, it would take about 10 seconds for `chronyd` to give up and exit
with a non-zero status. A faster configuration is possible. A single server can
be used instead of four servers, the number of measurements can be reduced with
the `maxsamples` option, and a timeout can be specified with the `-t` option.
The following command would take only up to about 1 second.
----
# chronyd -q -t 1 'server pool.ntp.org iburst maxsamples 1'
----
=== Can `chronyd` be configured to control the clock like `ntpd`?
It is not possible to perfectly emulate `ntpd`, but there are some options that

View File

@@ -22,25 +22,18 @@ The software is distributed as source code which has to be compiled. The source
code is supplied in the form of a gzipped tar file, which unpacks to a
subdirectory identifying the name and version of the program.
A C compiler (e.g. `gcc` or `clang`) and GNU Make are needed to build `chrony`.
The following libraries with their development files, and programs, are needed
to enable optional features:
The following programs and libraries with their development files are needed to
build `chrony`:
* pkg-config: detection of development libraries
* Nettle, NSS, or LibTomCrypt: secure hash functions (`SECHASH`)
* libcap: dropping root privileges on Linux (`DROPROOT`)
* libseccomp: system call filter on Linux (`SCFILTER`)
* GnuTLS and Nettle: Network Time Security (`NTS`)
* Editline: line editing in `chronyc` (`READLINE`)
* timepps.h header: PPS reference clock
* Asciidoctor: documentation in HTML format
* Bash: test suite
The following programs are needed when building `chrony` from the git
repository instead of a released tar file:
* Asciidoctor: manual pages
* Bison: parser for chronyc settime command
* 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)
After unpacking the source code, change directory into it, and type

View File

@@ -43,6 +43,7 @@ struct hash {
static struct hash hashes[] = {
{ "MD5", "md5", NULL, NULL },
{ "RMD160", "ripemd160", NULL, NULL },
{ "SHA1", "sha1", NULL, NULL },
{ "SHA256", "sha256", NULL, NULL },
{ "SHA384", "sha384", NULL, NULL },

View File

@@ -39,6 +39,18 @@ struct hash {
static const struct hash hashes[] = {
{ "MD5", "md5", &md5_desc },
#ifdef LTC_RIPEMD128
{ "RMD128", "rmd128", &rmd128_desc },
#endif
#ifdef LTC_RIPEMD160
{ "RMD160", "rmd160", &rmd160_desc },
#endif
#ifdef LTC_RIPEMD256
{ "RMD256", "rmd256", &rmd256_desc },
#endif
#ifdef LTC_RIPEMD320
{ "RMD320", "rmd320", &rmd320_desc },
#endif
#ifdef LTC_SHA1
{ "SHA1", "sha1", &sha1_desc },
#endif

156
keys.c
View File

@@ -32,7 +32,6 @@
#include "array.h"
#include "keys.h"
#include "cmac.h"
#include "cmdparse.h"
#include "conf.h"
#include "memory.h"
@@ -43,22 +42,11 @@
/* 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;
KeyClass class;
union {
struct {
unsigned char *value;
int length;
int hash_id;
} ntp_mac;
CMC_Instance cmac;
} data;
char *val;
int len;
int hash_id;
int auth_delay;
} Key;
@@ -74,21 +62,9 @@ static void
free_keys(void)
{
unsigned int i;
Key *key;
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);
}
}
for (i = 0; i < ARR_GetSize(keys); i++)
Free(((Key *)ARR_GetElement(keys, i))->val);
ARR_SetSize(keys, 0);
cache_valid = 0;
@@ -135,9 +111,8 @@ determine_hash_delay(uint32_t key_id)
for (i = 0; i < 10; i++) {
LCL_ReadRawTime(&before);
KEY_GenerateAuth(key_id, (unsigned char *)&pkt, NTP_HEADER_LENGTH,
(unsigned char *)&pkt + NTP_HEADER_LENGTH,
sizeof (pkt) - NTP_HEADER_LENGTH);
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);
@@ -146,7 +121,8 @@ determine_hash_delay(uint32_t key_id)
min_diff = diff;
}
nsecs = 1.0e9 * min_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);
@@ -154,18 +130,30 @@ determine_hash_delay(uint32_t key_id)
}
/* ================================================== */
/* Decode key encoded in ASCII or HEX */
/* Decode password encoded in ASCII or HEX */
static int
decode_key(char *key)
decode_password(char *key)
{
int len = strlen(key);
int i, j, len = strlen(key);
char buf[3], *p;
if (!strncmp(key, "ASCII:", 6)) {
memmove(key, key + 6, len - 6);
return len - 6;
} else if (!strncmp(key, "HEX:", 4)) {
return UTI_HexToBytes(key + 4, key, len);
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;
} else {
/* assume ASCII */
return len;
@@ -197,11 +185,11 @@ compare_keys_by_id(const void *a, const void *b)
void
KEY_Reload(void)
{
unsigned int i, line_number, key_length, cmac_key_length;
unsigned int i, line_number;
FILE *in;
char line[2048], *key_file, *key_value;
const char *key_type;
int hash_id;
uint32_t key_id;
char line[2048], *keyval, *key_file;
const char *hashname;
Key key;
free_keys();
@@ -212,7 +200,7 @@ KEY_Reload(void)
if (!key_file)
return;
in = UTI_OpenFile(NULL, key_file, NULL, 'r', 0);
in = fopen(key_file, "r");
if (!in) {
LOG(LOGS_WARN, "Could not open keyfile %s", key_file);
return;
@@ -225,43 +213,26 @@ KEY_Reload(void)
if (!*line)
continue;
memset(&key, 0, sizeof (key));
if (!CPS_ParseKey(line, &key.id, &key_type, &key_value)) {
if (!CPS_ParseKey(line, &key_id, &hashname, &keyval)) {
LOG(LOGS_WARN, "Could not parse key at line %u in file %s", line_number, key_file);
continue;
}
key_length = decode_key(key_value);
if (key_length == 0) {
LOG(LOGS_WARN, "Could not decode key %"PRIu32, key.id);
key.hash_id = HSH_GetHashId(hashname);
if (key.hash_id < 0) {
LOG(LOGS_WARN, "Unknown hash function in key %"PRIu32, key_id);
continue;
}
hash_id = HSH_GetHashId(key_type);
cmac_key_length = CMC_GetKeyLength(key_type);
if (hash_id >= 0) {
key.class = NTP_MAC;
key.data.ntp_mac.value = MallocArray(unsigned char, key_length);
memcpy(key.data.ntp_mac.value, key_value, key_length);
key.data.ntp_mac.length = key_length;
key.data.ntp_mac.hash_id = hash_id;
} else if (cmac_key_length > 0) {
if (cmac_key_length != key_length) {
LOG(LOGS_WARN, "Invalid length of %s key %"PRIu32" (expected %u bits)",
key_type, key.id, 8 * cmac_key_length);
continue;
}
key.class = CMAC;
key.data.cmac = CMC_CreateInstance(key_type, (unsigned char *)key_value, key_length);
assert(key.data.cmac);
} else {
LOG(LOGS_WARN, "Unknown hash function or cipher in key %"PRIu32, key.id);
key.len = decode_password(keyval);
if (!key.len) {
LOG(LOGS_WARN, "Could not decode password 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);
}
@@ -364,15 +335,7 @@ KEY_GetAuthLength(uint32_t key_id)
if (!key)
return 0;
switch (key->class) {
case NTP_MAC:
return HSH_Hash(key->data.ntp_mac.hash_id, buf, 0, buf, 0, buf, sizeof (buf));
case CMAC:
return CMC_Hash(key->data.cmac, buf, 0, buf, sizeof (buf));
default:
assert(0);
return 0;
}
return HSH_Hash(key->hash_id, buf, 0, buf, 0, buf, sizeof (buf));
}
/* ================================================== */
@@ -387,41 +350,30 @@ KEY_CheckKeyLength(uint32_t key_id)
if (!key)
return 0;
switch (key->class) {
case NTP_MAC:
return key->data.ntp_mac.length >= MIN_SECURE_KEY_LENGTH;
default:
return 1;
}
return key->len >= MIN_SECURE_KEY_LENGTH;
}
/* ================================================== */
static int
generate_auth(Key *key, const unsigned char *data, int data_len,
unsigned char *auth, int auth_len)
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)
{
switch (key->class) {
case NTP_MAC:
return HSH_Hash(key->data.ntp_mac.hash_id, key->data.ntp_mac.value,
key->data.ntp_mac.length, data, data_len, auth, auth_len);
case CMAC:
return CMC_Hash(key->data.cmac, data, data_len, auth, auth_len);
default:
return 0;
}
return HSH_Hash(hash_id, key, key_len, data, data_len, auth, auth_len);
}
/* ================================================== */
static int
check_auth(Key *key, const unsigned char *data, int data_len,
const unsigned char *auth, int auth_len, int trunc_len)
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)
{
unsigned char buf[MAX_HASH_LENGTH];
int hash_len;
hash_len = generate_auth(key, data, data_len, buf, sizeof (buf));
hash_len = generate_ntp_auth(hash_id, key, key_len, data, data_len, buf, sizeof (buf));
return MIN(hash_len, trunc_len) == auth_len && !memcmp(buf, auth, auth_len);
}
@@ -430,7 +382,7 @@ check_auth(Key *key, const unsigned char *data, int data_len,
int
KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
unsigned char *auth, int auth_len)
unsigned char *auth, int auth_len)
{
Key *key;
@@ -439,7 +391,8 @@ KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
if (!key)
return 0;
return generate_auth(key, data, data_len, auth, auth_len);
return generate_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len,
data, data_len, auth, auth_len);
}
/* ================================================== */
@@ -455,5 +408,6 @@ KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len,
if (!key)
return 0;
return check_auth(key, data, data_len, auth, auth_len, trunc_len);
return check_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len,
data, data_len, auth, auth_len, trunc_len);
}

View File

@@ -29,24 +29,26 @@
#include "sysincl.h"
#include <syslog.h>
#include "conf.h"
#include "logging.h"
#include "util.h"
/* This is used by DEBUG_LOG macro */
LOG_Severity log_min_severity = LOGS_INFO;
int log_debug_enabled = 0;
/* ================================================== */
/* Flag indicating we have initialised */
static int initialised = 0;
static FILE *file_log = NULL;
static FILE *file_log;
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;
@@ -68,7 +70,7 @@ void
LOG_Initialise(void)
{
initialised = 1;
LOG_OpenFileLog(NULL);
file_log = stderr;
}
/* ================================================== */
@@ -132,7 +134,7 @@ void LOG_Message(LOG_Severity severity,
time_t t;
struct tm *tm;
if (!system_log && file_log && severity >= log_min_severity) {
if (!system_log && file_log) {
/* Don't clutter up syslog with timestamps and internal debugging info */
time(&t);
tm = gmtime(&t);
@@ -141,7 +143,7 @@ void LOG_Message(LOG_Severity severity,
fprintf(file_log, "%s ", buf);
}
#if DEBUG > 0
if (log_min_severity <= LOGS_DEBUG)
if (debug_level >= DEBUG_LEVEL_PRINT_FUNCTION)
fprintf(file_log, "%s:%d:(%s) ", filename, line_number, function_name);
#endif
}
@@ -155,12 +157,10 @@ void LOG_Message(LOG_Severity severity,
case LOGS_INFO:
case LOGS_WARN:
case LOGS_ERR:
if (severity >= log_min_severity)
log_message(0, severity, buf);
log_message(0, severity, buf);
break;
case LOGS_FATAL:
if (severity >= log_min_severity)
log_message(1, severity, buf);
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 */
@@ -186,7 +186,9 @@ LOG_OpenFileLog(const char *log_file)
FILE *f;
if (log_file) {
f = UTI_OpenFile(NULL, log_file, NULL, 'A', 0640);
f = fopen(log_file, "a");
if (!f)
LOG_FATAL("Could not open log file %s", log_file);
} else {
f = stderr;
}
@@ -212,10 +214,12 @@ LOG_OpenSystemLog(void)
/* ================================================== */
void LOG_SetMinSeverity(LOG_Severity severity)
void LOG_SetDebugLevel(int level)
{
/* Don't print any debug messages in a non-debug build */
log_min_severity = CLAMP(DEBUG > 0 ? LOGS_DEBUG : LOGS_INFO, severity, LOGS_FATAL);
debug_level = level;
if (level >= DEBUG_LEVEL_PRINT_DEBUG) {
log_debug_enabled = 1;
}
}
/* ================================================== */
@@ -265,7 +269,7 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
return;
if (!logfiles[id].file) {
char *logdir = CNF_GetLogDir();
char filename[512], *logdir = CNF_GetLogDir();
if (logdir[0] == '\0') {
LOG(LOGS_WARN, "logdir not specified");
@@ -273,12 +277,16 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
return;
}
logfiles[id].file = UTI_OpenFile(logdir, logfiles[id].name, ".log", 'a', 0644);
if (!logfiles[id].file) {
/* Disable the log */
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].name = NULL;
return;
}
/* Close on exec */
UTI_FdSetCloexec(fileno(logfiles[id].file));
}
banner = CNF_GetLogBanner();
@@ -286,7 +294,7 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
char bannerline[256];
int i, bannerlen;
bannerlen = MIN(strlen(logfiles[id].banner), sizeof (bannerline) - 1);
bannerlen = strlen(logfiles[id].banner);
for (i = 0; i < bannerlen; i++)
bannerline[i] = '=';

View File

@@ -31,6 +31,9 @@
#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. */
@@ -52,7 +55,7 @@
#define DEBUG_LOG(...) \
do { \
if (DEBUG && log_min_severity == LOGS_DEBUG) \
if (DEBUG && log_debug_enabled) \
LOG_MESSAGE(LOGS_DEBUG, __VA_ARGS__); \
} while (0)
@@ -66,16 +69,13 @@
/* Definition of severity */
typedef enum {
LOGS_DEBUG = -1,
LOGS_INFO = 0,
LOGS_INFO,
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,10 +92,12 @@ FORMAT_ATTRIBUTE_PRINTF(2, 3)
extern void LOG_Message(LOG_Severity severity, const char *format, ...);
#endif
/* 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);
/* 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);
/* Log messages to a file instead of stderr, or stderr again if NULL */
extern void LOG_OpenFileLog(const char *log_file);

36
main.c
View File

@@ -38,10 +38,6 @@
#include "ntp_signd.h"
#include "ntp_sources.h"
#include "ntp_core.h"
#include "nts_ke_client.h"
#include "nts_ke_server.h"
#include "nts_ntp_server.h"
#include "socket.h"
#include "sources.h"
#include "sourcestats.h"
#include "reference.h"
@@ -94,8 +90,8 @@ delete_pidfile(void)
if (!pidfile[0])
return;
if (!UTI_RemoveFile(NULL, pidfile, NULL))
;
/* Don't care if this fails, there's not a lot we can do */
unlink(pidfile);
}
/* ================================================== */
@@ -116,16 +112,12 @@ MAI_CleanupAndExit(void)
TMC_Finalise();
MNL_Finalise();
CLG_Finalise();
NKC_Finalise();
NKS_Finalise();
NNS_Finalise();
NSD_Finalise();
NSR_Finalise();
SST_Finalise();
NCR_Finalise();
NIO_Finalise();
CAM_Finalise();
SCK_Finalise();
KEY_Finalise();
RCL_Finalise();
SRC_Finalise();
@@ -261,10 +253,7 @@ check_pidfile(void)
FILE *in;
int pid, count;
if (!pidfile[0])
return;
in = UTI_OpenFile(NULL, pidfile, NULL, 'r', 0);
in = fopen(pidfile, "r");
if (!in)
return;
@@ -411,9 +400,9 @@ int main
char *user = NULL, *log_file = NULL;
struct passwd *pw;
int opt, debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = -1;
int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = 0;
int scfilter_level = 0, lock_memory = 0, sched_priority = 0;
int clock_control = 1, system_log = 1, log_severity = LOGS_INFO;
int clock_control = 1, system_log = 1;
int config_args = 0;
do_platform_checks();
@@ -434,7 +423,7 @@ int main
optind = 1;
/* Parse short command-line options */
while ((opt = getopt(argc, argv, "46df:F:hl:L:mnP:qQrRst:u:vx")) != -1) {
while ((opt = getopt(argc, argv, "46df:F:hl:mnP:qQrRst:u:vx")) != -1) {
switch (opt) {
case '4':
case '6':
@@ -454,9 +443,6 @@ int main
case 'l':
log_file = optarg;
break;
case 'L':
log_severity = parse_int_arg(optarg);
break;
case 'm':
lock_memory = 1;
break;
@@ -520,7 +506,7 @@ int main
LOG_OpenSystemLog();
}
LOG_SetMinSeverity(debug >= 2 ? LOGS_DEBUG : log_severity);
LOG_SetDebugLevel(debug);
LOG(LOGS_INFO, "chronyd version %s starting (%s)", CHRONY_VERSION, CHRONYD_FEATURES);
@@ -561,7 +547,6 @@ int main
SRC_Initialise();
RCL_Initialise();
KEY_Initialise();
SCK_Initialise();
/* Open privileged ports before dropping root */
CAM_Initialise(address_family);
@@ -589,9 +574,6 @@ int main
SST_Initialise();
NSR_Initialise();
NSD_Initialise();
NNS_Initialise();
NKS_Initialise(scfilter_level);
NKC_Initialise();
CLG_Initialise();
MNL_Initialise();
TMC_Initialise();
@@ -605,7 +587,7 @@ int main
CAM_OpenUnixSocket();
if (scfilter_level)
SYS_EnableSystemCallFilter(scfilter_level, SYS_MAIN_PROCESS);
SYS_EnableSystemCallFilter(scfilter_level);
if (ref_mode == REF_ModeNormal && CNF_GetInitSources() > 0) {
ref_mode = REF_ModeInitStepSlew;
@@ -614,7 +596,7 @@ int main
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) {

View File

@@ -34,7 +34,6 @@
#include <resolv.h>
#include "nameserv.h"
#include "socket.h"
#include "util.h"
/* ================================================== */
@@ -70,7 +69,7 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
default:
hints.ai_family = AF_UNSPEC;
}
hints.ai_socktype = SOCK_DGRAM;
hints.ai_socktype = SOCK_STREAM;
result = getaddrinfo(name, NULL, &hints, &res);
@@ -157,14 +156,10 @@ DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len)
#ifdef FEAT_IPV6
struct sockaddr_in6 in6;
IPSockAddr ip_saddr;
socklen_t slen;
char hbuf[NI_MAXHOST];
ip_saddr.ip_addr = *ip_addr;
ip_saddr.port = 0;
slen = SCK_IPSockAddrToSockaddr(&ip_saddr, (struct sockaddr *)&in6, sizeof (in6));
slen = UTI_IPAndPortToSockaddr(ip_addr, 0, (struct sockaddr *)&in6);
if (!getnameinfo((struct sockaddr *)&in6, slen, hbuf, sizeof (hbuf), NULL, 0, 0))
result = hbuf;
#else

View File

@@ -51,7 +51,7 @@ struct DNS_Async_Instance {
int pipe[2];
};
static pthread_mutex_t privops_lock = PTHREAD_MUTEX_INITIALIZER;
static int resolving_threads = 0;
/* ================================================== */
@@ -60,9 +60,7 @@ start_resolving(void *anything)
{
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)
@@ -83,6 +81,8 @@ end_resolving(int fd, int event, void *anything)
LOG_FATAL("pthread_join() failed");
}
resolving_threads--;
SCH_RemoveFileHandler(inst->pipe[0]);
close(inst->pipe[0]);
close(inst->pipe[1]);
@@ -116,6 +116,9 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
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");
}

60
ntp.h
View File

@@ -47,21 +47,18 @@ typedef uint32_t NTP_int32;
/* Maximum stratum number (infinity) */
#define NTP_MAX_STRATUM 16
/* Invalid stratum number */
#define NTP_INVALID_STRATUM 0
/* 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
/* 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)
@@ -96,10 +93,21 @@ typedef struct {
NTP_int64 receive_ts;
NTP_int64 transmit_ts;
uint8_t extensions[NTP_MAX_EXTENSIONS_LENGTH];
/* 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];
} NTP_Packet;
#define NTP_HEADER_LENGTH (int)offsetof(NTP_Packet, extensions)
#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;
/* Macros to work with the lvm field */
#define NTP_LVM_TO_LEAP(lvm) (((lvm) >> 6) & 0x3)
@@ -113,33 +121,6 @@ typedef struct {
#define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */
#define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */
/* Enumeration for authentication modes of NTP packets */
typedef enum {
NTP_AUTH_NONE = 0, /* No authentication */
NTP_AUTH_SYMMETRIC, /* MAC using symmetric key (RFC 1305, RFC 5905) */
NTP_AUTH_MSSNTP, /* MS-SNTP authenticator field */
NTP_AUTH_MSSNTP_EXT, /* MS-SNTP extended authenticator field */
NTP_AUTH_NTS, /* Network Time Security (RFC ????) */
} NTP_AuthMode;
/* Structure describing an NTP packet */
typedef struct {
int length;
int version;
NTP_Mode mode;
int ext_fields;
struct {
NTP_AuthMode mode;
struct {
int start;
int length;
uint32_t key_id;
} mac;
} auth;
} NTP_PacketInfo;
/* Structure used to save NTP measurements. time is the local time at which
the sample is to be considered to have been made and offset is the offset at
the time (positive indicates that the local clock is slow relative to the
@@ -152,6 +133,7 @@ typedef struct {
double root_delay;
double root_dispersion;
int stratum;
NTP_Leap leap;
} NTP_Sample;
#endif /* GOT_NTP_H */

View File

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

View File

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

View File

@@ -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,6 +43,7 @@
#include "util.h"
#include "conf.h"
#include "logging.h"
#include "keys.h"
#include "addrfilt.h"
#include "clientlog.h"
@@ -62,6 +63,16 @@ typedef enum {
MD_BURST_WAS_ONLINE, /* Burst sampling, return to online afterwards */
} 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 */
@@ -126,7 +137,9 @@ struct NCR_Instance_Record {
double offset_correction; /* Correction applied to measured offset
(e.g. for asymmetry in network delay) */
NAU_Instance auth; /* Authentication */
AuthenticationMode auth_mode; /* Authentication mode of our requests */
uint32_t auth_key_id; /* The ID of the authentication key to
use. */
/* Count of transmitted packets since last valid response */
unsigned int tx_count;
@@ -196,7 +209,6 @@ struct NCR_Instance_Record {
typedef struct {
NTP_Remote_Address addr;
NTP_Local_Address local_addr;
NAU_Instance auth;
int interval;
} BroadcastDestination;
@@ -241,6 +253,9 @@ static ARR_Instance broadcasts;
/* 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
@@ -296,7 +311,6 @@ static const char tss_chars[3] = {'D', 'K', 'H'};
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);
/* ================================================== */
@@ -401,10 +415,8 @@ NCR_Finalise(void)
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);
@@ -501,8 +513,7 @@ take_offline(NCR_Instance inst)
/* ================================================== */
NCR_Instance
NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, const char *name)
NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params)
{
NCR_Instance result;
@@ -559,23 +570,29 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
result->auto_offline = params->auto_offline;
result->poll_target = params->poll_target;
if (params->nts) {
IPSockAddr nts_address;
result->version = NTP_VERSION;
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;
result->auth = NAU_CreateNtsInstance(&nts_address, name, &result->remote_addr);
} else if (params->authkey != INACTIVE_AUTHKEY) {
result->auth = NAU_CreateSymmetricInstance(params->authkey);
if (params->authkey == INACTIVE_AUTHKEY) {
result->auth_mode = AUTH_NONE;
result->auth_key_id = 0;
} else {
result->auth = NAU_CreateNoneInstance();
}
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");
}
result->version = NAU_GetSuggestedNtpVersion(result->auth);
/* 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;
}
if (params->version)
result->version = CLAMP(NTP_MIN_COMPAT_VERSION, params->version, NTP_VERSION);
@@ -630,8 +647,6 @@ NCR_DestroyInstance(NCR_Instance instance)
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. */
@@ -699,15 +714,10 @@ NCR_ResetPoll(NCR_Instance instance)
/* ================================================== */
void
NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int ntp_only)
NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr)
{
memset(&inst->report, 0, sizeof (inst->report));
NCR_ResetInstance(inst);
/* Update the authentication-specific address before NTP address */
if (!ntp_only)
NAU_ChangeAddress(inst->auth, &remote_addr->ip_addr);
inst->remote_addr = *remote_addr;
if (inst->mode == MODE_CLIENT)
@@ -904,7 +914,8 @@ receive_timeout(void *arg)
{
NCR_Instance inst = (NCR_Instance)arg;
DEBUG_LOG("Receive timeout for %s", UTI_IPSockAddrToString(&inst->remote_addr));
DEBUG_LOG("Receive timeout for [%s:%d]",
UTI_IPToString(&inst->remote_addr.ip_addr), inst->remote_addr.port);
inst->rx_timeout_id = 0;
close_client_socket(inst);
@@ -917,8 +928,8 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
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 */
uint32_t kod, /* KoD code - 0 disabled */
NAU_Instance auth, /* The authentication to be used for the packet */
int auth_mode, /* The authentication mode */
uint32_t key_id, /* The authentication key ID */
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 */
@@ -929,16 +940,13 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
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_Packet *request, /* The received packet if responding */
NTP_PacketInfo *request_info /* and its info */
NTP_Local_Address *from /* From what address to send it */
)
{
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 */
@@ -948,8 +956,6 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
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;
@@ -1001,12 +1007,6 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
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 */
@@ -1057,9 +1057,6 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
}
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);
@@ -1071,28 +1068,42 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
if (smooth_time)
UTI_AddDoubleToTimespec(&local_transmit, smooth_offset, &local_transmit);
/* Pre-compensate the transmit time by approximately how long it will take
to generate the authentication data */
if (auth)
NAU_AdjustRequestTimestamp(auth, &local_transmit);
else
NAU_AdjustResponseTimestamp(request, request_info, &local_transmit);
length = NTP_NORMAL_PACKET_LENGTH;
UTI_TimespecToNtp64(interleaved ? &local_tx->ts : &local_transmit,
&message.transmit_ts, &ts_fuzz);
/* Authenticate the packet */
/* Generate the authentication data */
if (auth) {
if (!NAU_GenerateRequestAuth(auth, &message, &info)) {
DEBUG_LOG("Could not generate request auth");
return 0;
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);
}
} else {
if (!NAU_GenerateResponseAuth(request, request_info, &message, &info,
where_to, from, kod)) {
DEBUG_LOG("Could not generate response auth");
return 0;
}
UTI_TimespecToNtp64(interleaved ? &local_tx->ts : &local_transmit,
&message.transmit_ts, &ts_fuzz);
}
/* Do not send a packet with a non-zero transmit timestamp which is
@@ -1106,13 +1117,7 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
UTI_IsEqualAnyNtp64(&message.transmit_ts, &message.receive_ts,
&message.originate_ts, local_ntp_tx));
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;
}
ret = NIO_SendPacket(&message, where_to, from, info.length, local_tx != NULL);
ret = NIO_SendPacket(&message, where_to, from, length, local_tx != NULL);
if (local_tx) {
local_tx->ts = local_transmit;
@@ -1167,17 +1172,8 @@ transmit_timeout(void *arg)
return;
}
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;
}
DEBUG_LOG("Transmit timeout for [%s:%d]",
UTI_IPToString(&inst->remote_addr.ip_addr), inst->remote_addr.port);
/* Open new client socket */
if (inst->mode == MODE_CLIENT) {
@@ -1229,13 +1225,13 @@ transmit_timeout(void *arg)
}
/* Send the request (which may also be a response in the symmetric mode) */
sent = transmit_packet(inst->mode, interleaved, inst->local_poll, inst->version, 0,
inst->auth,
sent = transmit_packet(inst->mode, interleaved, inst->local_poll, inst->version,
inst->auth_mode, inst->auth_key_id,
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, NULL, NULL);
&inst->remote_addr, &local_addr);
++inst->tx_count;
if (sent)
@@ -1288,33 +1284,127 @@ transmit_timeout(void *arg)
/* ================================================== */
static int
parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info)
check_packet_format(NTP_Packet *message, int length)
{
if (length < NTP_HEADER_LENGTH || length % 4U != 0) {
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) {
DEBUG_LOG("NTP packet has invalid length %d", length);
return 0;
}
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;
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;
}
/* Parse authentication extension fields or MAC */
if (!NAU_ParsePacket(packet, info))
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;
}
}
/* 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;
}
/* 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;
}
return 0;
}
/* ================================================== */
static int
check_delay_ratio(NCR_Instance inst, SST_Stats stats,
struct timespec *sample_time, double delay)
@@ -1376,52 +1466,6 @@ check_delay_dev_ratio(NCR_Instance inst, SST_Stats stats,
/* ================================================== */
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 server socket is open, i.e. a client or peer can actually
be synchronised to us */
if (!NIO_IsServerSocketOpen())
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)
{
@@ -1469,16 +1513,17 @@ process_sample(NCR_Instance inst, NTP_Sample *sample)
/* ================================================== */
static int
process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, NTP_PacketInfo *info)
receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length)
{
NTP_Sample sample;
SST_Stats stats;
int pkt_leap, pkt_version;
uint32_t pkt_refid;
uint32_t pkt_refid, pkt_key_id;
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;
@@ -1503,6 +1548,8 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
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);
@@ -1533,8 +1580,14 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* 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 */
test5 = NAU_CheckResponseAuth(inst->auth, message, info);
/* 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 6 checks for unsynchronised server */
test6 = pkt_leap != LEAP_Unsynchronised &&
@@ -1665,9 +1718,10 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
the increase in delay */
testC = check_delay_dev_ratio(inst, stats, &sample.time, sample.offset, sample.peer_delay);
/* 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);
/* 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);
} else {
remote_interval = local_interval = response_time = 0.0;
sample.offset = sample.peer_delay = sample.peer_dispersion = 0.0;
@@ -1684,6 +1738,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
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
@@ -1773,7 +1828,6 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
inst->tx_count = 0;
SRC_UpdateReachability(inst->source, synced_packet);
SRC_SetLeapStatus(inst->source, pkt_leap);
if (good_packet) {
/* Adjust the polling interval, accumulate the sample, etc. */
@@ -1853,7 +1907,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
test5) << 1 | test6) << 1 | test7) << 1 |
testA) << 1 | testB) << 1 | testC) << 1 | testD;
inst->report.interleaved = interleaved_packet;
inst->report.authenticated = NAU_IsAuthEnabled(inst->auth);
inst->report.authenticated = inst->auth_mode != AUTH_NONE;
inst->report.tx_tss_char = tss_chars[local_transmit.source];
inst->report.rx_tss_char = tss_chars[local_receive.source];
@@ -1913,19 +1967,17 @@ int
NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length)
{
int proc_packet, proc_as_unknown;
NTP_PacketInfo info;
int pkt_mode, proc_packet, proc_as_unknown;
inst->report.total_rx_count++;
if (!parse_packet(message, length, &info))
if (!check_packet_format(message, length))
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 (info.mode) {
switch (pkt_mode) {
case MODE_ACTIVE:
switch (inst->mode) {
case MODE_ACTIVE:
@@ -2015,13 +2067,13 @@ NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
return 0;
}
return process_response(inst, local_addr, rx_ts, message, &info);
return receive_packet(inst, local_addr, rx_ts, message, length);
} 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 mode=%d our_mode=%u", (int)info.mode, inst->mode);
DEBUG_LOG("NTP packet discarded pkt_mode=%d our_mode=%u", pkt_mode, inst->mode);
return 0;
}
}
@@ -2034,12 +2086,12 @@ void
NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length)
{
NTP_PacketInfo info;
NTP_Mode my_mode;
NTP_Mode pkt_mode, my_mode;
NTP_int64 *local_ntp_rx, *local_ntp_tx;
NTP_Local_Timestamp local_tx, *tx_ts;
int log_index, interleaved, poll, version;
uint32_t kod;
int pkt_version, valid_auth, log_index, interleaved, poll;
AuthenticationMode auth_mode;
uint32_t key_id;
/* Ignore the packet if it wasn't received by server socket */
if (!NIO_IsServerSocket(local_addr->sock_fd)) {
@@ -2047,16 +2099,20 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
return;
}
if (!parse_packet(message, length, &info))
if (!check_packet_format(message, length))
return;
if (!ADF_IsAllowed(access_auth_table, &remote_addr->ip_addr)) {
DEBUG_LOG("NTP packet received from unauthorised host %s",
UTI_IPToString(&remote_addr->ip_addr));
DEBUG_LOG("NTP packet received from unauthorised host %s port %d",
UTI_IPToString(&remote_addr->ip_addr),
remote_addr->port);
return;
}
switch (info.mode) {
pkt_mode = NTP_LVM_TO_MODE(message->lvm);
pkt_version = NTP_LVM_TO_VERSION(message->lvm);
switch (pkt_mode) {
case MODE_ACTIVE:
/* We are symmetric passive, even though we don't ever lock to him */
my_mode = MODE_PASSIVE;
@@ -2069,18 +2125,17 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
/* 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 (info.version == 1 && remote_addr->port != NTP_PORT) {
if (pkt_version == 1 && remote_addr->port != NTP_PORT) {
my_mode = MODE_SERVER;
break;
}
/* Fall through */
default:
/* Discard */
DEBUG_LOG("NTP packet discarded mode=%d", (int)info.mode);
DEBUG_LOG("NTP packet discarded pkt_mode=%u", pkt_mode);
return;
}
kod = 0;
log_index = CLG_LogNTPAccess(&remote_addr->ip_addr, &rx_ts->ts);
/* Don't reply to all requests if the rate is excessive */
@@ -2089,24 +2144,25 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
return;
}
/* Check authentication */
if (!NAU_CheckRequestAuth(message, &info, &kod)) {
DEBUG_LOG("NTP packet failed auth mode=%d kod=%"PRIx32, (int)info.auth.mode, kod);
/* Check if the packet includes MAC that authenticates properly */
valid_auth = check_packet_auth(message, length, &auth_mode, &key_id);
/* Don't respond unless a non-zero KoD was returned */
if (kod == 0)
return;
/* 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;
}
}
/* If it is an NTPv4 packet with a long MAC and no extension fields,
respond with a NTPv3 packet to avoid breaking RFC 7822 and keep
the length symmetric. Otherwise, respond with the same version. */
if (info.version == 4 && info.ext_fields == 0 && info.auth.mode == NTP_AUTH_SYMMETRIC &&
info.auth.mac.length > NTP_MAX_V4_MAC_LENGTH)
version = 3;
else
version = info.version;
local_ntp_rx = local_ntp_tx = NULL;
tx_ts = NULL;
interleaved = 0;
@@ -2116,7 +2172,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
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 (kod == 0 && log_index >= 0) {
if (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) &&
@@ -2137,10 +2193,9 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
poll = MAX(poll, message->poll);
/* Send a reply */
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);
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);
/* Save the transmit timestamp */
if (tx_ts)
@@ -2185,13 +2240,15 @@ void
NCR_ProcessTxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length)
{
NTP_PacketInfo info;
NTP_Mode pkt_mode;
if (!parse_packet(message, length, &info))
if (!check_packet_format(message, length))
return;
pkt_mode = NTP_LVM_TO_MODE(message->lvm);
/* Server and passive mode packets are responses to unknown sources */
if (info.mode != MODE_CLIENT && info.mode != MODE_ACTIVE) {
if (pkt_mode != MODE_CLIENT && pkt_mode != MODE_ACTIVE) {
NCR_ProcessTxUnknown(&inst->remote_addr, local_addr, tx_ts, message, length);
return;
}
@@ -2208,20 +2265,19 @@ NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
{
NTP_int64 *local_ntp_rx, *local_ntp_tx;
NTP_Local_Timestamp local_tx;
NTP_PacketInfo info;
int log_index;
if (!parse_packet(message, length, &info))
if (!check_packet_format(message, length))
return;
if (info.mode == MODE_BROADCAST)
if (NTP_LVM_TO_MODE(message->lvm) == MODE_BROADCAST)
return;
log_index = CLG_GetClientIndex(&remote_addr->ip_addr);
if (log_index < 0)
return;
if (SMT_IsEnabled() && info.mode == MODE_SERVER)
if (SMT_IsEnabled() && NTP_LVM_TO_MODE(message->lvm) == 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);
@@ -2562,14 +2618,6 @@ int NCR_IsSyncPeer(NCR_Instance inst)
/* ================================================== */
void
NCR_DumpAuthData(NCR_Instance inst)
{
NAU_DumpData(inst->auth);
}
/* ================================================== */
static void
broadcast_timeout(void *arg)
{
@@ -2584,9 +2632,8 @@ broadcast_timeout(void *arg)
UTI_ZeroNtp64(&orig_ts);
zero_local_timestamp(&recv_ts);
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);
transmit_packet(MODE_BROADCAST, 0, poll, NTP_VERSION, 0, 0, &orig_ts, &orig_ts, &recv_ts,
NULL, NULL, NULL, &destination->addr, &destination->local_addr);
/* Requeue timeout. We don't care if interval drifts gradually. */
SCH_AddTimeoutInClass(destination->interval, get_separation(poll), SAMPLING_RANDOMNESS,
@@ -2607,7 +2654,6 @@ NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval)
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,

View File

@@ -59,8 +59,7 @@ extern void NCR_Initialise(void);
extern void NCR_Finalise(void);
/* Get a new instance for a server or peer */
extern NCR_Instance NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, const char *name);
extern NCR_Instance NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params);
/* Destroy an instance */
extern void NCR_DestroyInstance(NCR_Instance instance);
@@ -75,8 +74,7 @@ extern void NCR_ResetInstance(NCR_Instance inst);
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,
int ntp_only);
extern void NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr);
/* 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 */
@@ -136,8 +134,6 @@ extern uint32_t NCR_GetLocalRefid(NCR_Instance inst);
extern int NCR_IsSyncPeer(NCR_Instance instance);
extern void NCR_DumpAuthData(NCR_Instance inst);
extern void NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval);
#endif /* GOT_NTP_CORE_H */

192
ntp_ext.c
View File

@@ -1,192 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2019
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Functions for adding and parsing NTPv4 extension fields
*/
#include "config.h"
#include "sysincl.h"
#include "ntp_ext.h"
struct ExtFieldHeader {
uint16_t type;
uint16_t length;
};
/* ================================================== */
static int
format_field(unsigned char *buffer, int buffer_length, int start,
int type, int body_length, int *length, void **body)
{
struct ExtFieldHeader *header;
if (buffer_length < 0 || start < 0 || buffer_length <= start ||
buffer_length - start < sizeof (*header) || start % 4 != 0)
return 0;
header = (struct ExtFieldHeader *)(buffer + start);
if (body_length < 0 || sizeof (*header) + body_length > 0xffff ||
start + sizeof (*header) + body_length > buffer_length || body_length % 4 != 0)
return 0;
header->type = htons(type);
header->length = htons(sizeof (*header) + body_length);
*length = sizeof (*header) + body_length;
*body = header + 1;
return 1;
}
/* ================================================== */
int
NEF_SetField(unsigned char *buffer, int buffer_length, int start,
int type, void *body, int body_length, int *length)
{
void *ef_body;
if (!format_field(buffer, buffer_length, start, type, body_length, length, &ef_body))
return 0;
memcpy(ef_body, body, body_length);
return 1;
}
/* ================================================== */
int
NEF_AddBlankField(NTP_Packet *packet, NTP_PacketInfo *info, int type, int body_length, void **body)
{
int ef_length, length = info->length;
if (length < NTP_HEADER_LENGTH || length >= sizeof (*packet) || length % 4 != 0)
return 0;
/* Only NTPv4 packets can have extension fields */
if (info->version != 4)
return 0;
if (!format_field((unsigned char *)packet, sizeof (*packet), length,
type, body_length, &ef_length, body))
return 0;
if (ef_length < NTP_MIN_EF_LENGTH)
return 0;
info->length += ef_length;
info->ext_fields++;
return 1;
}
/* ================================================== */
int
NEF_AddField(NTP_Packet *packet, NTP_PacketInfo *info,
int type, void *body, int body_length)
{
void *ef_body;
if (!NEF_AddBlankField(packet, info, type, body_length, &ef_body))
return 0;
memcpy(ef_body, body, body_length);
return 1;
}
/* ================================================== */
int
NEF_ParseSingleField(unsigned char *buffer, int buffer_length, int start,
int *length, int *type, void **body, int *body_length)
{
struct ExtFieldHeader *header;
int ef_length;
if (buffer_length < 0 || start < 0 || buffer_length <= start ||
buffer_length - start < sizeof (*header))
return 0;
header = (struct ExtFieldHeader *)(buffer + start);
assert(sizeof (*header) == 4);
ef_length = ntohs(header->length);
if (ef_length < (int)(sizeof (*header)) || start + ef_length > buffer_length ||
ef_length % 4 != 0)
return 0;
if (length)
*length = ef_length;
if (type)
*type = ntohs(header->type);
if (body)
*body = header + 1;
if (body_length)
*body_length = ef_length - sizeof (*header);
return 1;
}
/* ================================================== */
int
NEF_ParseField(NTP_Packet *packet, int packet_length, int start,
int *length, int *type, void **body, int *body_length)
{
int ef_length;
if (packet_length <= NTP_HEADER_LENGTH || packet_length > sizeof (*packet) ||
packet_length <= start || packet_length % 4 != 0 ||
start < NTP_HEADER_LENGTH || start % 4 != 0)
return 0;
/* Only NTPv4 packets have extension fields */
if (NTP_LVM_TO_VERSION(packet->lvm) != 4)
return 0;
/* Check if the remaining data is a MAC. RFC 7822 specifies the maximum
length of a MAC in NTPv4 packets in order to enable deterministic
parsing. */
if (packet_length - start <= NTP_MAX_V4_MAC_LENGTH)
return 0;
if (!NEF_ParseSingleField((unsigned char *)packet, packet_length, start,
&ef_length, type, body, body_length))
return 0;
if (ef_length < NTP_MIN_EF_LENGTH)
return 0;
if (length)
*length = ef_length;
return 1;
}

View File

@@ -1,43 +0,0 @@
/*
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

648
ntp_io.c
View File

@@ -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,16 +46,54 @@
#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 server_sock_fd6;
static int client_sock_fd4;
#ifdef FEAT_IPV6
static int server_sock_fd6;
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 */
@@ -81,45 +119,162 @@ static void read_from_socket(int sock_fd, int event, void *anything);
/* ================================================== */
static int
open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr)
prepare_socket(int family, int port_number, int client_only)
{
int sock_fd, sock_flags, events = SCH_FILE_INPUT;
IPSockAddr local_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;
if (!SCK_IsFamilySupported(family))
return INVALID_SOCK_FD;
/* Open Internet domain UDP socket for NTP message transmissions */
if (!client_only)
CNF_GetBindAddress(family, &local_addr.ip_addr);
else
CNF_GetBindAcquisitionAddress(family, &local_addr.ip_addr);
sock_fd = socket(family, SOCK_DGRAM, 0);
if (local_addr.ip_addr.family != family)
SCK_GetAnyLocalIPAddress(family, &local_addr.ip_addr);
local_addr.port = local_port;
sock_flags = SCK_FLAG_RX_DEST_ADDR | SCK_FLAG_PRIV_BIND;
if (!client_only)
sock_flags |= SCK_FLAG_BROADCAST;
sock_fd = SCK_OpenUdpSocket(remote_addr, &local_addr, sock_flags);
if (sock_fd < 0) {
if (!client_only)
LOG(LOGS_ERR, "Could not open NTP socket on %s", UTI_IPSockAddrToString(&local_addr));
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));
}
return INVALID_SOCK_FD;
}
if (!client_only && family == IPADDR_INET4 && local_addr.port > 0)
bound_server_sock_fd4 = local_addr.ip_addr.addr.in4 != INADDR_ANY;
/* 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;
#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 */
}
/* Enable kernel/HW timestamping of packets */
#ifdef HAVE_LINUX_TIMESTAMPING
if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, client_only, &events))
#endif
if (!SCK_EnableKernelRxTimestamping(sock_fd))
#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
;
#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);
@@ -129,9 +284,40 @@ open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr
/* ================================================== */
static int
open_separate_client_socket(IPSockAddr *remote_addr)
prepare_separate_client_socket(int family)
{
return open_socket(remote_addr->ip_addr.family, 0, 1, remote_addr);
switch (family) {
case IPADDR_INET4:
return prepare_socket(AF_INET, 0, 1);
#ifdef FEAT_IPV6
case IPADDR_INET6:
return prepare_socket(AF_INET6, 0, 1);
#endif
default:
return INVALID_SOCK_FD;
}
}
/* ================================================== */
static int
connect_socket(int sock_fd, NTP_Remote_Address *remote_addr)
{
union sockaddr_in46 addr;
socklen_t addr_len;
addr_len = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port, &addr.u);
assert(addr_len);
if (connect(sock_fd, &addr.u, addr_len) < 0) {
DEBUG_LOG("Could not connect NTP socket to %s:%d : %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
strerror(errno));
return 0;
}
return 1;
}
/* ================================================== */
@@ -146,7 +332,33 @@ close_socket(int sock_fd)
NIO_Linux_NotifySocketClosing(sock_fd);
#endif
SCH_RemoveFileHandler(sock_fd);
SCK_CloseSocket(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;
}
}
/* ================================================== */
@@ -159,10 +371,6 @@ NIO_Initialise(int family)
assert(!initialised);
initialised = 1;
#ifdef PRIVOPS_BINDSOCKET
SCK_SetPrivBind(PRV_BindSocket);
#endif
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_Initialise();
#else
@@ -173,6 +381,12 @@ NIO_Initialise(int family)
}
#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();
@@ -185,38 +399,47 @@ NIO_Initialise(int family)
client_port == server_port);
server_sock_fd4 = INVALID_SOCK_FD;
server_sock_fd6 = INVALID_SOCK_FD;
client_sock_fd4 = INVALID_SOCK_FD;
client_sock_fd6 = INVALID_SOCK_FD;
server_sock_ref4 = 0;
#ifdef FEAT_IPV6
server_sock_fd6 = INVALID_SOCK_FD;
client_sock_fd6 = INVALID_SOCK_FD;
server_sock_ref6 = 0;
#endif
if (family == IPADDR_UNSPEC || family == IPADDR_INET4) {
if (permanent_server_sockets && server_port)
server_sock_fd4 = open_socket(IPADDR_INET4, server_port, 0, NULL);
server_sock_fd4 = prepare_socket(AF_INET, server_port, 0);
if (!separate_client_sockets) {
if (client_port != server_port || !server_port)
client_sock_fd4 = open_socket(IPADDR_INET4, client_port, 1, NULL);
client_sock_fd4 = prepare_socket(AF_INET, client_port, 1);
else
client_sock_fd4 = server_sock_fd4;
}
}
#ifdef FEAT_IPV6
if (family == IPADDR_UNSPEC || family == IPADDR_INET6) {
if (permanent_server_sockets && server_port)
server_sock_fd6 = open_socket(IPADDR_INET6, server_port, 0, NULL);
server_sock_fd6 = prepare_socket(AF_INET6, server_port, 0);
if (!separate_client_sockets) {
if (client_port != server_port || !server_port)
client_sock_fd6 = open_socket(IPADDR_INET6, client_port, 1, NULL);
client_sock_fd6 = prepare_socket(AF_INET6, client_port, 1);
else
client_sock_fd6 = server_sock_fd6;
}
}
#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)) {
if ((server_port && server_sock_fd4 == INVALID_SOCK_FD &&
permanent_server_sockets
#ifdef FEAT_IPV6
&& server_sock_fd6 == INVALID_SOCK_FD
#endif
) || (!separate_client_sockets && client_sock_fd4 == INVALID_SOCK_FD
#ifdef FEAT_IPV6
&& client_sock_fd6 == INVALID_SOCK_FD
#endif
)) {
LOG_FATAL("Could not open NTP sockets");
}
}
@@ -230,11 +453,14 @@ NIO_Finalise(void)
close_socket(client_sock_fd4);
close_socket(server_sock_fd4);
server_sock_fd4 = client_sock_fd4 = INVALID_SOCK_FD;
#ifdef FEAT_IPV6
if (server_sock_fd6 != client_sock_fd6)
close_socket(client_sock_fd6);
close_socket(server_sock_fd6);
server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD;
#endif
ARR_DestroyInstance(recv_headers);
ARR_DestroyInstance(recv_messages);
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_Finalise();
@@ -249,13 +475,25 @@ int
NIO_OpenClientSocket(NTP_Remote_Address *remote_addr)
{
if (separate_client_sockets) {
return open_separate_client_socket(remote_addr);
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;
} 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;
}
@@ -272,18 +510,20 @@ NIO_OpenServerSocket(NTP_Remote_Address *remote_addr)
if (permanent_server_sockets)
return server_sock_fd4;
if (server_sock_fd4 == INVALID_SOCK_FD)
server_sock_fd4 = open_socket(IPADDR_INET4, CNF_GetNTPPort(), 0, NULL);
server_sock_fd4 = prepare_socket(AF_INET, CNF_GetNTPPort(), 0);
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 = open_socket(IPADDR_INET6, CNF_GetNTPPort(), 0, NULL);
server_sock_fd6 = prepare_socket(AF_INET6, CNF_GetNTPPort(), 0);
if (server_sock_fd6 != INVALID_SOCK_FD)
server_sock_ref6++;
return server_sock_fd6;
#endif
default:
return INVALID_SOCK_FD;
}
@@ -311,12 +551,16 @@ NIO_CloseServerSocket(int sock_fd)
close_socket(server_sock_fd4);
server_sock_fd4 = INVALID_SOCK_FD;
}
} else if (sock_fd == server_sock_fd6) {
}
#ifdef FEAT_IPV6
else if (sock_fd == server_sock_fd6) {
if (--server_sock_ref6 <= 0) {
close_socket(server_sock_fd6);
server_sock_fd6 = INVALID_SOCK_FD;
}
} else {
}
#endif
else {
assert(0);
}
}
@@ -327,15 +571,11 @@ int
NIO_IsServerSocket(int sock_fd)
{
return sock_fd != INVALID_SOCK_FD &&
(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;
(sock_fd == server_sock_fd4
#ifdef FEAT_IPV6
|| sock_fd == server_sock_fd6
#endif
);
}
/* ================================================== */
@@ -343,60 +583,132 @@ NIO_IsServerSocketOpen(void)
int
NIO_IsServerConnectable(NTP_Remote_Address *remote_addr)
{
int sock_fd;
int sock_fd, r;
sock_fd = open_separate_client_socket(remote_addr);
sock_fd = prepare_separate_client_socket(remote_addr->ip_addr.family);
if (sock_fd == INVALID_SOCK_FD)
return 0;
r = connect_socket(sock_fd, remote_addr);
close_socket(sock_fd);
return 1;
return r;
}
/* ================================================== */
static void
process_message(SCK_Message *message, int sock_fd, int event)
process_message(struct msghdr *hdr, int length, int sock_fd)
{
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 (message->addr_type != SCK_ADDR_IP) {
DEBUG_LOG("Unexpected address type");
if (hdr->msg_namelen > sizeof (union sockaddr_in46)) {
DEBUG_LOG("Truncated source address");
return;
}
local_addr.ip_addr = message->local_addr.ip;
local_addr.if_index = message->if_index;;
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.sock_fd = sock_fd;
#ifdef HAVE_LINUX_TIMESTAMPING
if (NIO_Linux_ProcessMessage(message, &local_addr, &local_ts, event))
if (hdr->msg_flags & MSG_TRUNC) {
DEBUG_LOG("Received truncated message from %s:%d",
UTI_IPToString(&remote_addr.ip_addr), remote_addr.port);
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;
}
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
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);
#ifdef HAVE_IN6_PKTINFO
if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
struct in6_pktinfo ipi;
/* Just ignore the packet if it's not of a recognized length */
if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet)) {
DEBUG_LOG("Unexpected length");
return;
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
}
NSR_ProcessRx(&message->remote_addr.ip, &local_addr, &local_ts, message->data, message->length);
#ifdef HAVE_LINUX_TIMESTAMPING
if (NIO_Linux_ProcessMessage(&remote_addr, &local_addr, &local_ts, hdr, length))
return;
#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));
/* Just ignore the packet if it's not of a recognized length */
if (length < NTP_NORMAL_PACKET_LENGTH || length > sizeof (NTP_Receive_Buffer))
return;
NSR_ProcessRx(&remote_addr, &local_addr, &local_ts,
(NTP_Packet *)hdr->msg_iov[0].iov_base, length);
}
/* ================================================== */
@@ -404,28 +716,68 @@ process_message(SCK_Message *message, int sock_fd, int event)
static void
read_from_socket(int sock_fd, int event, void *anything)
{
SCK_Message *messages;
int i, received, flags = 0;
/* 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;
#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 |= SCK_FLAG_MSG_ERRQUEUE;
flags |= MSG_ERRQUEUE;
#else
assert(0);
#endif
}
messages = SCK_ReceiveMessages(sock_fd, flags, &received);
if (!messages)
return;
#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
for (i = 0; i < received; i++)
process_message(&messages[i], sock_fd, event);
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));
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);
}
/* ================================================== */
@@ -435,43 +787,123 @@ int
NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
NTP_Local_Address *local_addr, int length, int process_tx)
{
SCK_Message message;
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;
assert(initialised);
if (local_addr->sock_fd == INVALID_SOCK_FD) {
DEBUG_LOG("No socket to send to %s", UTI_IPSockAddrToString(remote_addr));
DEBUG_LOG("No socket to send to %s:%d",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port);
return 0;
}
SCK_InitMessage(&message, SCK_ADDR_IP);
message.data = packet;
message.length = length;
/* Specify remote address if the socket is not connected */
/* Don't set address with connected socket */
if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) {
message.remote_addr.ip.ip_addr = remote_addr->ip_addr;
message.remote_addr.ip.port = remote_addr->port;
addrlen = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port,
&remote.u);
if (!addrlen)
return 0;
}
message.if_index = local_addr->if_index;
message.local_addr.ip = local_addr->ip_addr;
if (addrlen) {
msg.msg_name = &remote.u;
msg.msg_namelen = addrlen;
} else {
msg.msg_name = NULL;
msg.msg_namelen = 0;
}
#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;
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;
}
#endif
#ifdef HAVE_LINUX_TIMESTAMPING
if (process_tx)
NIO_Linux_RequestTxTimestamp(&message, local_addr->sock_fd);
cmsglen = NIO_Linux_RequestTxTimestamp(&msg, cmsglen, local_addr->sock_fd);
#endif
if (!SCK_SendMessage(local_addr->sock_fd, &message, 0))
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));
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;
}

View File

@@ -53,9 +53,6 @@ extern void NIO_CloseServerSocket(int sock_fd);
/* Function to check if socket is a server socket */
extern int NIO_IsServerSocket(int sock_fd);
/* Function to check if a server socket is currently open */
extern int NIO_IsServerSocketOpen(void);
/* Function to check if client packets can be sent to a server */
extern int NIO_IsServerConnectable(NTP_Remote_Address *remote_addr);

View File

@@ -29,6 +29,7 @@
#include "sysincl.h"
#include <ifaddrs.h>
#include <linux/errqueue.h>
#include <linux/ethtool.h>
#include <linux/net_tstamp.h>
#include <linux/sockios.h>
@@ -44,10 +45,17 @@
#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;
@@ -125,7 +133,7 @@ add_interface(CNF_HwTsInterface *conf_iface)
return 1;
}
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
return 0;
@@ -134,13 +142,13 @@ add_interface(CNF_HwTsInterface *conf_iface)
if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", conf_iface->name) >=
sizeof (req.ifr_name)) {
SCK_CloseSocket(sock_fd);
close(sock_fd);
return 0;
}
if (ioctl(sock_fd, SIOCGIFINDEX, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCGIFINDEX", strerror(errno));
SCK_CloseSocket(sock_fd);
close(sock_fd);
return 0;
}
@@ -151,7 +159,7 @@ add_interface(CNF_HwTsInterface *conf_iface)
if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
SCK_CloseSocket(sock_fd);
close(sock_fd);
return 0;
}
@@ -159,13 +167,13 @@ add_interface(CNF_HwTsInterface *conf_iface)
SOF_TIMESTAMPING_RAW_HARDWARE;
if ((ts_info.so_timestamping & req_hwts_flags) != req_hwts_flags) {
DEBUG_LOG("HW timestamping not supported on %s", req.ifr_name);
SCK_CloseSocket(sock_fd);
close(sock_fd);
return 0;
}
if (ts_info.phc_index < 0) {
DEBUG_LOG("PHC missing on %s", req.ifr_name);
SCK_CloseSocket(sock_fd);
close(sock_fd);
return 0;
}
@@ -211,12 +219,12 @@ add_interface(CNF_HwTsInterface *conf_iface)
ts_config.tx_type != HWTSTAMP_TX_ON || ts_config.rx_filter != rx_filter)
#endif
{
SCK_CloseSocket(sock_fd);
close(sock_fd);
return 0;
}
}
SCK_CloseSocket(sock_fd);
close(sock_fd);
phc_fd = SYS_Linux_OpenPHC(NULL, ts_info.phc_index);
if (phc_fd < 0)
@@ -285,7 +293,7 @@ update_interface_speed(struct Interface *iface)
struct ifreq req;
int sock_fd, link_speed;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
return;
@@ -298,11 +306,11 @@ update_interface_speed(struct Interface *iface)
if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
SCK_CloseSocket(sock_fd);
close(sock_fd);
return;
}
SCK_CloseSocket(sock_fd);
close(sock_fd);
link_speed = ethtool_cmd_speed(&cmd);
@@ -320,16 +328,17 @@ check_timestamping_option(int option)
{
int sock_fd;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
return 0;
if (!SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, option)) {
SCK_CloseSocket(sock_fd);
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);
return 0;
}
SCK_CloseSocket(sock_fd);
close(sock_fd);
return 1;
}
#endif
@@ -341,15 +350,19 @@ open_dummy_socket(void)
{
int sock_fd, events = 0;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
if (sock_fd < 0)
if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0
#ifdef FEAT_IPV6
&& (sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0
#endif
)
return INVALID_SOCK_FD;
if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, 1, &events)) {
SCK_CloseSocket(sock_fd);
close(sock_fd);
return INVALID_SOCK_FD;
}
UTI_FdSetCloexec(sock_fd);
return sock_fd;
}
@@ -419,7 +432,7 @@ NIO_Linux_Finalise(void)
unsigned int i;
if (dummy_rxts_socket != INVALID_SOCK_FD)
SCK_CloseSocket(dummy_rxts_socket);
close(dummy_rxts_socket);
for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i);
@@ -449,12 +462,14 @@ NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
if (client_only || permanent_ts_options)
flags |= ts_tx_flags;
if (!SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, val)) {
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");
ts_flags = 0;
return 0;
}
if (!SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, flags)) {
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof (flags)) < 0) {
LOG(LOGS_ERR, "Could not set %s socket option", "SO_TIMESTAMPING");
ts_flags = 0;
return 0;
}
@@ -618,6 +633,7 @@ static int
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;
@@ -640,21 +656,19 @@ extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
/* Parse destination address and port from IPv4/IPv6 and UDP headers */
if (len >= 20 && msg[0] >> 4 == 4) {
int ihl = (msg[0] & 0xf) * 4;
uint32_t addr;
if (len < ihl + 8 || msg[9] != 17)
return 0;
memcpy(&addr, msg + 16, sizeof (addr));
remote_addr->ip_addr.addr.in4 = ntohl(addr);
remote_addr->port = ntohs(*(uint16_t *)(msg + ihl + 2));
remote_addr->ip_addr.family = IPADDR_INET4;
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;
len -= ihl + 8, msg += ihl + 8;
#ifdef FEAT_IPV6
} else if (len >= 48 && msg[0] >> 4 == 6) {
int eh_len, next_header = msg[6];
memcpy(&remote_addr->ip_addr.addr.in6, msg + 24, sizeof (remote_addr->ip_addr.addr.in6));
memcpy(&addr.in6.sin6_addr.s6_addr, msg + 24, 16);
len -= 40, msg += 40;
/* Skip IPv6 extension headers if present */
@@ -686,14 +700,16 @@ extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
len -= eh_len, msg += eh_len;
}
remote_addr->port = ntohs(*(uint16_t *)(msg + 2));
remote_addr->ip_addr.family = IPADDR_INET6;
addr.in6.sin6_port = *(uint16_t *)(msg + 2);
addr.in6.sin6_family = AF_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);
@@ -704,39 +720,72 @@ extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
/* ================================================== */
int
NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, int event)
NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length)
{
struct Interface *iface;
struct cmsghdr *cmsg;
int is_tx, ts_if_index, l2_length;
is_tx = event == SCH_FILE_EXCEPTION;
is_tx = hdr->msg_flags & MSG_ERRQUEUE;
iface = NULL;
ts_if_index = local_addr->if_index;
l2_length = 0;
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;
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;
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);
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);
}
#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;
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;
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 the kernel is slow with enabling RX timestamping, open a dummy
@@ -754,19 +803,19 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
/* The data from the error queue includes all layers up to UDP. We have to
extract the UDP data and also the destination address with port as there
currently doesn't seem to be a better way to get them both. */
l2_length = message->length;
message->length = extract_udp_data(message->data, &message->remote_addr.ip, message->length);
l2_length = length;
length = extract_udp_data(hdr->msg_iov[0].iov_base, remote_addr, length);
DEBUG_LOG("Extracted message for %s fd=%d len=%u",
UTI_IPSockAddrToString(&message->remote_addr.ip),
local_addr->sock_fd, 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);
/* Update assumed position of UDP data at layer 2 for next received packet */
if (iface && message->length) {
if (message->remote_addr.ip.ip_addr.family == IPADDR_INET4)
iface->l2_udp4_ntp_start = l2_length - message->length;
else if (message->remote_addr.ip.ip_addr.family == IPADDR_INET6)
iface->l2_udp6_ntp_start = l2_length - message->length;
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;
}
/* Drop the message if it has no timestamp or its processing failed */
@@ -775,21 +824,24 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
return 1;
}
if (message->length < NTP_HEADER_LENGTH)
if (length < NTP_NORMAL_PACKET_LENGTH)
return 1;
NSR_ProcessTx(&message->remote_addr.ip, local_addr, local_ts, message->data, message->length);
NSR_ProcessTx(remote_addr, local_addr, local_ts,
(NTP_Packet *)hdr->msg_iov[0].iov_base, length);
return 1;
}
/* ================================================== */
void
NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd)
int
NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
{
struct cmsghdr *cmsg;
if (!ts_flags)
return;
return cmsglen;
/* 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
@@ -799,9 +851,27 @@ NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd)
/* Check if TX timestamping is disabled on this socket */
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
return;
return cmsglen;
message->timestamp.tx_flags = ts_tx_flags;
/* 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 cmsglen;
}
/* ================================================== */

View File

@@ -27,8 +27,6 @@
#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);
@@ -37,10 +35,10 @@ extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int
extern int NIO_Linux_ProcessEvent(int sock_fd, int event);
extern int NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, int event);
extern 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 void NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd);
extern int NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd);
extern void NIO_Linux_NotifySocketClosing(int sock_fd);

View File

@@ -34,7 +34,6 @@
#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 */
@@ -91,7 +90,7 @@ static ARR_Instance queue;
static unsigned int queue_head;
static unsigned int queue_tail;
#define INVALID_SOCK_FD (-6)
#define INVALID_SOCK_FD -1
/* Unix domain socket connected to ntp_signd */
static int sock_fd;
@@ -117,7 +116,7 @@ static void
close_socket(void)
{
SCH_RemoveFileHandler(sock_fd);
SCK_CloseSocket(sock_fd);
close(sock_fd);
sock_fd = INVALID_SOCK_FD;
/* Empty the queue */
@@ -129,24 +128,36 @@ close_socket(void)
static int
open_socket(void)
{
char path[PATH_MAX];
struct sockaddr_un s;
if (sock_fd != INVALID_SOCK_FD)
if (sock_fd >= 0)
return 1;
if (snprintf(path, sizeof (path), "%s/socket", CNF_GetNtpSigndSocket()) >= sizeof (path)) {
DEBUG_LOG("signd socket path too long");
return 0;
}
sock_fd = SCK_OpenUnixStreamSocket(path, NULL, 0);
sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock_fd < 0) {
sock_fd = INVALID_SOCK_FD;
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)) {
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();
return 0;
}
DEBUG_LOG("Connected to signd");
return 1;
}
@@ -207,14 +218,16 @@ read_write_socket(int sock_fd, int event, void *anything)
if (!inst->sent)
SCH_GetLastEventTime(NULL, NULL, &inst->request_ts);
s = SCK_Send(sock_fd, (char *)&inst->request + inst->sent,
inst->request_length - inst->sent, 0);
s = 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 */
@@ -233,14 +246,20 @@ read_write_socket(int sock_fd, int event, void *anything)
}
assert(inst->received < sizeof (inst->response));
s = SCK_Receive(sock_fd, (char *)&inst->response + inst->received,
sizeof (inst->response) - inst->received, 0);
s = recv(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))
@@ -309,8 +328,7 @@ extern int NSD_GetAuthDelay(uint32_t key_id)
/* ================================================== */
int
NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
{
SignInstance *inst;
@@ -324,7 +342,7 @@ NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
return 0;
}
if (info->length != NTP_HEADER_LENGTH) {
if (length != NTP_NORMAL_PACKET_LENGTH) {
DEBUG_LOG("Invalid packet length");
return 0;
}
@@ -337,7 +355,7 @@ NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
inst->local_addr = *local_addr;
inst->sent = 0;
inst->received = 0;
inst->request_length = offsetof(SigndRequest, packet_to_sign) + info->length;
inst->request_length = offsetof(SigndRequest, packet_to_sign) + length;
/* The length field doesn't include itself */
inst->request.length = htonl(inst->request_length - sizeof (inst->request.length));
@@ -347,7 +365,7 @@ NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
inst->request._pad = 0;
inst->request.key_id = htonl(key_id);
memcpy(&inst->request.packet_to_sign, packet, info->length);
memcpy(&inst->request.packet_to_sign, packet, length);
/* Enable output if there was no pending request */
if (IS_QUEUE_EMPTY())

View File

@@ -39,7 +39,6 @@ extern void NSD_Finalise(void);
extern int NSD_GetAuthDelay(uint32_t key_id);
/* Function to sign an NTP packet and send it */
extern int NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr);
extern int NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
#endif

View File

@@ -34,7 +34,6 @@
#include "array.h"
#include "ntp_sources.h"
#include "ntp_core.h"
#include "ntp_io.h"
#include "util.h"
#include "logging.h"
#include "local.h"
@@ -49,9 +48,7 @@
particular sources */
typedef struct {
NTP_Remote_Address *remote_addr; /* The address of this source, non-NULL
means this slot in table is in use
(an IPADDR_ID address means the address
is not resolved yet) */
means this slot in table is in use */
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
@@ -70,21 +67,21 @@ static int n_sources;
/* Flag indicating new sources will be started automatically when added */
static int auto_start_sources = 0;
/* Last assigned address ID */
static uint32_t last_address_id = 0;
/* Source scheduled for name resolving (first resolving or replacement) */
/* Source with unknown address (which may be resolved later) */
struct UnresolvedSource {
/* Current address of the source (IDADDR_ID is used for a single source
with unknown address and IPADDR_UNSPEC for a pool of sources */
NTP_Remote_Address address;
/* ID of the pool if not a single source */
int pool;
/* Name to be resolved */
char *name;
/* Flag indicating addresses should be used in a random order */
int port;
int random_order;
/* Next unresolved source in the list */
int replacement;
union {
struct {
NTP_Source_Type type;
SourceParameters params;
int pool;
int max_new_sources;
} new_source;
NTP_Remote_Address replace_source;
};
struct UnresolvedSource *next;
};
@@ -104,13 +101,9 @@ static NSR_SourceResolvingEndHandler resolving_end_handler = NULL;
/* Pool of sources with the same name */
struct SourcePool {
/* Number of all sources from the pool */
/* Number of sources added from this pool (ignoring tentative sources) */
int sources;
/* Number of sources with unresolved address */
int unresolved_sources;
/* Number of non-tentative sources */
int confirmed_sources;
/* Maximum number of confirmed sources */
/* Maximum number of sources */
int max_sources;
};
@@ -120,11 +113,9 @@ static ARR_Instance pools;
/* ================================================== */
/* Forward prototypes */
static void resolve_sources(void);
static void resolve_sources(void *arg);
static void rehash_records(void);
static void clean_source_record(SourceRecord *record);
static void remove_pool_sources(int pool, int tentative, int unresolved);
static void remove_unresolved_source(struct UnresolvedSource *us);
static void
slew_sources(struct timespec *raw,
@@ -149,14 +140,6 @@ get_record(unsigned index)
/* ================================================== */
static struct SourcePool *
get_pool(unsigned index)
{
return (struct SourcePool *)ARR_GetElement(pools, index);
}
/* ================================================== */
void
NSR_Initialise(void)
{
@@ -177,8 +160,11 @@ void
NSR_Finalise(void)
{
SourceRecord *record;
struct UnresolvedSource *us;
unsigned int i;
ARR_DestroyInstance(pools);
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (record->remote_addr)
@@ -186,10 +172,13 @@ NSR_Finalise(void)
}
ARR_DestroyInstance(records);
ARR_DestroyInstance(pools);
while (unresolved_sources)
remove_unresolved_source(unresolved_sources);
while (unresolved_sources) {
us = unresolved_sources;
unresolved_sources = us->next;
Free(us->name);
Free(us);
}
initialised = 0;
}
@@ -222,8 +211,7 @@ find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found)
*found = 0;
if (remote_addr->ip_addr.family != IPADDR_INET4 &&
remote_addr->ip_addr.family != IPADDR_INET6 &&
remote_addr->ip_addr.family != IPADDR_ID)
remote_addr->ip_addr.family != IPADDR_INET6)
return;
hash = UTI_IPToHash(&remote_addr->ip_addr);
@@ -307,8 +295,7 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type, So
return NSR_AlreadyInUse;
} else {
if (remote_addr->ip_addr.family != IPADDR_INET4 &&
remote_addr->ip_addr.family != IPADDR_INET6 &&
remote_addr->ip_addr.family != IPADDR_ID) {
remote_addr->ip_addr.family != IPADDR_INET6) {
return NSR_InvalidAF;
} else {
n_sources++;
@@ -320,19 +307,13 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type, So
assert(!found);
record = get_record(slot);
record->data = NCR_CreateInstance(remote_addr, type, params, name);
record->data = NCR_GetInstance(remote_addr, type, params);
record->remote_addr = NCR_GetRemoteAddress(record->data);
record->name = name ? Strdup(name) : NULL;
record->pool = pool;
record->tentative = 1;
if (record->pool != INVALID_POOL) {
get_pool(record->pool)->sources++;
if (!UTI_IsIPReal(&remote_addr->ip_addr))
get_pool(record->pool)->unresolved_sources++;
}
if (auto_start_sources && UTI_IsIPReal(&remote_addr->ip_addr))
if (auto_start_sources)
NCR_StartInstance(record->data);
return NSR_Success;
@@ -343,109 +324,70 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type, So
/* ================================================== */
static NSR_Status
change_source_address(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr,
int replacement)
replace_source(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr)
{
int slot1, slot2, found;
SourceRecord *record;
LOG_Severity severity;
char *name;
struct SourcePool *pool;
find_slot(old_addr, &slot1, &found);
if (!found)
return NSR_NoSuchSource;
/* Make sure there is no other source using the new address (with the same
or different port), but allow a source to have its port changed */
find_slot(new_addr, &slot2, &found);
if (found == 2 || (found != 0 && slot1 != slot2))
if (found)
return NSR_AlreadyInUse;
record = get_record(slot1);
NCR_ChangeRemoteAddress(record->data, new_addr, !replacement);
NCR_ChangeRemoteAddress(record->data, new_addr);
record->remote_addr = NCR_GetRemoteAddress(record->data);
if (!UTI_IsIPReal(&old_addr->ip_addr) && UTI_IsIPReal(&new_addr->ip_addr)) {
if (auto_start_sources)
NCR_StartInstance(record->data);
if (record->pool != INVALID_POOL)
get_pool(record->pool)->unresolved_sources--;
}
if (!record->tentative) {
record->tentative = 1;
if (record->pool != INVALID_POOL)
get_pool(record->pool)->confirmed_sources--;
if (record->pool != INVALID_POOL) {
pool = ARR_GetElement(pools, record->pool);
pool->sources--;
}
}
name = record->name;
severity = UTI_IsIPReal(&old_addr->ip_addr) ? LOGS_INFO : LOGS_DEBUG;
/* The hash table must be rebuilt for the new address */
rehash_records();
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 ? name : "");
} else {
LOG(severity, "Source %s (%s) changed port to %d",
UTI_IPToString(&new_addr->ip_addr), name ? name : "", new_addr->port);
}
LOG(LOGS_INFO, "Source %s replaced with %s",
UTI_IPToString(&old_addr->ip_addr),
UTI_IPToString(&new_addr->ip_addr));
return NSR_Success;
}
/* ================================================== */
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;
return 1;
}
/* ================================================== */
static void
process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs)
{
NTP_Remote_Address old_addr, new_addr;
SourceRecord *record;
NTP_Remote_Address address;
int i, added;
unsigned short first = 0;
int i, j;
if (us->random_order)
UTI_GetRandomBytes(&first, sizeof (first));
for (i = 0; i < n_addrs; i++) {
new_addr.ip_addr = ip_addrs[((unsigned int)i + first) % n_addrs];
for (i = added = 0; i < n_addrs; i++) {
address.ip_addr = ip_addrs[((unsigned int)i + first) % n_addrs];
address.port = us->port;
DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&new_addr.ip_addr));
DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&address.ip_addr));
if (us->pool != INVALID_POOL) {
/* In the pool resolving mode, try to replace all sources from
the pool which don't have a real address yet */
for (j = 0; j < ARR_GetSize(records); j++) {
record = get_record(j);
if (!record->remote_addr || record->pool != us->pool ||
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;
}
if (us->replacement) {
if (replace_source(&us->replace_source, &address) != NSR_AlreadyInUse)
break;
} else {
new_addr.port = us->address.port;
if (replace_source_connectable(&us->address, &new_addr))
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)
break;
}
}
@@ -453,41 +395,14 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
/* ================================================== */
static int
is_resolved(struct UnresolvedSource *us)
{
int slot, found;
if (us->pool != INVALID_POOL) {
return get_pool(us->pool)->unresolved_sources <= 0;
} else {
/* If the address is no longer present, it was removed or replaced
(i.e. resolved) */
find_slot(&us->address, &slot, &found);
return !found;
}
}
/* ================================================== */
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, *next;
struct UnresolvedSource *us, **i, *next;
us = (struct UnresolvedSource *)anything;
assert(us == resolving_source);
assert(resolving_id == 0);
DEBUG_LOG("%s resolved to %d addrs", us->name, n_addrs);
@@ -506,10 +421,18 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
next = us->next;
/* 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);
/* 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;
}
}
}
resolving_source = next;
@@ -521,10 +444,12 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
/* This was the last source in the list. If some sources couldn't
be resolved, try again in exponentially increasing interval. */
if (unresolved_sources) {
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);
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);
} else {
resolving_interval = 0;
}
@@ -538,22 +463,12 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
/* ================================================== */
static void
resolve_sources(void)
resolve_sources(void *arg)
{
struct UnresolvedSource *us, *next, *i;
struct UnresolvedSource *us;
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
@@ -580,23 +495,6 @@ append_unresolved_source(struct UnresolvedSource *us)
/* ================================================== */
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;
}
}
}
/* ================================================== */
NSR_Status
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params)
{
@@ -605,60 +503,41 @@ NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParam
/* ================================================== */
NSR_Status
void
NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, SourceParameters *params)
{
struct UnresolvedSource *us;
struct SourcePool *sp;
NTP_Remote_Address remote_addr;
int i, new_sources;
/* If the name is an IP address, don't bother with full resolving now
or later when trying to replace the source */
if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
remote_addr.port = port;
return NSR_AddSource(&remote_addr, type, params);
}
/* Make sure the name is at least printable and has no spaces */
for (i = 0; name[i] != '\0'; i++) {
if (!isgraph(name[i]))
return NSR_InvalidName;
NSR_AddSource(&remote_addr, type, params);
return;
}
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(name);
us->port = port;
us->random_order = 0;
remote_addr.ip_addr.family = IPADDR_ID;
remote_addr.ip_addr.addr.id = ++last_address_id;
remote_addr.port = port;
us->replacement = 0;
us->new_source.type = type;
us->new_source.params = *params;
if (!pool) {
us->pool = INVALID_POOL;
us->address = remote_addr;
new_sources = 1;
us->new_source.pool = INVALID_POOL;
us->new_source.max_new_sources = 1;
} else {
sp = (struct SourcePool *)ARR_GetNewElement(pools);
sp->sources = 0;
sp->unresolved_sources = 0;
sp->confirmed_sources = 0;
sp->max_sources = CLAMP(1, params->max_sources, MAX_POOL_SOURCES);
us->pool = ARR_GetSize(pools) - 1;
us->address.ip_addr.family = IPADDR_UNSPEC;
new_sources = MIN(2 * sp->max_sources, MAX_POOL_SOURCES);
sp->max_sources = params->max_sources;
us->new_source.pool = ARR_GetSize(pools) - 1;
us->new_source.max_new_sources = MAX_POOL_SOURCES;
}
append_unresolved_source(us);
for (i = 0; i < new_sources; i++) {
if (i > 0)
remote_addr.ip_addr.addr.id = ++last_address_id;
if (add_source(&remote_addr, name, type, params, us->pool) != NSR_Success)
return NSR_TooManySources;
}
return NSR_UnresolvedName;
}
/* ================================================== */
@@ -678,12 +557,11 @@ NSR_ResolveSources(void)
if (unresolved_sources) {
/* Make sure no resolving is currently running */
if (!resolving_source) {
if (resolving_id != 0) {
if (resolving_interval) {
SCH_RemoveTimeout(resolving_id);
resolving_id = 0;
resolving_interval--;
}
resolve_sources();
resolve_sources(NULL);
}
} else {
/* No unresolved sources, we are done */
@@ -696,12 +574,10 @@ NSR_ResolveSources(void)
void NSR_StartSources(void)
{
NTP_Remote_Address *addr;
unsigned int i;
for (i = 0; i < ARR_GetSize(records); i++) {
addr = get_record(i)->remote_addr;
if (!addr || !UTI_IsIPReal(&addr->ip_addr))
if (!get_record(i)->remote_addr)
continue;
NCR_StartInstance(get_record(i)->data);
}
@@ -720,19 +596,6 @@ static void
clean_source_record(SourceRecord *record)
{
assert(record->remote_addr);
if (record->pool != INVALID_POOL) {
struct SourcePool *pool = get_pool(record->pool);
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)
@@ -798,13 +661,14 @@ resolve_source_replacement(SourceRecord *record)
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->pool = INVALID_POOL;
us->address = *record->remote_addr;
us->replacement = 1;
us->replace_source = *record->remote_addr;
append_unresolved_source(us);
NSR_ResolveSources();
@@ -866,18 +730,7 @@ NSR_RefreshAddresses(void)
/* ================================================== */
NSR_Status
NSR_UpdateSourceNtpAddress(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr)
{
if (new_addr->ip_addr.family == IPADDR_UNSPEC)
return NSR_InvalidAF;
return change_source_address(old_addr, new_addr, 0);
}
/* ================================================== */
static void remove_pool_sources(int pool, int tentative, int unresolved)
static void remove_tentative_pool_sources(int pool)
{
SourceRecord *record;
unsigned int i, removed;
@@ -885,14 +738,10 @@ static void remove_pool_sources(int pool, int tentative, int unresolved)
for (i = removed = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (!record->remote_addr || record->pool != pool)
if (!record->remote_addr || record->pool != pool || !record->tentative)
continue;
if ((tentative && !record->tentative) ||
(unresolved && UTI_IsIPReal(&record->remote_addr->ip_addr)))
continue;
DEBUG_LOG("removing %ssource %s", tentative ? "tentative " : "",
DEBUG_LOG("removing tentative source %s",
UTI_IPToString(&record->remote_addr->ip_addr));
clean_source_record(record);
@@ -923,29 +772,6 @@ NSR_GetLocalRefid(IPAddr *address)
/* ================================================== */
char *
NSR_GetName(IPAddr *address)
{
NTP_Remote_Address remote_addr;
int slot, found;
SourceRecord *record;
remote_addr.ip_addr = *address;
remote_addr.port = 0;
find_slot(&remote_addr, &slot, &found);
if (!found)
return NULL;
record = get_record(slot);
if (record->name)
return record->name;
return UTI_IPToString(&record->remote_addr->ip_addr);
}
/* ================================================== */
/* This routine is called by ntp_io when a new packet arrives off the network,
possibly with an authentication tail */
void
@@ -970,15 +796,15 @@ NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
record->tentative = 0;
if (record->pool != INVALID_POOL) {
pool = get_pool(record->pool);
pool->confirmed_sources++;
pool = ARR_GetElement(pools, record->pool);
pool->sources++;
DEBUG_LOG("pool %s has %d confirmed sources", record->name, pool->confirmed_sources);
DEBUG_LOG("pool %s has %d confirmed sources", record->name, pool->sources);
/* If the number of sources from the pool reached the configured
maximum, remove the remaining tentative sources */
if (pool->confirmed_sources >= pool->max_sources)
remove_pool_sources(record->pool, 1, 0);
if (pool->sources >= pool->max_sources)
remove_tentative_pool_sources(record->pool);
}
}
} else {
@@ -1047,10 +873,7 @@ NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (record->remote_addr) {
/* 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))) ||
if (address->family == IPADDR_UNSPEC ||
!UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) {
any = 1;
if (NCR_IsSyncPeer(record->data)) {
@@ -1066,6 +889,17 @@ NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity
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;
}
@@ -1276,39 +1110,28 @@ NSR_GetActivityReport(RPT_ActivityReport *report)
{
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)
continue;
if (!UTI_IsIPReal(&record->remote_addr->ip_addr)) {
report->unresolved++;
} else {
if (record->remote_addr) {
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);
}
}

View File

@@ -44,9 +44,7 @@ typedef enum {
NSR_NoSuchSource, /* Remove - attempt to remove a source that is not known */
NSR_AlreadyInUse, /* AddSource - attempt to add a source that is already known */
NSR_TooManySources, /* AddSource - too many sources already present */
NSR_InvalidAF, /* AddSource - attempt to add a source with invalid address family */
NSR_InvalidName, /* AddSourceByName - attempt to add a source with invalid name */
NSR_UnresolvedName, /* AddSourceByName - name will be resolved later */
NSR_InvalidAF /* AddSource - attempt to add a source with invalid address family */
} NSR_Status;
/* Procedure to add a new server or peer source. */
@@ -54,10 +52,8 @@ extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type
/* Procedure to add a new server, peer source, or pool of servers specified by
name instead of address. The name is resolved in exponentially increasing
intervals until it succeeds or fails with a non-temporary error. 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);
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);
/* Function type for handlers to be called back when an attempt
* (possibly unsuccessful) to resolve unresolved sources ends */
@@ -87,17 +83,9 @@ extern void NSR_HandleBadSource(IPAddr *address);
/* Procedure to resolve all names again */
extern void NSR_RefreshAddresses(void);
/* Procedure to update the address of a source */
extern NSR_Status NSR_UpdateSourceNtpAddress(NTP_Remote_Address *old_addr,
NTP_Remote_Address *new_addr);
/* Procedure to get local reference ID corresponding to a source */
extern uint32_t NSR_GetLocalRefid(IPAddr *address);
/* Procedure to get the name of a source. If the source doesn't have a name,
it returns a temporary string containing formatted address. */
extern char *NSR_GetName(IPAddr *address);
/* This routine is called by ntp_io when a new packet arrives off the network */
extern void NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length);
@@ -140,6 +128,4 @@ extern int NSR_GetNTPReport(RPT_NTPReport *report);
extern void NSR_GetActivityReport(RPT_ActivityReport *report);
extern void NSR_DumpAuthData(void);
#endif /* GOT_NTP_SOURCES_H */

View File

@@ -1,79 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header file for the NTS Key Establishment protocol
*/
#ifndef GOT_NTS_KE_H
#define GOT_NTS_KE_H
#include "siv.h"
#define NKE_RECORD_CRITICAL_BIT (1U << 15)
#define NKE_RECORD_END_OF_MESSAGE 0
#define NKE_RECORD_NEXT_PROTOCOL 1
#define NKE_RECORD_ERROR 2
#define NKE_RECORD_WARNING 3
#define NKE_RECORD_AEAD_ALGORITHM 4
#define NKE_RECORD_COOKIE 5
#define NKE_RECORD_NTPV4_SERVER_NEGOTIATION 6
#define NKE_RECORD_NTPV4_PORT_NEGOTIATION 7
#define NKE_NEXT_PROTOCOL_NTPV4 0
#define NKE_ERROR_UNRECOGNIZED_CRITICAL_RECORD 0
#define NKE_ERROR_BAD_REQUEST 1
#define NKE_ERROR_INTERNAL_SERVER_ERROR 2
#define NKE_ALPN_NAME "ntske/1"
#define NKE_EXPORTER_LABEL "EXPORTER-network-time-security"
#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

View File

@@ -1,400 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
NTS-KE client
*/
#include "config.h"
#include "sysincl.h"
#include "nts_ke_client.h"
#include "conf.h"
#include "logging.h"
#include "memory.h"
#include "nameserv_async.h"
#include "nts_ke_session.h"
#include "siv.h"
#include "socket.h"
#include "util.h"
#define CLIENT_TIMEOUT 16.0
struct NKC_Instance_Record {
char *name;
IPSockAddr address;
NKSN_Instance session;
int destroying;
int got_response;
int resolving_name;
NKE_Context context;
NKE_Cookie cookies[NKE_MAX_COOKIES];
int num_cookies;
char server_name[NKE_MAX_RECORD_BODY_LENGTH + 1];
IPSockAddr ntp_address;
};
/* ================================================== */
static void *client_credentials;
/* ================================================== */
static void
name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *arg)
{
NKC_Instance inst = arg;
int i;
inst->resolving_name = 0;
if (inst->destroying) {
NKC_DestroyInstance(inst);
return;
}
if (status != DNS_Success || n_addrs < 1) {
LOG(LOGS_ERR, "Could not resolve NTP server %s from %s", inst->server_name, inst->name);
/* Force restart */
inst->got_response = 0;
return;
}
inst->ntp_address.ip_addr = ip_addrs[0];
/* Prefer an address of the same family as NTS-KE */
for (i = 0; i < n_addrs; i++) {
DEBUG_LOG("%s resolved to %s", inst->server_name, UTI_IPToString(&ip_addrs[i]));
if (ip_addrs[i].family == inst->address.ip_addr.family) {
inst->ntp_address.ip_addr = ip_addrs[i];
break;
}
}
}
/* ================================================== */
static int
prepare_request(NKC_Instance inst)
{
NKSN_Instance session = inst->session;
uint16_t datum;
NKSN_BeginMessage(session);
datum = htons(NKE_NEXT_PROTOCOL_NTPV4);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum)))
return 0;
datum = htons(AEAD_AES_SIV_CMAC_256);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
return 0;
if (!NKSN_EndMessage(session))
return 0;
return 1;
}
/* ================================================== */
static int
process_response(NKC_Instance inst)
{
int next_protocol = -1, aead_algorithm = -1, error = 0;
int i, critical, type, length;
uint16_t data[NKE_MAX_COOKIE_LENGTH / sizeof (uint16_t)];
assert(NKE_MAX_COOKIE_LENGTH % sizeof (uint16_t) == 0);
assert(sizeof (uint16_t) == 2);
inst->num_cookies = 0;
inst->ntp_address.ip_addr.family = IPADDR_UNSPEC;
inst->ntp_address.port = 0;
inst->server_name[0] = '\0';
while (!error) {
if (!NKSN_GetRecord(inst->session, &critical, &type, &length, &data, sizeof (data)))
break;
switch (type) {
case NKE_RECORD_NEXT_PROTOCOL:
if (!critical || length != 2 || ntohs(data[0]) != NKE_NEXT_PROTOCOL_NTPV4) {
DEBUG_LOG("Unexpected NTS-KE next protocol");
error = 1;
break;
}
next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
break;
case NKE_RECORD_AEAD_ALGORITHM:
if (length != 2 || ntohs(data[0]) != AEAD_AES_SIV_CMAC_256) {
DEBUG_LOG("Unexpected NTS-KE AEAD algorithm");
error = 1;
break;
}
aead_algorithm = AEAD_AES_SIV_CMAC_256;
inst->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 #%d length=%d", inst->num_cookies + 1, length);
assert(NKE_MAX_COOKIE_LENGTH == sizeof (inst->cookies[inst->num_cookies].cookie));
if (length <= NKE_MAX_COOKIE_LENGTH && inst->num_cookies < NKE_MAX_COOKIES) {
inst->cookies[inst->num_cookies].length = length;
memcpy(inst->cookies[inst->num_cookies].cookie, data, length);
inst->num_cookies++;
}
break;
case NKE_RECORD_NTPV4_SERVER_NEGOTIATION:
if (length < 1 || length >= sizeof (inst->server_name)) {
DEBUG_LOG("Invalid server name");
error = 1;
break;
}
memcpy(inst->server_name, data, length);
inst->server_name[length] = '\0';
/* Make sure the name is printable and has no spaces */
for (i = 0; i < length && isgraph(inst->server_name[i]); i++)
;
if (i != length) {
DEBUG_LOG("Invalid server name");
error = 1;
break;
}
DEBUG_LOG("Negotiated server %s", inst->server_name);
break;
case NKE_RECORD_NTPV4_PORT_NEGOTIATION:
if (length != 2) {
DEBUG_LOG("Invalid port");
error = 1;
break;
}
inst->ntp_address.port = ntohs(data[0]);
DEBUG_LOG("Negotiated port %d", inst->ntp_address.port);
break;
default:
DEBUG_LOG("Unknown record type=%d length=%d critical=%d", type, length, critical);
if (critical)
error = 1;
}
}
DEBUG_LOG("NTS-KE response: error=%d next=%d aead=%d",
error, next_protocol, aead_algorithm);
if (error || inst->num_cookies == 0 ||
next_protocol != NKE_NEXT_PROTOCOL_NTPV4 ||
aead_algorithm != AEAD_AES_SIV_CMAC_256)
return 0;
return 1;
}
/* ================================================== */
static int
handle_message(void *arg)
{
NKC_Instance inst = arg;
if (!process_response(inst)) {
LOG(LOGS_ERR, "Received invalid NTS-KE response from %s", inst->name);
return 0;
}
if (!NKSN_GetKeys(inst->session, inst->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)) {
DNS_Name2IPAddressAsync(inst->server_name, name_resolve_handler, inst);
inst->resolving_name = 1;
}
}
inst->got_response = 1;
return 1;
}
/* ================================================== */
void
NKC_Initialise(void)
{
client_credentials = NULL;
}
/* ================================================== */
void
NKC_Finalise(void)
{
if (client_credentials)
NKSN_DestroyCertCredentials(client_credentials);
}
/* ================================================== */
NKC_Instance
NKC_CreateInstance(IPSockAddr *address, const char *name)
{
NKC_Instance inst;
inst = MallocNew(struct NKC_Instance_Record);
inst->address = *address;
inst->name = Strdup(name);
inst->session = NKSN_CreateInstance(0, inst->name, handle_message, inst);
inst->resolving_name = 0;
inst->destroying = 0;
inst->got_response = 0;
/* Create the credentials with the first client instance and share them
with other instances */
if (!client_credentials)
client_credentials = NKSN_CreateCertCredentials(NULL, NULL, CNF_GetNtsTrustedCertFile());
return inst;
}
/* ================================================== */
void
NKC_DestroyInstance(NKC_Instance inst)
{
/* If the resolver is running, destroy the instance later when finished */
if (inst->resolving_name) {
inst->destroying = 1;
return;
}
NKSN_DestroyInstance(inst->session);
Free(inst->name);
Free(inst);
}
/* ================================================== */
int
NKC_Start(NKC_Instance inst)
{
IPSockAddr local_addr;
char label[512];
int sock_fd;
assert(!NKC_IsActive(inst));
if (!client_credentials) {
DEBUG_LOG("Missing client credentials");
return 0;
}
/* Follow the bindacqaddress setting */
CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
if (local_addr.ip_addr.family != inst->address.ip_addr.family)
SCK_GetAnyLocalIPAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
local_addr.port = 0;
sock_fd = SCK_OpenTcpSocket(&inst->address, &local_addr, 0);
if (sock_fd < 0)
return 0;
/* 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))
;
/* Start a NTS-KE session */
if (!NKSN_StartSession(inst->session, sock_fd, label, client_credentials, CLIENT_TIMEOUT)) {
SCK_CloseSocket(sock_fd);
return 0;
}
/* Send a request */
if (!prepare_request(inst)) {
DEBUG_LOG("Could not prepare NTS-KE request");
NKSN_StopSession(inst->session);
return 0;
}
return 1;
}
/* ================================================== */
int
NKC_IsActive(NKC_Instance inst)
{
return !NKSN_IsStopped(inst->session) || inst->resolving_name;
}
/* ================================================== */
int
NKC_GetNtsData(NKC_Instance inst, 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 i;
}
/* ================================================== */
int
NKC_GetRetryFactor(NKC_Instance inst)
{
return NKSN_GetRetryFactor(inst->session);
}

View File

@@ -1,60 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header file for the NTS-KE client
*/
#ifndef GOT_NTS_KE_CLIENT_H
#define GOT_NTS_KE_CLIENT_H
#include "addressing.h"
#include "nts_ke.h"
typedef struct NKC_Instance_Record *NKC_Instance;
/* Init and fini functions */
extern void NKC_Initialise(void);
extern void NKC_Finalise(void);
/* Create a client NTS-KE instance */
extern NKC_Instance NKC_CreateInstance(IPSockAddr *address, const char *name);
/* Destroy an instance */
extern void NKC_DestroyInstance(NKC_Instance inst);
/* Connect to the server, start an NTS-KE session, send an NTS-KE request, and
process the response (asynchronously) */
extern int NKC_Start(NKC_Instance inst);
/* Check if the client is still running */
extern int NKC_IsActive(NKC_Instance inst);
/* Get the NTS data if the session was successful */
extern int NKC_GetNtsData(NKC_Instance inst, 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

View File

@@ -1,857 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
NTS-KE server
*/
#include "config.h"
#include "sysincl.h"
#include "nts_ke_server.h"
#include "array.h"
#include "conf.h"
#include "clientlog.h"
#include "logging.h"
#include "memory.h"
#include "ntp_core.h"
#include "nts_ke_session.h"
#include "siv.h"
#include "socket.h"
#include "sched.h"
#include "sys.h"
#include "util.h"
#define SERVER_TIMEOUT 2.0
#define SERVER_COOKIE_SIV AEAD_AES_SIV_CMAC_256
#define SERVER_COOKIE_NONCE_LENGTH 16
#define KEY_ID_INDEX_BITS 2
#define MAX_SERVER_KEYS (1U << KEY_ID_INDEX_BITS)
#define FUTURE_KEYS 1
#define DUMP_FILENAME "ntskeys"
#define DUMP_IDENTIFIER "NKS0\n"
#define INVALID_SOCK_FD (-7)
typedef struct {
uint32_t key_id;
uint8_t nonce[SERVER_COOKIE_NONCE_LENGTH];
} ServerCookieHeader;
typedef struct {
uint32_t id;
unsigned char key[SIV_MAX_KEY_LENGTH];
SIV_Instance siv;
} ServerKey;
typedef struct {
uint32_t key_id;
unsigned char key[SIV_MAX_KEY_LENGTH];
IPAddr client_addr;
uint16_t client_port;
uint16_t _pad;
} HelperRequest;
/* ================================================== */
static ServerKey server_keys[MAX_SERVER_KEYS];
static int current_server_key;
static 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 initialised = 0;
/* Array of NKSN instances */
static ARR_Instance sessions;
static void *server_credentials;
/* ================================================== */
static int handle_message(void *arg);
/* ================================================== */
static int
handle_client(int sock_fd, IPSockAddr *addr)
{
NKSN_Instance inst, *instp;
int i;
if (sock_fd > FD_SETSIZE / 2) {
DEBUG_LOG("Rejected connection from %s (%s)",
UTI_IPSockAddrToString(addr), "too many descriptors");
return 0;
}
/* Find a slot which is free or has a stopped session */
for (i = 0, inst = NULL; i < ARR_GetSize(sessions); i++) {
instp = ARR_GetElement(sessions, i);
if (!*instp) {
/* NULL handler arg will be replaced with the session instance */
inst = NKSN_CreateInstance(1, 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;
}
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;
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 (message->length != sizeof (HelperRequest)) {
DEBUG_LOG("Unexpected message length");
SCK_CloseSocket(sock_fd);
return;
}
req = message->data;
/* Extract the server key and client address from the request */
server_keys[current_server_key].id = ntohl(req->key_id);
memcpy(server_keys[current_server_key].key, req->key,
sizeof (server_keys[current_server_key].key));
UTI_IPNetworkToHost(&req->client_addr, &client_addr.ip_addr);
client_addr.port = ntohs(req->client_port);
if (!SIV_SetKey(server_keys[current_server_key].siv, server_keys[current_server_key].key,
SIV_GetKeyLength(SERVER_COOKIE_SIV)))
assert(0);
if (!handle_client(sock_fd, &client_addr)) {
SCK_CloseSocket(sock_fd);
return;
}
DEBUG_LOG("Accepted helper request fd=%d", sock_fd);
}
/* ================================================== */
static void
accept_connection(int server_fd, int event, void *arg)
{
SCK_Message message;
IPSockAddr addr;
int log_index, sock_fd;
struct timespec now;
sock_fd = SCK_AcceptConnection(server_fd, &addr);
if (sock_fd < 0)
return;
if (!NCR_CheckAccessRestriction(&addr.ip_addr)) {
DEBUG_LOG("Rejected connection from %s (%s)",
UTI_IPSockAddrToString(&addr), "access denied");
SCK_CloseSocket(sock_fd);
return;
}
SCH_GetLastEventTime(&now, NULL, NULL);
log_index = CLG_LogNTPAccess(&addr.ip_addr, &now);
if (log_index >= 0 && CLG_LimitNTPResponseRate(log_index)) {
DEBUG_LOG("Rejected connection from %s (%s)",
UTI_IPSockAddrToString(&addr), "rate limit");
SCK_CloseSocket(sock_fd);
return;
}
/* Pass the socket to a helper process if enabled. Otherwise, handle the
client in the main process. */
if (helper_sock_fd != INVALID_SOCK_FD) {
HelperRequest req;
/* Include the current server key and client address in the request */
memset(&req, 0, sizeof (req));
req.key_id = htonl(server_keys[current_server_key].id);
memcpy(req.key, server_keys[current_server_key].key, sizeof (req.key));
UTI_IPHostToNetwork(&addr.ip_addr, &req.client_addr);
req.client_port = htons(addr.port);
SCK_InitMessage(&message, SCK_ADDR_UNSPEC);
message.data = &req;
message.length = sizeof (req);
message.descriptor = sock_fd;
if (!SCK_SendMessage(helper_sock_fd, &message, SCK_FLAG_MSG_DESCRIPTOR)) {
SCK_CloseSocket(sock_fd);
return;
}
SCK_CloseSocket(sock_fd);
} else {
if (!handle_client(sock_fd, &addr)) {
SCK_CloseSocket(sock_fd);
return;
}
}
DEBUG_LOG("Accepted connection from %s fd=%d", UTI_IPSockAddrToString(&addr), sock_fd);
}
/* ================================================== */
static int
open_socket(int family, int port)
{
IPSockAddr local_addr;
int sock_fd;
if (!SCK_IsFamilySupported(family))
return INVALID_SOCK_FD;
CNF_GetBindAddress(family, &local_addr.ip_addr);
if (local_addr.ip_addr.family != family)
SCK_GetAnyLocalIPAddress(family, &local_addr.ip_addr);
local_addr.port = port;
sock_fd = SCK_OpenTcpSocket(NULL, &local_addr, 0);
if (sock_fd < 0) {
LOG(LOGS_ERR, "Could not open NTS-KE socket on %s", UTI_IPSockAddrToString(&local_addr));
return INVALID_SOCK_FD;
}
if (!SCK_ListenOnSocket(sock_fd, CNF_GetNtsServerConnections())) {
SCK_CloseSocket(sock_fd);
return INVALID_SOCK_FD;
}
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, accept_connection, NULL);
return sock_fd;
}
/* ================================================== */
static void
helper_signal(int x)
{
SCH_QuitProgram();
}
/* ================================================== */
static int
prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_algorithm)
{
NKE_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 {
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 = -1, aead_algorithm = -1, error = -1;
int i, critical, type, length;
uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)];
assert(NKE_MAX_RECORD_BODY_LENGTH % sizeof (uint16_t) == 0);
assert(sizeof (uint16_t) == 2);
while (error == -1) {
if (!NKSN_GetRecord(session, &critical, &type, &length, &data, sizeof (data)))
break;
switch (type) {
case NKE_RECORD_NEXT_PROTOCOL:
if (!critical || length < 2 || length % 2 != 0) {
error = NKE_ERROR_BAD_REQUEST;
break;
}
for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
if (ntohs(data[i]) == NKE_NEXT_PROTOCOL_NTPV4)
next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
}
break;
case NKE_RECORD_AEAD_ALGORITHM:
if (length < 2 || length % 2 != 0) {
error = NKE_ERROR_BAD_REQUEST;
break;
}
for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
if (ntohs(data[i]) == AEAD_AES_SIV_CMAC_256)
aead_algorithm = AEAD_AES_SIV_CMAC_256;
}
break;
case NKE_RECORD_ERROR:
case NKE_RECORD_WARNING:
case NKE_RECORD_COOKIE:
error = NKE_ERROR_BAD_REQUEST;
break;
default:
if (critical)
error = NKE_ERROR_UNRECOGNIZED_CRITICAL_RECORD;
}
}
if (aead_algorithm < 0 || next_protocol < 0)
error = NKE_ERROR_BAD_REQUEST;
if (!prepare_response(session, error, next_protocol, aead_algorithm))
return 0;
return 1;
}
/* ================================================== */
static int
handle_message(void *arg)
{
NKSN_Instance session = arg;
return process_request(session);
}
/* ================================================== */
static void
generate_key(int index)
{
int key_length;
assert(index < MAX_SERVER_KEYS);
key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
if (key_length > sizeof (server_keys[index].key))
assert(0);
UTI_GetRandomBytesUrandom(server_keys[index].key, key_length);
if (!SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length))
assert(0);
UTI_GetRandomBytes(&server_keys[index].id, sizeof (server_keys[index].id));
server_keys[index].id &= -1U << KEY_ID_INDEX_BITS;
server_keys[index].id |= index;
DEBUG_LOG("Generated server key %"PRIX32, server_keys[index].id);
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);
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 void
load_keys(void)
{
char *dump_dir, line[1024], *words[MAX_WORDS];
int i, index, key_length, algorithm;
double key_age;
FILE *f;
uint32_t id;
dump_dir = CNF_GetNtsDumpDir();
if (!dump_dir)
return;
f = UTI_OpenFile(dump_dir, DUMP_FILENAME, NULL, 'r', 0);
if (!f)
return;
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;
index = id % MAX_SERVER_KEYS;
if (UTI_HexToBytes(words[1], server_keys[index].key,
sizeof (server_keys[index].key)) != key_length)
goto error;
server_keys[index].id = id;
if (!SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length))
assert(0);
DEBUG_LOG("Loaded key %"PRIX32, id);
current_server_key = (index + MAX_SERVER_KEYS - FUTURE_KEYS) % MAX_SERVER_KEYS;
}
fclose(f);
return;
error:
DEBUG_LOG("Could not %s server keys", "load");
fclose(f);
}
/* ================================================== */
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
start_helper(int id, int scfilter_level, int main_fd, int helper_fd)
{
pid_t pid;
pid = fork();
if (pid < 0)
LOG_FATAL("fork() failed : %s", strerror(errno));
if (pid > 0)
return;
SCK_CloseSocket(main_fd);
LOG_CloseParentFd();
SCH_Reset();
SCH_AddFileHandler(helper_fd, SCH_FILE_INPUT, handle_helper_request, NULL);
UTI_SetQuitSignalsHandler(helper_signal, 1);
if (scfilter_level != 0)
SYS_EnableSystemCallFilter(scfilter_level, SYS_NTSKE_HELPER);
initialised = 1;
DEBUG_LOG("NTS-KE helper #%d started", id);
SCH_MainLoop();
NKS_Finalise();
DEBUG_LOG("NTS-KE helper #%d exiting", id);
exit(0);
}
/* ================================================== */
void
NKS_Initialise(int scfilter_level)
{
char *cert, *key;
int i, processes;
double key_delay;
server_sock_fd4 = INVALID_SOCK_FD;
server_sock_fd6 = INVALID_SOCK_FD;
helper_sock_fd = INVALID_SOCK_FD;
cert = CNF_GetNtsServerCertFile();
key = CNF_GetNtsServerKeyFile();
if (!cert || !key)
return;
server_credentials = NKSN_CreateCertCredentials(cert, key, NULL);
if (!server_credentials)
return;
sessions = ARR_CreateInstance(sizeof (NKSN_Instance));
for (i = 0; i < CNF_GetNtsServerConnections(); i++)
*(NKSN_Instance *)ARR_GetNewElement(sessions) = NULL;
for (i = 0; i < MAX_SERVER_KEYS; i++)
server_keys[i].siv = NULL;
server_sock_fd4 = open_socket(IPADDR_INET4, CNF_GetNtsServerPort());
server_sock_fd6 = open_socket(IPADDR_INET6, CNF_GetNtsServerPort());
for (i = 0; i < MAX_SERVER_KEYS; i++) {
server_keys[i].siv = SIV_CreateInstance(SERVER_COOKIE_SIV);
generate_key(i);
}
current_server_key = MAX_SERVER_KEYS - 1;
load_keys();
key_rotation_interval = MAX(CNF_GetNtsRotate(), 0);
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);
}
processes = CNF_GetNtsServerProcesses();
if (processes > 0) {
int sock_fd1, sock_fd2;
sock_fd1 = SCK_OpenUnixSocketPair(0, &sock_fd2);
if (sock_fd1 < 0)
LOG_FATAL("Could not open socket pair");
for (i = 0; i < processes; i++)
start_helper(i + 1, scfilter_level, sock_fd1, sock_fd2);
SCK_CloseSocket(sock_fd2);
helper_sock_fd = sock_fd1;
}
initialised = 1;
}
/* ================================================== */
void
NKS_Finalise(void)
{
int i;
if (!initialised)
return;
if (helper_sock_fd != INVALID_SOCK_FD) {
for (i = 0; i < CNF_GetNtsServerProcesses(); i++) {
if (!SCK_Send(helper_sock_fd, "", 1, 0))
;
}
SCK_CloseSocket(helper_sock_fd);
}
if (server_sock_fd4 != INVALID_SOCK_FD)
SCK_CloseSocket(server_sock_fd4);
if (server_sock_fd6 != INVALID_SOCK_FD)
SCK_CloseSocket(server_sock_fd6);
save_keys();
for (i = 0; i < MAX_SERVER_KEYS; i++) {
if (server_keys[i].siv != NULL)
SIV_DestroyInstance(server_keys[i].siv);
}
for (i = 0; i < ARR_GetSize(sessions); i++) {
NKSN_Instance session = *(NKSN_Instance *)ARR_GetElement(sessions, i);
if (session)
NKSN_DestroyInstance(session);
}
ARR_DestroyInstance(sessions);
NKSN_DestroyCertCredentials(server_credentials);
}
/* ================================================== */
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 <= 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;
}

View File

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

View File

@@ -1,848 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
NTS-KE session used by server and client
*/
#include "config.h"
#include "sysincl.h"
#include "nts_ke_session.h"
#include "conf.h"
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "siv.h"
#include "socket.h"
#include "sched.h"
#include "util.h"
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#define INVALID_SOCK_FD (-8)
struct RecordHeader {
uint16_t type;
uint16_t body_length;
};
struct Message {
int length;
int sent;
int parsed;
int complete;
unsigned char data[NKE_MAX_MESSAGE_LENGTH];
};
typedef enum {
KE_WAIT_CONNECT,
KE_HANDSHAKE,
KE_SEND,
KE_RECEIVE,
KE_SHUTDOWN,
KE_STOPPED,
} KeState;
struct NKSN_Instance_Record {
int server;
char *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;
int ended_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;
if (body_length < 0 || body_length > 0xffff || type < 0 || type > 0x7fff ||
message->length + sizeof (header) + body_length > sizeof (message->data))
return 0;
header.type = htons(!!critical * NKE_RECORD_CRITICAL_BIT | type);
header.body_length = htons(body_length);
memcpy(&message->data[message->length], &header, sizeof (header));
message->length += sizeof (header);
if (body_length > 0) {
memcpy(&message->data[message->length], body, body_length);
message->length += body_length;
}
return 1;
}
/* ================================================== */
static void
reset_message_parsing(struct Message *message)
{
message->parsed = 0;
}
/* ================================================== */
static int
get_record(struct Message *message, int *critical, int *type, int *body_length,
void *body, int buffer_length)
{
struct RecordHeader header;
int blen, rlen;
if (message->length < message->parsed + sizeof (header) ||
buffer_length < 0)
return 0;
memcpy(&header, &message->data[message->parsed], sizeof (header));
blen = ntohs(header.body_length);
rlen = sizeof (header) + blen;
if (message->length < message->parsed + rlen)
return 0;
if (critical)
*critical = !!(ntohs(header.type) & NKE_RECORD_CRITICAL_BIT);
if (type)
*type = ntohs(header.type) & ~NKE_RECORD_CRITICAL_BIT;
if (body)
memcpy(body, &message->data[message->parsed + sizeof (header)], MIN(buffer_length, blen));
if (body_length)
*body_length = blen;
message->parsed += rlen;
return 1;
}
/* ================================================== */
static int
check_message_format(struct Message *message, int eof)
{
int critical = 0, type = -1, length = -1, ends = 0;
reset_message_parsing(message);
message->complete = 0;
while (get_record(message, &critical, &type, &length, NULL, 0)) {
if (type == NKE_RECORD_END_OF_MESSAGE) {
if (!critical || length != 0 || ends > 0)
return 0;
ends++;
}
}
/* If the message cannot be fully parsed, but more data may be coming,
consider the format to be ok */
if (message->length == 0 || message->parsed < message->length)
return !eof;
if (type != NKE_RECORD_END_OF_MESSAGE)
return !eof;
message->complete = 1;
return 1;
}
/* ================================================== */
static gnutls_session_t
create_tls_session(int server_mode, int sock_fd, const char *server_name,
gnutls_certificate_credentials_t credentials,
gnutls_priority_t priority)
{
unsigned char alpn_name[sizeof (NKE_ALPN_NAME)];
gnutls_session_t session;
gnutls_datum_t alpn;
unsigned int flags;
int r;
r = gnutls_init(&session, GNUTLS_NONBLOCK | (server_mode ? GNUTLS_SERVER : GNUTLS_CLIENT));
if (r < 0) {
LOG(LOGS_ERR, "Could not %s TLS session : %s", "create", gnutls_strerror(r));
return NULL;
}
if (!server_mode) {
r = gnutls_server_name_set(session, GNUTLS_NAME_DNS, server_name, strlen(server_name));
if (r < 0)
goto error;
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
get_socket_error(int sock_fd)
{
int optval;
socklen_t optlen = sizeof (optval);
if (getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) {
DEBUG_LOG("getsockopt() failed : %s", strerror(errno));
return EINVAL;
}
return optval;
}
/* ================================================== */
static int
check_alpn(NKSN_Instance inst)
{
gnutls_datum_t alpn;
int r;
r = gnutls_alpn_get_selected_protocol(inst->tls_session, &alpn);
if (r < 0 || alpn.size != sizeof (NKE_ALPN_NAME) - 1 ||
strncmp((const char *)alpn.data, NKE_ALPN_NAME, sizeof (NKE_ALPN_NAME) - 1))
return 0;
return 1;
}
/* ================================================== */
static void
change_state(NKSN_Instance inst, KeState state)
{
int output;
switch (state) {
case KE_HANDSHAKE:
output = !inst->server;
break;
case KE_WAIT_CONNECT:
case KE_SEND:
case KE_SHUTDOWN:
output = 1;
break;
case KE_RECEIVE:
output = 0;
break;
default:
assert(0);
}
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT, output);
inst->state = state;
}
/* ================================================== */
static int
handle_event(NKSN_Instance inst, int event)
{
struct Message *message = &inst->message;
int r;
DEBUG_LOG("Session event %d fd=%d state=%d", event, inst->sock_fd, (int)inst->state);
switch (inst->state) {
case KE_WAIT_CONNECT:
/* Check if connect() succeeded */
if (event != SCH_FILE_OUTPUT)
return 0;
r = get_socket_error(inst->sock_fd);
if (r) {
LOG(LOGS_ERR, "Could not connect to %s : %s", inst->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)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"TLS handshake with %s failed : %s", inst->label, gnutls_strerror(r));
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 */
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT,
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);
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);
break;
case KE_SHUTDOWN:
r = gnutls_bye(inst->tls_session, GNUTLS_SHUT_RDWR);
if (r < 0) {
if (gnutls_error_is_fatal(r)) {
DEBUG_LOG("Shutdown with %s failed : %s", inst->label, gnutls_strerror(r));
stop_session(inst);
return 0;
}
/* Disable output when the TLS shutdown is trying to receive data */
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT,
gnutls_record_get_direction(inst->tls_session));
return 0;
}
SCK_ShutdownConnection(inst->sock_fd);
stop_session(inst);
DEBUG_LOG("Shutdown completed");
return 0;
default:
assert(0);
}
return 1;
}
/* ================================================== */
static void
read_write_socket(int fd, int event, void *arg)
{
NKSN_Instance inst = arg;
if (!handle_event(inst, event))
return;
reset_message_parsing(&inst->message);
if (!(inst->handler)(inst->handler_arg)) {
stop_session(inst);
return;
}
}
/* ================================================== */
static 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));
/* 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",
NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND);
if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "priority cache", gnutls_strerror(r));
gnutls_global_set_time_function(get_time);
gnutls_initialised = 1;
LCL_AddParameterChangeHandler(handle_step, NULL);
}
/* ================================================== */
static void
deinit_gnutls(void)
{
assert(gnutls_initialised);
LCL_RemoveParameterChangeHandler(handle_step, NULL);
gnutls_priority_deinit(priority_cache);
gnutls_global_deinit();
gnutls_initialised = 0;
}
/* ================================================== */
void *
NKSN_CreateCertCredentials(char *cert, char *key, char *trusted_certs)
{
gnutls_certificate_credentials_t credentials = NULL;
int r;
init_gnutls();
r = gnutls_certificate_allocate_credentials(&credentials);
if (r < 0)
goto error;
if (cert && key) {
r = gnutls_certificate_set_x509_key_file(credentials, cert, key,
GNUTLS_X509_FMT_PEM);
if (r < 0)
goto error;
} else {
if (!CNF_GetNoSystemCert()) {
r = gnutls_certificate_set_x509_system_trust(credentials);
if (r < 0)
goto error;
}
if (trusted_certs) {
r = gnutls_certificate_set_x509_trust_file(credentials, trusted_certs,
GNUTLS_X509_FMT_PEM);
if (r < 0)
goto error;
}
}
credentials_counter++;
return credentials;
error:
LOG(LOGS_ERR, "Could not set credentials : %s", gnutls_strerror(r));
if (credentials)
gnutls_certificate_free_credentials(credentials);
return NULL;
}
/* ================================================== */
void
NKSN_DestroyCertCredentials(void *credentials)
{
gnutls_certificate_free_credentials(credentials);
credentials_counter--;
if (credentials_counter != 0)
return;
deinit_gnutls();
}
/* ================================================== */
NKSN_Instance
NKSN_CreateInstance(int server_mode, const char *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 NULL arg with the session itself */
if (!inst->handler_arg)
inst->handler_arg = inst;
inst->state = KE_STOPPED;
inst->sock_fd = INVALID_SOCK_FD;
inst->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,
void *credentials, double timeout)
{
assert(inst->state == KE_STOPPED);
inst->tls_session = create_tls_session(inst->server, sock_fd, inst->server_name,
credentials, priority_cache);
if (!inst->tls_session)
return 0;
inst->sock_fd = sock_fd;
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, inst);
inst->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;
inst->ended_message = 0;
change_state(inst, inst->server ? KE_HANDSHAKE : KE_WAIT_CONNECT);
return 1;
}
/* ================================================== */
void
NKSN_BeginMessage(NKSN_Instance inst)
{
reset_message(&inst->message);
inst->new_message = 1;
}
/* ================================================== */
int
NKSN_AddRecord(NKSN_Instance inst, int critical, int type, const void *body, int body_length)
{
assert(inst->new_message && !inst->message.complete);
assert(type != NKE_RECORD_END_OF_MESSAGE);
return add_record(&inst->message, critical, type, body, body_length);
}
/* ================================================== */
int
NKSN_EndMessage(NKSN_Instance inst)
{
assert(!inst->message.complete);
if (!add_record(&inst->message, 1, NKE_RECORD_END_OF_MESSAGE, NULL, 0))
return 0;
inst->message.complete = 1;
return 1;
}
/* ================================================== */
int
NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
void *body, int buffer_length)
{
int type2;
assert(inst->message.complete);
if (!get_record(&inst->message, critical, &type2, body_length, body, buffer_length))
return 0;
if (type2 == NKE_RECORD_END_OF_MESSAGE)
return 0;
if (type)
*type = type2;
return 1;
}
/* ================================================== */
int
NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c)
{
c2s->length = SIV_GetKeyLength(siv);
s2c->length = SIV_GetKeyLength(siv);
assert(c2s->length <= sizeof (c2s->key));
assert(s2c->length <= sizeof (s2c->key));
if (gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (NKE_EXPORTER_CONTEXT_C2S) - 1, NKE_EXPORTER_CONTEXT_C2S,
c2s->length, (char *)c2s->key) < 0)
return 0;
if (gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (NKE_EXPORTER_CONTEXT_S2C) - 1, NKE_EXPORTER_CONTEXT_S2C,
s2c->length, (char *)s2c->key) < 0)
return 0;
return 1;
}
/* ================================================== */
int
NKSN_IsStopped(NKSN_Instance inst)
{
return inst->state == KE_STOPPED;
}
/* ================================================== */
void
NKSN_StopSession(NKSN_Instance inst)
{
stop_session(inst);
}
/* ================================================== */
int
NKSN_GetRetryFactor(NKSN_Instance inst)
{
return inst->retry_factor;
}

View File

@@ -1,87 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header file for the NTS-KE session
*/
#ifndef GOT_NTS_KE_SESSION_H
#define GOT_NTS_KE_SESSION_H
#include "nts_ke.h"
#include "siv.h"
typedef struct NKSN_Instance_Record *NKSN_Instance;
/* Handler for received NTS-KE messages. A non-zero return code stops
the session. */
typedef int (*NKSN_MessageHandler)(void *arg);
/* Get client or server credentials using certificates of trusted CAs,
or a server certificate and key. The credentials may be shared between
different clients or servers. */
extern void *NKSN_CreateCertCredentials(char *cert, char *key, char *trusted_certs);
/* Destroy the credentials */
extern void NKSN_DestroyCertCredentials(void *credentials);
/* Create an instance */
extern NKSN_Instance NKSN_CreateInstance(int server_mode, const char *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,
void *credentials, double timeout);
/* Begin an NTS-KE message. A request should be made right after starting
the session and response should be made in the message handler. */
extern void NKSN_BeginMessage(NKSN_Instance inst);
/* Add a record to the message */
extern int NKSN_AddRecord(NKSN_Instance inst, int critical, int type,
const void *body, int body_length);
/* Terminate the message */
extern int NKSN_EndMessage(NKSN_Instance inst);
/* Get the next record from the received message. This function should be
called from the message handler. */
extern int NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
void *body, int buffer_length);
/* Export NTS keys for a specified algorithm */
extern int NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c);
/* Check if the session has stopped */
extern int NKSN_IsStopped(NKSN_Instance inst);
/* Stop the session */
extern void NKSN_StopSession(NKSN_Instance inst);
/* 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

View File

@@ -1,41 +0,0 @@
/*
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

View File

@@ -1,174 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
NTS Authenticator and Encrypted Extension Fields extension field
*/
#include "config.h"
#include "sysincl.h"
#include "nts_ntp_auth.h"
#include "logging.h"
#include "ntp_ext.h"
#include "nts_ntp.h"
#include "siv.h"
#include "util.h"
struct AuthHeader {
uint16_t nonce_length;
uint16_t ciphertext_length;
};
/* ================================================== */
static int
get_padding_length(int length)
{
return length % 4U ? 4 - length % 4U : 0;
}
/* ================================================== */
static int
get_padded_length(int length)
{
return length + get_padding_length(length);
}
/* ================================================== */
int
NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
const unsigned char *nonce, int nonce_length,
const unsigned char *plaintext, int plaintext_length,
int min_ef_length)
{
int auth_length, ciphertext_length, assoc_length;
int nonce_padding, ciphertext_padding, additional_padding;
unsigned char *ciphertext, *body;
struct AuthHeader *header;
assert(sizeof (*header) == 4);
if (nonce_length <= 0 || plaintext_length < 0) {
DEBUG_LOG("Invalid nonce/plaintext length");
return 0;
}
assoc_length = info->length;
ciphertext_length = SIV_GetTagLength(siv) + plaintext_length;
nonce_padding = get_padding_length(nonce_length);
ciphertext_padding = get_padding_length(ciphertext_length);
min_ef_length = get_padded_length(min_ef_length);
auth_length = sizeof (*header) + nonce_length + nonce_padding +
ciphertext_length + ciphertext_padding;
additional_padding = MAX(min_ef_length - auth_length - 4, 0);
additional_padding = MAX(NTS_MIN_UNPADDED_NONCE_LENGTH - nonce_length - nonce_padding,
additional_padding);
auth_length += additional_padding;
if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, auth_length,
(void **)&header)) {
DEBUG_LOG("Could not add EF");
return 0;
}
header->nonce_length = htons(nonce_length);
header->ciphertext_length = htons(ciphertext_length);
body = (unsigned char *)(header + 1);
ciphertext = body + nonce_length + nonce_padding;
memcpy(body, nonce, nonce_length);
memset(body + nonce_length, 0, nonce_padding);
if (!SIV_Encrypt(siv, nonce, nonce_length, packet, assoc_length,
plaintext, plaintext_length, ciphertext, ciphertext_length)) {
DEBUG_LOG("SIV encrypt failed");
return 0;
}
memset(ciphertext + ciphertext_length, 0, ciphertext_padding + additional_padding);
return 1;
}
/* ================================================== */
int
NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, int ef_start,
unsigned char *plaintext, int buffer_length, int *plaintext_length)
{
unsigned int siv_tag_length, nonce_length, ciphertext_length;
unsigned char *nonce, *ciphertext;
int ef_type, ef_body_length;
void *ef_body;
struct AuthHeader *header;
if (!NEF_ParseField(packet, info->length, ef_start,
NULL, &ef_type, &ef_body, &ef_body_length))
return 0;
if (ef_type != NTP_EF_NTS_AUTH_AND_EEF)
return 0;
header = ef_body;
nonce_length = ntohs(header->nonce_length);
ciphertext_length = ntohs(header->ciphertext_length);
if (get_padded_length(nonce_length) +
get_padded_length(ciphertext_length) > ef_body_length)
return 0;
nonce = (unsigned char *)(header + 1);
ciphertext = (unsigned char *)(header + 1) + get_padded_length(nonce_length);
siv_tag_length = SIV_GetTagLength(siv);
if (nonce_length < 1 ||
ciphertext_length < siv_tag_length ||
ciphertext_length - siv_tag_length > buffer_length) {
DEBUG_LOG("Unexpected nonce/ciphertext length");
return 0;
}
if (ef_body_length < sizeof (*header) +
NTS_MIN_UNPADDED_NONCE_LENGTH + get_padded_length(ciphertext_length)) {
DEBUG_LOG("Missing padding");
return 0;
}
*plaintext_length = ciphertext_length - siv_tag_length;
if (!SIV_Decrypt(siv, nonce, nonce_length, packet, info->length - ef_body_length - 4,
ciphertext, ciphertext_length, plaintext, *plaintext_length)) {
DEBUG_LOG("SIV decrypt failed");
return 0;
}
return 1;
}

View File

@@ -1,43 +0,0 @@
/*
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

View File

@@ -1,629 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Client NTS-NTP authentication
*/
#include "config.h"
#include "sysincl.h"
#include "nts_ntp_client.h"
#include "conf.h"
#include "logging.h"
#include "memory.h"
#include "ntp.h"
#include "ntp_ext.h"
#include "ntp_sources.h"
#include "nts_ke_client.h"
#include "nts_ntp.h"
#include "nts_ntp_auth.h"
#include "sched.h"
#include "siv.h"
#include "util.h"
#define MAX_TOTAL_COOKIE_LENGTH (8 * 108)
#define DUMP_IDENTIFIER "NNC0\n"
struct NNC_Instance_Record {
const IPSockAddr *ntp_address;
IPSockAddr nts_address;
char *name;
NKC_Instance nke;
SIV_Instance siv;
int load_attempt;
int nke_attempts;
double next_nke_attempt;
double last_nke_success;
NKE_Context context;
NKE_Cookie cookies[NTS_MAX_COOKIES];
int num_cookies;
int cookie_index;
int nak_response;
int ok_response;
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
unsigned char uniq_id[NTS_MIN_UNIQ_ID_LENGTH];
};
/* ================================================== */
static void save_cookies(NNC_Instance inst);
static void load_cookies(NNC_Instance inst);
/* ================================================== */
static void
reset_instance(NNC_Instance inst)
{
inst->load_attempt = 0;
inst->nke_attempts = 0;
inst->next_nke_attempt = 0.0;
inst->last_nke_success = 0.0;
memset(&inst->context, 0, sizeof (inst->context));
inst->num_cookies = 0;
inst->cookie_index = 0;
inst->nak_response = 0;
inst->ok_response = 1;
memset(inst->nonce, 0, sizeof (inst->nonce));
memset(inst->uniq_id, 0, sizeof (inst->uniq_id));
}
/* ================================================== */
NNC_Instance
NNC_CreateInstance(IPSockAddr *nts_address, const char *name, const IPSockAddr *ntp_address)
{
NNC_Instance inst;
inst = MallocNew(struct NNC_Instance_Record);
inst->ntp_address = ntp_address;
inst->nts_address = *nts_address;
inst->name = name ? Strdup(name) : NULL;
inst->siv = NULL;
inst->nke = NULL;
reset_instance(inst);
return inst;
}
/* ================================================== */
void
NNC_DestroyInstance(NNC_Instance inst)
{
save_cookies(inst);
if (inst->nke)
NKC_DestroyInstance(inst->nke);
if (inst->siv)
SIV_DestroyInstance(inst->siv);
Free(inst->name);
Free(inst);
}
/* ================================================== */
static int
check_cookies(NNC_Instance inst)
{
/* Force NKE if a NAK was received since last valid auth */
if (inst->nak_response && !inst->ok_response && inst->num_cookies > 0) {
inst->num_cookies = 0;
DEBUG_LOG("Dropped cookies");
}
/* Force NKE if the keys encrypting the cookies are too old */
if (inst->num_cookies > 0 &&
SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())
inst->num_cookies = 0;
return inst->num_cookies > 0;
}
/* ================================================== */
static int
set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address)
{
NTP_Remote_Address old_address, new_address;
old_address = *inst->ntp_address;
new_address = *negotiated_address;
if (new_address.ip_addr.family == IPADDR_UNSPEC)
new_address.ip_addr = old_address.ip_addr;
if (new_address.port == 0)
new_address.port = old_address.port;
if (UTI_CompareIPs(&old_address.ip_addr, &new_address.ip_addr, NULL) == 0 &&
old_address.port == new_address.port)
/* Nothing to do */
return 1;
if (NSR_UpdateSourceNtpAddress(&old_address, &new_address) != NSR_Success) {
LOG(LOGS_ERR, "Could not change %s to negotiated address %s",
UTI_IPToString(&old_address.ip_addr), UTI_IPToString(&new_address.ip_addr));
return 0;
}
return 1;
}
/* ================================================== */
static 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(!check_cookies(inst));
now = SCH_GetLastEventMonoTime();
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;
}
if (!inst->name) {
LOG(LOGS_ERR, "Missing name of %s for NTS-KE",
UTI_IPToString(&inst->nts_address.ip_addr));
return 0;
}
inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name);
inst->nke_attempts++;
update_next_nke_attempt(inst, now);
if (!NKC_Start(inst->nke))
return 0;
}
update_next_nke_attempt(inst, now);
if (NKC_IsActive(inst->nke))
return 0;
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;
if (!set_ntp_address(inst, &ntp_address)) {
inst->num_cookies = 0;
return 0;
}
inst->cookie_index = 0;
inst->nak_response = 0;
inst->last_nke_success = now;
return 1;
}
/* ================================================== */
int
NNC_PrepareForAuth(NNC_Instance inst)
{
if (!inst->load_attempt) {
load_cookies(inst);
inst->load_attempt = 1;
}
if (!check_cookies(inst)) {
if (!get_cookies(inst))
return 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;
}
UTI_GetRandomBytes(&inst->uniq_id, sizeof (inst->uniq_id));
UTI_GetRandomBytes(&inst->nonce, sizeof (inst->nonce));
return 1;
}
/* ================================================== */
int
NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
NTP_PacketInfo *info)
{
NKE_Cookie *cookie;
int i, req_cookies;
void *ef_body;
if (inst->num_cookies == 0 || !inst->siv)
return 0;
if (info->mode != MODE_CLIENT)
return 0;
cookie = &inst->cookies[inst->cookie_index];
req_cookies = MIN(NTS_MAX_COOKIES - inst->num_cookies + 1,
MAX_TOTAL_COOKIE_LENGTH / (cookie->length + 4));
if (!NEF_AddField(packet, info, NTP_EF_NTS_UNIQUE_IDENTIFIER,
&inst->uniq_id, sizeof (inst->uniq_id)))
return 0;
if (!NEF_AddField(packet, info, NTP_EF_NTS_COOKIE,
cookie->cookie, cookie->length))
return 0;
for (i = 0; i < req_cookies - 1; i++) {
if (!NEF_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->num_cookies--;
inst->cookie_index = (inst->cookie_index + 1) % NTS_MAX_COOKIES;
inst->ok_response = 0;
return 1;
}
/* ================================================== */
static int
extract_cookies(NNC_Instance inst, unsigned char *plaintext, int length)
{
int ef_type, ef_body_length, ef_length, parsed, index, acceptable, saved;
void *ef_body;
acceptable = saved = 0;
for (parsed = 0; parsed < length; parsed += ef_length) {
if (!NEF_ParseSingleField(plaintext, length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length))
break;
if (ef_type != NTP_EF_NTS_COOKIE)
continue;
if (ef_length < NTP_MIN_EF_LENGTH || ef_body_length > sizeof (inst->cookies[0].cookie)) {
DEBUG_LOG("Unexpected cookie length %d", ef_body_length);
continue;
}
acceptable++;
if (inst->num_cookies >= NTS_MAX_COOKIES)
continue;
index = (inst->cookie_index + inst->num_cookies) % NTS_MAX_COOKIES;
memcpy(inst->cookies[index].cookie, ef_body, ef_body_length);
inst->cookies[index].length = ef_body_length;
inst->num_cookies++;
saved++;
}
DEBUG_LOG("Extracted %d cookies (saved %d)", acceptable, saved);
return acceptable > 0;
}
/* ================================================== */
int
NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
NTP_PacketInfo *info)
{
int ef_type, ef_body_length, ef_length, parsed, plaintext_length;
int has_valid_uniq_id = 0, has_valid_auth = 0;
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
void *ef_body;
if (info->ext_fields == 0 || info->mode != MODE_SERVER)
return 0;
/* Accept only one response per request */
if (inst->ok_response)
return 0;
if (!inst->siv ||
!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))
break;
switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
if (ef_body_length != sizeof (inst->uniq_id) ||
memcmp(ef_body, inst->uniq_id, sizeof (inst->uniq_id)) != 0) {
DEBUG_LOG("Invalid uniq id");
return 0;
}
has_valid_uniq_id = 1;
break;
case NTP_EF_NTS_COOKIE:
DEBUG_LOG("Unencrypted cookie");
break;
case NTP_EF_NTS_AUTH_AND_EEF:
if (parsed + ef_length != info->length) {
DEBUG_LOG("Auth not last EF");
return 0;
}
if (!NNA_DecryptAuthEF(packet, info, inst->siv, parsed,
plaintext, sizeof (plaintext), &plaintext_length))
return 0;
has_valid_auth = 1;
break;
default:
break;
}
}
if (!has_valid_uniq_id || !has_valid_auth) {
if (has_valid_uniq_id && packet->stratum == NTP_INVALID_STRATUM &&
ntohl(packet->reference_id) == NTP_KOD_NTS_NAK) {
DEBUG_LOG("NTS NAK");
inst->nak_response = 1;
return 0;
}
DEBUG_LOG("Missing NTS EF");
return 0;
}
if (!extract_cookies(inst, plaintext, plaintext_length))
return 0;
inst->ok_response = 1;
/* At this point we know the client interoperates with the server. Allow a
new NTS-KE session to be started as soon as the cookies run out. */
inst->nke_attempts = 0;
inst->next_nke_attempt = 0.0;
return 1;
}
/* ================================================== */
void
NNC_ChangeAddress(NNC_Instance inst, IPAddr *address)
{
save_cookies(inst);
if (inst->nke)
NKC_DestroyInstance(inst->nke);
inst->nke = NULL;
inst->num_cookies = 0;
inst->nts_address.ip_addr = *address;
reset_instance(inst);
DEBUG_LOG("NTS reset");
}
/* ================================================== */
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%.1f\n%s %d\n%d ",
DUMP_IDENTIFIER, context_time, UTI_IPToString(&inst->ntp_address->ip_addr),
inst->ntp_address->port, (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 3
static void
load_cookies(NNC_Instance inst)
{
char line[2 * NKE_MAX_COOKIE_LENGTH + 2], *dump_dir, *filename, *words[MAX_WORDS];
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 ||
sscanf(words[0], "%lf", &context_time) != 1 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 ||
!UTI_StringToIP(words[0], &ntp_addr.ip_addr) || sscanf(words[1], "%d", &port) != 1 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 3 ||
sscanf(words[0], "%d", &algorithm) != 1)
goto error;
inst->context.algorithm = algorithm;
inst->context.s2c.length = UTI_HexToBytes(words[1], inst->context.s2c.key,
sizeof (inst->context.s2c.key));
inst->context.c2s.length = UTI_HexToBytes(words[2], 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();
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);
}

View File

@@ -1,48 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header file for client NTS-NTP authentication
*/
#ifndef GOT_NTS_NTP_CLIENT_H
#define GOT_NTS_NTP_CLIENT_H
#include "addressing.h"
#include "ntp.h"
typedef struct NNC_Instance_Record *NNC_Instance;
extern NNC_Instance NNC_CreateInstance(IPSockAddr *nts_address, const char *name,
const IPSockAddr *ntp_address);
extern void NNC_DestroyInstance(NNC_Instance inst);
extern int NNC_PrepareForAuth(NNC_Instance inst);
extern int NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
NTP_PacketInfo *info);
extern int NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
NTP_PacketInfo *info);
extern void NNC_ChangeAddress(NNC_Instance inst, IPAddr *address);
extern void NNC_DumpData(NNC_Instance inst);
#endif

View File

@@ -1,260 +0,0 @@
/*
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)
{
/* Create an NTS-NTP server instance only if NTS-KE server is enabled */
if (!CNF_GetNtsServerCertFile() || !CNF_GetNtsServerKeyFile()) {
server = NULL;
return;
}
server = Malloc(sizeof (struct NtsServer));
server->siv = SIV_CreateInstance(SERVER_SIV);
}
/* ================================================== */
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;
if (!server)
return 0;
*kod = 0;
server->num_cookies = 0;
server->req_tx = packet->transmit_ts;
if (info->ext_fields == 0 || info->mode != MODE_CLIENT)
return 0;
requested_cookies = 0;
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
if (!NEF_ParseField(packet, info->length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length))
break;
switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
has_uniq_id = 1;
break;
case NTP_EF_NTS_COOKIE:
if (has_cookie || ef_body_length > sizeof (cookie.cookie))
return 0;
cookie.length = ef_body_length;
memcpy(cookie.cookie, ef_body, ef_body_length);
has_cookie = 1;
/* Fall through */
case NTP_EF_NTS_COOKIE_PLACEHOLDER:
requested_cookies++;
if (cookie_length >= 0 && cookie_length != ef_body_length) {
DEBUG_LOG("Invalid cookie/placeholder length");
return 0;
}
cookie_length = ef_body_length;
break;
case NTP_EF_NTS_AUTH_AND_EEF:
auth_start = parsed;
has_auth = 1;
break;
default:
break;
}
}
if (!has_uniq_id || !has_cookie || !has_auth) {
DEBUG_LOG("Missing an NTS EF");
return 0;
}
if (!NKS_DecodeCookie(&cookie, &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))
break;
switch (ef_type) {
case NTP_EF_NTS_COOKIE_PLACEHOLDER:
if (cookie_length != ef_body_length) {
DEBUG_LOG("Invalid cookie/placeholder length");
return 0;
}
requested_cookies++;
break;
default:
break;
}
}
if (!SIV_SetKey(server->siv, context.s2c.key, context.s2c.length)) {
DEBUG_LOG("Could not set S2C key");
return 0;
}
UTI_GetRandomBytes(server->nonce, sizeof (server->nonce));
server->num_cookies = MIN(NTS_MAX_COOKIES, requested_cookies);
for (i = 0; i < server->num_cookies; i++)
if (!NKS_GenerateCookie(&context, &server->cookies[i]))
return 0;
return 1;
}
/* ================================================== */
int
NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
NTP_Packet *response, NTP_PacketInfo *res_info,
uint32_t kod)
{
int i, ef_type, ef_body_length, ef_length, parsed;
void *ef_body;
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
int plaintext_length;
if (!server || req_info->mode != MODE_CLIENT || res_info->mode != MODE_SERVER)
return 0;
/* Make sure this is a response to the expected request */
if (UTI_CompareNtp64(&server->req_tx, &request->transmit_ts) != 0)
assert(0);
for (parsed = NTP_HEADER_LENGTH; parsed < req_info->length; parsed += ef_length) {
if (!NEF_ParseField(request, req_info->length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length))
break;
switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
/* Copy the ID from the request */
if (!NEF_AddField(response, res_info, ef_type, ef_body, ef_body_length))
return 0;
default:
break;
}
}
/* NTS NAK response does not have any other fields */
if (kod)
return 1;
for (i = 0, plaintext_length = 0; i < server->num_cookies; i++) {
if (!NEF_SetField(plaintext, sizeof (plaintext), plaintext_length,
NTP_EF_NTS_COOKIE, &server->cookies[i].cookie,
server->cookies[i].length, &ef_length))
return 0;
plaintext_length += ef_length;
assert(plaintext_length <= sizeof (plaintext));
}
server->num_cookies = 0;
if (!NNA_GenerateAuthEF(response, res_info, server->siv,
server->nonce, sizeof (server->nonce),
plaintext, plaintext_length,
req_info->length - res_info->length))
return 0;
return 1;
}

View File

@@ -1,40 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header file for server NTS-NTP authentication
*/
#ifndef GOT_NTS_NTP_SERVER_H
#define GOT_NTS_NTP_SERVER_H
#include "ntp.h"
extern void NNS_Initialise(void);
extern void NNS_Finalise(void);
extern int NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod);
extern int NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
NTP_Packet *response, NTP_PacketInfo *res_info,
uint32_t kod);
#endif

View File

@@ -116,14 +116,10 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */
{ 0, 0 }, /* ADD_SERVER2 */
{ 0, 0 }, /* ADD_PEER2 */
{ 0, 0 }, /* ADD_SERVER3 */
{ 0, 0 }, /* ADD_PEER3 */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER3 */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER3 */
REQ_LENGTH_ENTRY(null, null), /* SHUTDOWN */
REQ_LENGTH_ENTRY(null, null), /* ONOFFLINE */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SOURCE */
REQ_LENGTH_ENTRY(ntp_source_name,
ntp_source_name), /* NTP_SOURCE_NAME */
REQ_LENGTH_ENTRY(null, null), /* RESET */
};
static const uint16_t reply_lengths[] = {
@@ -146,7 +142,6 @@ static const uint16_t reply_lengths[] = {
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 */
};
/* ================================================== */

129
privops.c
View File

@@ -33,7 +33,6 @@
#include "nameserv.h"
#include "logging.h"
#include "privops.h"
#include "socket.h"
#include "util.h"
#define OP_ADJUSTTIME 1024
@@ -159,7 +158,7 @@ res_fatal(PrvResponse *res, const char *fmt, ...)
static int
send_response(int fd, const PrvResponse *res)
{
if (SCK_Send(fd, res, sizeof (*res), 0) != sizeof (*res))
if (send(fd, res, sizeof (*res), 0) != sizeof (*res))
return 0;
return 1;
@@ -171,23 +170,37 @@ send_response(int fd, const PrvResponse *res)
static int
receive_from_daemon(int fd, PrvRequest *req)
{
SCK_Message *message;
struct msghdr msg;
struct cmsghdr *cmsg;
struct iovec iov;
char cmsgbuf[256];
message = SCK_ReceiveMessage(fd, SCK_FLAG_MSG_DESCRIPTOR);
if (!message || message->length != sizeof (*req))
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;
/* read the data */
if (recvmsg(fd, &msg, 0) != sizeof (*req))
return 0;
memcpy(req, message->data, sizeof (*req));
if (req->op == OP_BINDSOCKET) {
req->data.bind_socket.sock = message->descriptor;
/* 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));
}
/* 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;
@@ -244,7 +257,8 @@ do_set_time(const ReqSetTime *req, PrvResponse *res)
static void
do_bind_socket(ReqBindSocket *req, PrvResponse *res)
{
IPSockAddr ip_saddr;
unsigned short port;
IPAddr ip;
int sock_fd;
struct sockaddr *sa;
socklen_t sa_len;
@@ -253,11 +267,10 @@ do_bind_socket(ReqBindSocket *req, PrvResponse *res)
sa_len = req->sa_len;
sock_fd = req->sock;
SCK_SockaddrToIPSockAddr(sa, sa_len, &ip_saddr);
if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() &&
ip_saddr.port != CNF_GetAcquisitionPort()) {
SCK_CloseSocket(sock_fd);
res_fatal(res, "Invalid port %d", ip_saddr.port);
UTI_SockaddrToIPAndPort(sa, &ip, &port);
if (port && port != CNF_GetNTPPort() && port != CNF_GetAcquisitionPort()) {
close(sock_fd);
res_fatal(res, "Invalid port %d", port);
return;
}
@@ -266,7 +279,7 @@ do_bind_socket(ReqBindSocket *req, PrvResponse *res)
res->res_errno = errno;
/* sock is still open on daemon side, but we're done with it in the helper */
SCK_CloseSocket(sock_fd);
close(sock_fd);
}
#endif
@@ -360,7 +373,7 @@ helper_main(int fd)
send_response(fd, &res);
}
SCK_CloseSocket(fd);
close(fd);
exit(0);
}
@@ -373,7 +386,7 @@ receive_response(PrvResponse *res)
{
int resp_len;
resp_len = SCK_Receive(helper_fd, res, sizeof (*res), 0);
resp_len = recv(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))
@@ -396,22 +409,41 @@ receive_response(PrvResponse *res)
static void
send_request(PrvRequest *req)
{
SCK_Message message;
int flags;
struct msghdr msg;
struct iovec iov;
char cmsgbuf[256];
SCK_InitMessage(&message, SCK_ADDR_UNSPEC);
iov.iov_base = req;
iov.iov_len = sizeof (*req);
message.data = req;
message.length = sizeof (*req);
flags = 0;
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 */
message.descriptor = req->data.bind_socket.sock;
flags |= SCK_FLAG_MSG_DESCRIPTOR;
struct cmsghdr *cmsg;
int *ptr_send_fd;
msg.msg_control = cmsgbuf;
msg.msg_controllen = CMSG_SPACE(sizeof (int));
cmsg = CMSG_FIRSTHDR(&msg);
memset(cmsg, 0, CMSG_SPACE(sizeof (int)));
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 (!SCK_SendMessage(helper_fd, &message, flags)) {
if (sendmsg(helper_fd, &msg, 0) < 0) {
/* don't try to send another request from exit() */
helper_fd = -1;
LOG_FATAL("Could not send to helper : %s", strerror(errno));
@@ -541,13 +573,13 @@ PRV_SetTime(const struct timeval *tp, const struct timezone *tzp)
int
PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len)
{
IPSockAddr ip_saddr;
PrvRequest req;
PrvResponse res;
IPAddr ip;
unsigned short port;
SCK_SockaddrToIPSockAddr(address, address_len, &ip_saddr);
if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() &&
ip_saddr.port != CNF_GetAcquisitionPort())
UTI_SockaddrToIPAndPort(address, &ip, &port);
if (port && port != CNF_GetNTPPort() && port != CNF_GetAcquisitionPort())
assert(0);
if (!have_helper())
@@ -557,7 +589,6 @@ PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len)
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);
@@ -585,6 +616,7 @@ PRV_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
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;
}
@@ -638,14 +670,20 @@ void
PRV_StartHelper(void)
{
pid_t pid;
int fd, sock_fd1, sock_fd2;
int fd, sock_pair[2];
if (have_helper())
LOG_FATAL("Helper already running");
sock_fd1 = SCK_OpenUnixSocketPair(SCK_FLAG_BLOCK, &sock_fd2);
if (sock_fd1 < 0)
LOG_FATAL("Could not open socket pair");
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]);
pid = fork();
if (pid < 0)
@@ -653,24 +691,23 @@ PRV_StartHelper(void)
if (pid == 0) {
/* child process */
SCK_CloseSocket(sock_fd1);
close(sock_pair[0]);
/* 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 other descriptors inherited from the parent process */
for (fd = 0; fd < 1024; fd++) {
if (fd != sock_pair[1])
close(fd);
}
/* ignore signals, the process will exit on OP_QUIT request */
UTI_SetQuitSignalsHandler(SIG_IGN, 1);
helper_main(sock_fd2);
helper_main(sock_pair[1]);
} else {
/* parent process */
SCK_CloseSocket(sock_fd2);
helper_fd = sock_fd1;
close(sock_pair[1]);
helper_fd = sock_pair[0];
helper_pid = pid;
/* stop the helper even when not exiting cleanly from the main function */

View File

@@ -415,6 +415,7 @@ accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double of
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)
@@ -703,7 +704,6 @@ poll_timeout(void *arg)
if (SPF_GetFilteredSample(inst->filter, &sample)) {
SRC_UpdateReachability(inst->source, 1);
SRC_SetLeapStatus(inst->source, inst->leap_status);
SRC_AccumulateSample(inst->source, &sample);
SRC_SelectSource(inst->source);

View File

@@ -33,7 +33,6 @@
#include "logging.h"
#include "util.h"
#include "sched.h"
#include "socket.h"
#define SOCK_MAGIC 0x534f434b
@@ -98,6 +97,7 @@ static void read_sample(int sockfd, int event, void *anything)
static int sock_initialise(RCL_Instance instance)
{
struct sockaddr_un s;
int sockfd;
char *path;
@@ -105,9 +105,25 @@ static int sock_initialise(RCL_Instance instance)
path = RCL_GetDriverParameter(instance);
sockfd = SCK_OpenUnixDatagramSocket(NULL, path, 0);
if (sockfd < 0)
LOG_FATAL("Could not open socket %s", path);
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;
}
RCL_SetDriverData(instance, (void *)(long)sockfd);
SCH_AddFileHandler(sockfd, SCH_FILE_INPUT, read_sample, instance);
@@ -120,8 +136,7 @@ static void sock_finalise(RCL_Instance instance)
sockfd = (long)RCL_GetDriverData(instance);
SCH_RemoveFileHandler(sockfd);
SCK_RemoveSocket(sockfd);
SCK_CloseSocket(sockfd);
close(sockfd);
}
RefclockDriver RCL_SOCK_driver = {

View File

@@ -42,18 +42,11 @@
/* 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;
@@ -65,8 +58,6 @@ static double our_skew;
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;
@@ -112,9 +103,6 @@ static void update_drift_file(double, double);
/* 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;
@@ -146,8 +134,8 @@ static struct fb_drift *fb_drifts = NULL;
static int next_fb_drift;
static SCH_TimeoutID fb_drift_timeout_id;
/* Monotonic timestamp of the last reference update */
static double last_ref_update;
/* Timestamp of last reference update */
static struct timespec last_ref_update;
static double last_ref_update_interval;
/* ================================================== */
@@ -172,8 +160,9 @@ handle_slew(struct timespec *raw,
UTI_AdjustTimespec(&our_ref_time, cooked, &our_ref_time, &delta, dfreq, doffset);
if (change_type == LCL_ChangeUnknownStep) {
last_ref_update = 0.0;
REF_SetUnsynchronised();
UTI_ZeroTimespec(&last_ref_update);
} else if (last_ref_update.tv_sec) {
UTI_AdjustTimespec(&last_ref_update, cooked, &last_ref_update, &delta, dfreq, doffset);
}
/* When the clock was stepped, check if that doesn't change our leap status
@@ -205,14 +194,12 @@ REF_Initialise(void)
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 = UTI_OpenFile(NULL, drift_file, NULL, 'r', 0);
in = fopen(drift_file, "r");
if (in) {
if (fscanf(in, "%lf%lf", &file_freq_ppm, &file_skew_ppm) == 2) {
/* We have read valid data */
@@ -247,9 +234,7 @@ REF_Initialise(void)
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();
@@ -284,7 +269,7 @@ REF_Initialise(void)
}
UTI_ZeroTimespec(&our_ref_time);
last_ref_update = 0.0;
UTI_ZeroTimespec(&last_ref_update);
last_ref_update_interval = 0.0;
LCL_AddParameterChangeHandler(handle_slew, NULL);
@@ -346,20 +331,61 @@ REF_GetLeapMode(void)
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. */
out = UTI_OpenFile(NULL, drift_file, ".tmp", 'w', 0644);
if (!out)
temp_drift_file = (char*) Malloc(strlen(drift_file)+8);
if(!temp_drift_file) {
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 */
fprintf(out, "%20.6f %20.6f\n", freq_ppm, 1.0e6 * skew);
fclose(out);
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;
}
/* Rename the temporary file to the correct location */
if (!UTI_RenameTempFile(NULL, drift_file, ".tmp", NULL))
;
/* 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);
}
}
/* 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);
}
/* ================================================== */
@@ -425,16 +451,16 @@ fb_drift_timeout(void *arg)
/* ================================================== */
static void
schedule_fb_drift(void)
schedule_fb_drift(struct timespec *now)
{
int i, c, secs;
double unsynchronised, now;
double unsynchronised;
struct timespec when;
if (fb_drift_timeout_id)
return; /* already scheduled */
now = SCH_GetLastEventMonoTime();
unsynchronised = now - last_ref_update;
unsynchronised = UTI_DiffTimespecsToDouble(now, &last_ref_update);
for (c = secs = 0, i = fb_drift_min; i <= fb_drift_max; i++) {
secs = 1 << i;
@@ -456,7 +482,8 @@ schedule_fb_drift(void)
if (i <= fb_drift_max) {
next_fb_drift = i;
fb_drift_timeout_id = SCH_AddTimeoutByDelay(secs - unsynchronised, fb_drift_timeout, NULL);
UTI_AddDoubleToTimespec(now, secs - unsynchronised, &when);
fb_drift_timeout_id = SCH_AddTimeout(&when, fb_drift_timeout, NULL);
DEBUG_LOG("Fallback drift %d scheduled", i);
}
}
@@ -724,12 +751,10 @@ set_leap_timeout(time_t now)
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 = leap_when;
when.tv_sec = (now / (24 * 3600) + 1) * (24 * 3600);
when.tv_nsec = 0;
if (our_leap_sec < 0)
when.tv_sec--;
@@ -773,7 +798,7 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
}
if ((leap_sec != our_leap_sec || tai_offset != our_tai_offset)
&& !REF_IsLeapSecondClose(NULL, 0.0)) {
&& !REF_IsLeapSecondClose()) {
our_leap_sec = leap_sec;
our_tai_offset = tai_offset;
@@ -812,20 +837,6 @@ get_root_dispersion(struct timespec *ts)
/* ================================================== */
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,
@@ -948,18 +959,6 @@ get_clock_estimates(int manual,
/* ================================================== */
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,
@@ -969,8 +968,9 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
{
double uncorrected_offset, accumulate_offset, step_offset;
double residual_frequency, local_abs_frequency;
double elapsed, mono_now, update_interval, correction_rate, orig_root_distance;
double elapsed, update_interval, correction_rate, orig_root_distance;
struct timespec now, raw_now;
NTP_int64 ref_fuzz;
int manual;
assert(initialised);
@@ -983,16 +983,17 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
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 != 0.0) {
update_interval = mono_now - last_ref_update;
if (last_ref_update.tv_sec) {
update_interval = UTI_DiffTimespecsToDouble(&now, &last_ref_update);
update_interval = MAX(update_interval, 0.0);
} else {
update_interval = 0.0;
}
@@ -1018,9 +1019,7 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
our_residual_freq = residual_frequency;
our_root_delay = root_delay;
our_root_dispersion = root_dispersion;
our_frequency_sd = offset_sd;
our_offset_sd = offset_sd;
last_ref_update = mono_now;
last_ref_update = now;
last_ref_update_interval = update_interval;
last_offset = offset;
@@ -1052,6 +1051,7 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
/* 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,13 +1059,17 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", -step_offset);
}
update_leap_status(leap, raw_now.tv_sec, 0);
update_sync_status(&now);
LCL_SetSyncStatus(are_we_synchronised, offset_sd,
root_delay / 2.0 + get_root_dispersion(&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 */
fuzz_ref_time(&our_ref_time);
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--;
local_abs_frequency = LCL_ReadAbsoluteFrequency();
@@ -1075,7 +1079,7 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
if (drift_file) {
/* Update drift file at most once per hour */
drift_file_age += update_interval;
if (drift_file_age >= MAX_DRIFTFILE_AGE) {
if (drift_file_age < 0.0 || drift_file_age > 3600.0) {
update_drift_file(local_abs_frequency, our_skew);
drift_file_age = 0.0;
}
@@ -1084,7 +1088,7 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
/* Update fallback drifts */
if (fb_drifts && are_we_synchronised) {
update_fb_drifts(local_abs_frequency, update_interval);
schedule_fb_drift();
schedule_fb_drift(&now);
}
/* Update the moving average of squares of offset, quickly on start */
@@ -1137,7 +1141,7 @@ REF_SetUnsynchronised(void)
UTI_AddDoubleToTimespec(&now_raw, uncorrected_offset, &now);
if (fb_drifts) {
schedule_fb_drift();
schedule_fb_drift(&now);
}
update_leap_status(LEAP_Unsynchronised, 0, 0);
@@ -1154,25 +1158,6 @@ REF_SetUnsynchronised(void)
/* ================================================== */
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
(
@@ -1186,7 +1171,7 @@ REF_GetReferenceParams
double *root_dispersion
)
{
double dispersion, delta;
double dispersion;
assert(initialised);
@@ -1218,17 +1203,13 @@ REF_GetReferenceParams
*stratum = local_stratum;
*ref_id = NTP_REFID_LOCAL;
/* 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;
/* 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;
/* Not much else we can do for leap second bits - maybe need to
have a way for the administrator to feed leap bits in */
@@ -1330,24 +1311,22 @@ REF_DisableLocal(void)
#define LEAP_SECOND_CLOSE 5
static int
is_leap_close(time_t t)
{
return t >= leap_when - LEAP_SECOND_CLOSE && t < leap_when + LEAP_SECOND_CLOSE;
}
/* ================================================== */
int REF_IsLeapSecondClose(struct timespec *ts, double offset)
int REF_IsLeapSecondClose(void)
{
struct timespec now, now_raw;
time_t t;
if (!our_leap_sec)
return 0;
SCH_GetLastEventTime(&now, NULL, &now_raw);
if (is_leap_close(now.tv_sec) || is_leap_close(now_raw.tv_sec))
t = now.tv_sec > 0 ? now.tv_sec : -now.tv_sec;
if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE)
return 1;
if (ts && (is_leap_close(ts->tv_sec) || is_leap_close(ts->tv_sec + offset)))
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)
return 1;
return 0;

View File

@@ -162,9 +162,6 @@ extern void REF_SetManualReference
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);
@@ -184,9 +181,9 @@ extern void REF_ModifyMakestep(int limit, double threshold);
extern void REF_EnableLocal(int stratum, double distance, int orphan);
extern void REF_DisableLocal(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);
/* 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);
/* Return TAI-UTC offset corresponding to a time in UTC if available */
extern int REF_GetTaiOffset(struct timespec *ts);

2
rtc.c
View File

@@ -160,7 +160,7 @@ RTC_Initialise(int initial_set)
void
RTC_Finalise(void)
{
if (driver_initialised) {
if (driver.fini) {
(driver.fini)();
}
}

View File

@@ -390,9 +390,12 @@ read_hwclock_file(const char *hwclock_file)
if (!hwclock_file || !hwclock_file[0])
return;
in = UTI_OpenFile(NULL, hwclock_file, NULL, 'r', 0);
if (!in)
in = fopen(hwclock_file, "r");
if (!in) {
LOG(LOGS_WARN, "Could not open %s : %s",
hwclock_file, strerror(errno));
return;
}
/* Read third line from the file. */
for (i = 0; i < 3; i++) {
@@ -442,8 +445,7 @@ read_coefs_from_file(void)
tried_to_load_coefs = 1;
if (coefs_file_name &&
(in = UTI_OpenFile(NULL, coefs_file_name, NULL, 'r', 0))) {
if (coefs_file_name && (in = fopen(coefs_file_name, "r"))) {
if (fscanf(in, "%d%ld%lf%lf",
&valid_coefs_from_file,
&file_ref_time,
@@ -464,40 +466,67 @@ read_coefs_from_file(void)
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. */
out = UTI_OpenFile(NULL, coefs_file_name, ".tmp", 'w', 0644);
if (!out)
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);
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);
fclose(out);
/* Rename the temporary file to the correct location */
if (!UTI_RenameTempFile(NULL, coefs_file_name, ".tmp", NULL))
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;
}
/* Clone the file attributes from the existing file if there is one. */
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);
}
}
/* 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;
}
Free(temp_coefs_file_name);
return RTC_ST_OK;
}
/* ================================================== */
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;
}
if (on_off)
skip_interrupts = 1;
return 1;
}
/* ================================================== */
/* file_name is the name of the file where we save the RTC params
@@ -507,23 +536,6 @@ 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;
}
/* 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);
@@ -534,6 +546,18 @@ 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;
@@ -566,7 +590,6 @@ RTC_Linux_Finalise(void)
/* Remove input file handler */
if (fd >= 0) {
SCH_RemoveFileHandler(fd);
switch_interrupts(0);
close(fd);
/* Save the RTC data */
@@ -580,6 +603,29 @@ 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)
{

View File

@@ -267,7 +267,7 @@ select_samples(SPF_Instance filter)
}
}
for (i = j = 0; i < filter->used; i++) {
for (i = j = 0, k = -1; i < filter->used; i++) {
if (selected[i] != -1)
selected[j++] = (selected[i] + filter->used - o) % filter->used;
}
@@ -387,6 +387,7 @@ combine_selected_samples(SPF_Instance filter, int n, NTP_Sample *result)
result->peer_delay = mean_peer_delay;
result->root_delay = mean_root_delay;
result->stratum = last_sample->stratum;
result->leap = last_sample->leap;
return 1;
}

59
sched.c
View File

@@ -65,12 +65,6 @@ static ARR_Instance file_handlers;
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 */
@@ -142,8 +136,6 @@ SCH_Initialise(void)
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;
}
@@ -155,8 +147,6 @@ void
SCH_Finalise(void) {
ARR_DestroyInstance(file_handlers);
LCL_RemoveParameterChangeHandler(handle_slew, NULL);
initialised = 0;
}
@@ -257,14 +247,6 @@ SCH_GetLastEventTime(struct timespec *cooked, double *err, struct timespec *raw)
/* ================================================== */
double
SCH_GetLastEventMonoTime(void)
{
return last_select_ts_mono;
}
/* ================================================== */
#define TQE_ALLOC_QUANTUM 32
static TimerQueueEntry *
@@ -490,20 +472,6 @@ SCH_RemoveTimeout(SCH_TimeoutID id)
assert(0);
}
/* ================================================== */
void
SCH_Reset(void)
{
while (n_timer_queue_entries > 0)
SCH_RemoveTimeout(timer_queue.next->id);
while (one_highest_fd > 0) {
close(one_highest_fd - 1);
SCH_RemoveFileHandler(one_highest_fd - 1);
}
}
/* ================================================== */
/* Try to dispatch any timeouts that have already gone by, and
keep going until all are done. (The earlier ones may take so
@@ -738,31 +706,6 @@ check_current_time(struct timespec *prev_raw, struct timespec *raw, int timeout,
/* ================================================== */
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)
{
@@ -819,8 +762,6 @@ SCH_MainLoop(void)
LCL_CookTime(&now, &cooked, &err);
}
update_monotonic_time(&cooked, &last_select_ts);
last_select_ts_raw = now;
last_select_ts = cooked;
last_select_ts_err = err;

View File

@@ -65,9 +65,6 @@ extern void SCH_SetFileHandlerEvent(int fd, int event, int enable);
/* 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);
@@ -85,9 +82,6 @@ extern SCH_TimeoutID SCH_AddTimeoutInClass(double min_delay, double separation,
/* The next one probably ought to return a status code */
extern void SCH_RemoveTimeout(SCH_TimeoutID);
/* Remove all timeouts and close all file descriptors */
extern void SCH_Reset(void);
extern void SCH_MainLoop(void);
extern void SCH_QuitProgram(void);

70
siv.h
View File

@@ -1,70 +0,0 @@
/*
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

View File

@@ -1,142 +0,0 @@
/*
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 <nettle/siv-cmac.h>
#else
#include "siv_nettle_int.c"
#endif
#include "memory.h"
#include "siv.h"
struct SIV_Instance_Record {
struct siv_cmac_aes128_ctx siv;
};
/* ================================================== */
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);
return instance;
}
/* ================================================== */
void
SIV_DestroyInstance(SIV_Instance instance)
{
Free(instance);
}
/* ================================================== */
int
SIV_GetKeyLength(SIV_Algorithm algorithm)
{
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);
return 1;
}
/* ================================================== */
int
SIV_GetTagLength(SIV_Instance instance)
{
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 (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 (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;
}

View File

@@ -1,452 +0,0 @@
/* 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 <assert.h>
#include <string.h>
#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);
}

1530
socket.c

File diff suppressed because it is too large Load Diff

141
socket.h
View File

@@ -1,141 +0,0 @@
/*
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;
unsigned 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 */
extern void SCK_Initialise(void);
/* Finalisation function */
extern void SCK_Finalise(void);
/* Check if support for the IP family was enabled in the build */
extern int SCK_IsFamilySupported(int family);
/* 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);
/* Specify a bind()-like function for binding sockets to privileged ports when
running in a restricted process (e.g. after dropping root privileges) */
extern void SCK_SetPrivBind(int (*function)(int sock_fd, struct sockaddr *address,
socklen_t address_len));
/* Open socket */
extern int SCK_OpenUdpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int flags);
extern int SCK_OpenTcpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int flags);
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, unsigned int length, int flags);
extern int SCK_Send(int sock_fd, const void *buffer, unsigned 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

132
sources.c
View File

@@ -54,6 +54,7 @@ static int initialised = 0;
/* ================================================== */
/* Structure used to hold info for selecting between sources */
struct SelectInfo {
NTP_Leap leap;
int stratum;
int select_ok;
double std_dev;
@@ -125,12 +126,6 @@ struct SRC_Instance_Record {
double sel_score;
struct SelectInfo sel_info;
/* Latest leap status */
NTP_Leap leap;
/* Flag indicating the source has a leap second vote */
int leap_vote;
};
/* ================================================== */
@@ -254,7 +249,6 @@ SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_optio
result->index = n_sources;
result->type = type;
result->sel_options = sel_options;
result->active = 0;
SRC_SetRefid(result, ref_id, addr);
SRC_ResetInstance(result);
@@ -297,14 +291,13 @@ void SRC_DestroyInstance(SRC_Instance instance)
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->leap = LEAP_Unsynchronised;
instance->leap_vote = 0;
SST_ResetInstance(instance->stats);
}
@@ -330,48 +323,6 @@ SRC_GetSourcestats(SRC_Instance instance)
/* ================================================== */
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_SetLeapStatus(SRC_Instance inst, NTP_Leap leap)
{
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.
@@ -390,7 +341,7 @@ SRC_AccumulateSample(SRC_Instance inst, NTP_Sample *sample)
source_to_string(inst), UTI_TimespecToString(&sample->time), -sample->offset,
sample->root_delay, sample->root_dispersion, sample->stratum);
if (REF_IsLeapSecondClose(&sample->time, sample->offset)) {
if (REF_IsLeapSecondClose()) {
LOG(LOGS_INFO, "Dropping sample around leap second");
return;
}
@@ -483,7 +434,7 @@ SRC_ResetReachability(SRC_Instance inst)
/* ================================================== */
static void
log_selection_message(const char *format, const char *arg)
log_selection_message(char *format, char *arg)
{
if (REF_GetMode() != REF_ModeNormal)
return;
@@ -644,7 +595,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
int n_badstats_sources, max_sel_reach, max_sel_reach_size, max_badstat_reach;
int depth, best_depth, trust_depth, best_trust_depth;
int combined, stratum, min_stratum, max_score_index;
int orphan_stratum, orphan_source;
int orphan_stratum, orphan_source, leap_votes, leap_ins, leap_del;
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;
@@ -691,7 +642,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
}
si = &sources[i]->sel_info;
SST_GetSelectionData(sources[i]->stats, &now, &si->stratum,
SST_GetSelectionData(sources[i]->stats, &now, &si->stratum, &si->leap,
&si->lo_limit, &si->hi_limit, &si->root_distance,
&si->std_dev, &first_sample_ago,
&si->last_sample_ago, &si->select_ok);
@@ -727,7 +678,6 @@ SRC_SelectSource(SRC_Instance updated_inst)
}
sources[i]->status = SRC_OK; /* For now */
sources[i]->leap_vote = 0;
if (sources[i]->reachability && max_reach_sample_ago < first_sample_ago)
max_reach_sample_ago = first_sample_ago;
@@ -964,15 +914,26 @@ SRC_SelectSource(SRC_Instance updated_inst)
return;
}
/* Enable the selectable sources (and trusted if there are any) to
vote on leap seconds */
for (i = 0; i < n_sel_sources; i++) {
/* 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++) {
index = sel_sources[i];
if (best_trust_depth && !(sources[index]->sel_options & SRC_SELECT_TRUST))
continue;
sources[index]->leap_vote = 1;
leap_votes++;
if (sources[index]->sel_info.leap == LEAP_InsertSecond)
leap_ins++;
else if (sources[index]->sel_info.leap == LEAP_DeleteSecond)
leap_del++;
}
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++) {
@@ -1104,8 +1065,6 @@ SRC_SelectSource(SRC_Instance updated_inst)
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 */
@@ -1169,7 +1128,7 @@ slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq,
}
if (change_type == LCL_ChangeUnknownStep) {
/* Update selection status */
/* After resetting no source is selectable, set reference unsynchronised */
SRC_SelectSource(NULL);
}
}
@@ -1191,24 +1150,34 @@ add_dispersion(double dispersion, void *anything)
/* ================================================== */
static
FILE *open_dumpfile(SRC_Instance inst, char mode)
FILE *open_dumpfile(SRC_Instance inst, const char *mode)
{
char filename[64], *dumpdir;
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 && UTI_IsIPReal(inst->ip_addr))
snprintf(filename, sizeof (filename), "%s", source_to_string(inst));
else if (inst->type == SRC_REFCLOCK)
snprintf(filename, sizeof (filename), "refid:%08"PRIx32, inst->ref_id);
else
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;
}
return UTI_OpenFile(dumpdir, filename, ".dat", mode, 0644);
f = fopen(filename, mode);
if (!f && mode[0] != 'r')
LOG(LOGS_WARN, "Could not open dump file for %s",
source_to_string(inst));
return f;
}
/* ================================================== */
@@ -1221,7 +1190,7 @@ SRC_DumpSources(void)
int i;
for (i = 0; i < n_sources; i++) {
out = open_dumpfile(sources[i], 'w');
out = open_dumpfile(sources[i], "w");
if (!out)
continue;
SST_SaveToFile(sources[i]->stats, out);
@@ -1238,7 +1207,7 @@ SRC_ReloadSources(void)
int i;
for (i = 0; i < n_sources; i++) {
in = open_dumpfile(sources[i], 'r');
in = open_dumpfile(sources[i], "r");
if (!in)
continue;
if (!SST_LoadFromFile(sources[i]->stats, in))
@@ -1256,7 +1225,7 @@ SRC_ReloadSources(void)
void
SRC_RemoveDumpFiles(void)
{
char pattern[PATH_MAX], name[64], *dumpdir, *s;
char pattern[1024], name[64], *dumpdir, *s;
IPAddr ip_addr;
glob_t gl;
size_t i;
@@ -1283,8 +1252,8 @@ SRC_RemoveDumpFiles(void)
if (strncmp(name, "refid:", 6) && !UTI_StringToIP(name, &ip_addr))
continue;
if (!UTI_RemoveFile(NULL, gl.gl_pathv[i], NULL))
;
DEBUG_LOG("Removing %s", gl.gl_pathv[i]);
unlink(gl.gl_pathv[i]);
}
globfree(&gl);
@@ -1292,17 +1261,6 @@ SRC_RemoveDumpFiles(void)
/* ================================================== */
void
SRC_ResetSources(void)
{
int i;
for (i = 0; i < n_sources; i++)
SRC_ResetInstance(sources[i]);
}
/* ================================================== */
int
SRC_IsSyncPeer(SRC_Instance inst)
{

View File

@@ -79,10 +79,8 @@ extern void SRC_SetRefid(SRC_Instance instance, uint32_t ref_id, IPAddr *addr);
/* Function to get access to the sourcestats instance */
extern SST_Stats SRC_GetSourcestats(SRC_Instance instance);
/* Function to set the current leap status according to the source */
extern void SRC_SetLeapStatus(SRC_Instance instance, NTP_Leap leap);
/* Function to accumulate a new sample from the source */
/* This function is called by one of the source drivers when it has
a new sample that is to be accumulated */
extern void SRC_AccumulateSample(SRC_Instance instance, NTP_Sample *sample);
/* This routine sets the source as receiving reachability updates */
@@ -116,8 +114,6 @@ extern void SRC_DumpSources(void);
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);

View File

@@ -54,9 +54,6 @@
/* 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
@@ -180,6 +177,9 @@ struct SST_Stats_Record {
/* The stratum from the last accumulated sample */
int stratum;
/* The leap status from the last accumulated sample */
NTP_Leap leap;
};
/* ================================================== */
@@ -249,12 +249,13 @@ SST_ResetInstance(SST_Stats inst)
inst->estimated_frequency_sd = WORST_CASE_FREQ_BOUND;
inst->skew = WORST_CASE_FREQ_BOUND;
inst->estimated_offset = 0.0;
inst->estimated_offset_sd = WORST_CASE_STDDEV_BOUND;
inst->estimated_offset_sd = 86400.0; /* Assume it's at least within a day! */
UTI_ZeroTimespec(&inst->offset_time);
inst->std_dev = WORST_CASE_STDDEV_BOUND;
inst->std_dev = 4.0;
inst->nruns = 0;
inst->asymmetry_run = 0;
inst->asymmetry = 0.0;
inst->leap = LEAP_Unsynchronised;
}
/* ================================================== */
@@ -322,6 +323,7 @@ SST_AccumulateSample(SST_Stats inst, NTP_Sample *sample)
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];
@@ -601,20 +603,9 @@ SST_DoNewRegression(SST_Stats inst)
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;
}
@@ -650,7 +641,7 @@ SST_GetFrequencyRange(SST_Stats inst,
void
SST_GetSelectionData(SST_Stats inst, struct timespec *now,
int *stratum,
int *stratum, NTP_Leap *leap,
double *offset_lo_limit,
double *offset_hi_limit,
double *root_distance,
@@ -671,6 +662,7 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
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]));
@@ -702,13 +694,6 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
*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);
@@ -1008,24 +993,31 @@ SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct ti
{
double dspan;
double elapsed, sample_elapsed;
int bi, bj;
int li, lj, bi, bj;
report->n_samples = inst->n_samples;
report->n_runs = inst->nruns;
if (inst->n_samples > 0) {
bi = get_runsbuf_index(inst, inst->best_single_sample);
bj = get_buf_index(inst, inst->best_single_sample);
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);
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]);
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];
}
} else {
report->span_seconds = 0;
report->est_offset = 0;

View File

@@ -69,7 +69,7 @@ extern void SST_GetFrequencyRange(SST_Stats inst, double *lo, double *hi);
/* Get data needed for selection */
extern void
SST_GetSelectionData(SST_Stats inst, struct timespec *now,
int *stratum,
int *stratum, NTP_Leap *leap,
double *offset_lo_limit,
double *offset_hi_limit,
double *root_distance,

View File

@@ -52,8 +52,6 @@ typedef struct {
int filter_length;
int interleaved;
int sel_options;
int nts;
int nts_port;
uint32_t authkey;
double max_delay;
double max_delay_ratio;
@@ -76,7 +74,6 @@ typedef struct {
#define SRC_DEFAULT_MINSAMPLES (-1)
#define SRC_DEFAULT_MAXSAMPLES (-1)
#define SRC_DEFAULT_ASYMMETRY 1.0
#define SRC_DEFAULT_NTSPORT 11443
#define INACTIVE_AUTHKEY 0
/* Flags for source selection */

150
stubs.c
View File

@@ -28,7 +28,6 @@
#include "config.h"
#include "clientlog.h"
#include "cmac.h"
#include "cmdmon.h"
#include "keys.h"
#include "logging.h"
@@ -40,10 +39,6 @@
#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"
@@ -199,10 +194,9 @@ NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParam
return NSR_TooManySources;
}
NSR_Status
void
NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, SourceParameters *params)
{
return NSR_TooManySources;
}
NSR_Status
@@ -226,12 +220,6 @@ NSR_RefreshAddresses(void)
{
}
char *
NSR_GetName(IPAddr *address)
{
return NULL;
}
void
NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler)
{
@@ -331,11 +319,6 @@ NSR_GetActivityReport(RPT_ActivityReport *report)
memset(report, 0, sizeof (*report));
}
void
NSR_DumpAuthData(void)
{
}
#ifndef FEAT_CMDMON
void
@@ -421,138 +404,9 @@ NSD_GetAuthDelay(uint32_t key_id)
}
int
NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
{
return 0;
}
#endif /* !FEAT_SIGND */
#ifndef HAVE_CMAC
unsigned int
CMC_GetKeyLength(const char *cipher)
{
return 0;
}
CMC_Instance
CMC_CreateInstance(const char *cipher, const unsigned char *key, unsigned int length)
{
return NULL;
}
unsigned int
CMC_Hash(CMC_Instance inst, const unsigned char *in, unsigned int in_len,
unsigned char *out, unsigned int out_len)
{
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, const IPSockAddr *ntp_address)
{
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)
{
DEBUG_LOG("NTS support disabled");
return 0;
}
int
NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info)
{
DEBUG_LOG("NTS support disabled");
return 0;
}
void
NNC_ChangeAddress(NNC_Instance inst, IPAddr *address)
{
}
void
NNC_DumpData(NNC_Instance inst)
{
}
void
NKC_Initialise(void)
{
}
void
NKC_Finalise(void)
{
}
void
NKS_Initialise(int scfilter_level)
{
}
void
NKS_Finalise(void)
{
}
void
NKS_DumpKeys(void)
{
}
void
NKS_ReloadKeys(void)
{
}
#endif /* !FEAT_NTS */

4
sys.c
View File

@@ -114,10 +114,10 @@ void SYS_DropRoot(uid_t uid, gid_t gid)
/* ================================================== */
void SYS_EnableSystemCallFilter(int level, SYS_SystemCallContext context)
void SYS_EnableSystemCallFilter(int level)
{
#if defined(LINUX) && defined(FEAT_SCFILTER)
SYS_Linux_EnableSystemCallFilter(level, context);
SYS_Linux_EnableSystemCallFilter(level);
#else
LOG_FATAL("system call filter not supported");
#endif

7
sys.h
View File

@@ -38,14 +38,9 @@ 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_SystemCallContext;
/* Enable a system call filter to allow only system calls
which chronyd normally needs after initialization */
extern void SYS_EnableSystemCallFilter(int level, SYS_SystemCallContext context);
extern void SYS_EnableSystemCallFilter(int level);
extern void SYS_SetScheduler(int SchedPriority);
extern void SYS_LockMemory(void);

View File

@@ -474,15 +474,15 @@ void check_seccomp_applicability(void)
/* ================================================== */
void
SYS_Linux_EnableSystemCallFilter(int level, SYS_SystemCallContext context)
SYS_Linux_EnableSystemCallFilter(int level)
{
const int syscalls[] = {
/* Clock */
SCMP_SYS(adjtimex), SCMP_SYS(clock_adjtime), SCMP_SYS(clock_gettime),
SCMP_SYS(gettimeofday), SCMP_SYS(settimeofday), SCMP_SYS(time),
SCMP_SYS(adjtimex), SCMP_SYS(clock_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(getuid), SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigreturn),
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),
/* Memory */
@@ -493,13 +493,12 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_SystemCallContext context)
SCMP_SYS(chown32), SCMP_SYS(faccessat), SCMP_SYS(fchmodat), SCMP_SYS(fchownat),
SCMP_SYS(fstat), SCMP_SYS(fstat64), SCMP_SYS(getdents), SCMP_SYS(getdents64),
SCMP_SYS(lseek), SCMP_SYS(newfstatat), SCMP_SYS(rename), SCMP_SYS(renameat),
SCMP_SYS(renameat2), SCMP_SYS(stat), SCMP_SYS(stat64), SCMP_SYS(statfs),
SCMP_SYS(statfs64), SCMP_SYS(unlink), SCMP_SYS(unlinkat),
SCMP_SYS(stat), SCMP_SYS(stat64), SCMP_SYS(statfs), SCMP_SYS(statfs64),
SCMP_SYS(unlink), SCMP_SYS(unlinkat),
/* Socket */
SCMP_SYS(accept), SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname),
SCMP_SYS(getsockopt), SCMP_SYS(recv), SCMP_SYS(recvfrom),
SCMP_SYS(recvmmsg), SCMP_SYS(recvmsg), SCMP_SYS(send), SCMP_SYS(sendmmsg),
SCMP_SYS(sendmsg), SCMP_SYS(sendto), SCMP_SYS(shutdown),
SCMP_SYS(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),
/* TODO: check socketcall arguments */
SCMP_SYS(socketcall),
/* General I/O */
@@ -523,16 +522,13 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_SystemCallContext context)
{ SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO },
#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_GETFL, F_SETFL };
const static int fcntls[] = { F_GETFD, F_SETFD, F_SETFL };
const static unsigned long ioctls[] = {
FIONREAD, TCGETS,
@@ -562,16 +558,14 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_SystemCallContext context)
scmp_filter_ctx *ctx;
int i;
if (context == SYS_MAIN_PROCESS) {
/* Check if the chronyd configuration is supported */
check_seccomp_applicability();
/* Check if the chronyd configuration is supported */
check_seccomp_applicability();
/* 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();
}
/* 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();
ctx = seccomp_init(level > 0 ? SCMP_ACT_KILL : SCMP_ACT_TRAP);
if (ctx == NULL)
@@ -583,44 +577,42 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_SystemCallContext context)
goto add_failed;
}
if (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 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 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 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 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 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;
}
/* 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 (seccomp_load(ctx) < 0)
LOG_FATAL("Failed to load seccomp rules");
LOG(context == SYS_MAIN_PROCESS ? LOGS_INFO : LOGS_DEBUG, "Loaded seccomp filter");
LOG(LOGS_INFO, "Loaded seccomp filter");
seccomp_release(ctx);
return;

View File

@@ -27,15 +27,13 @@
#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_EnableSystemCallFilter(int level, SYS_SystemCallContext context);
extern void SYS_Linux_EnableSystemCallFilter(int level);
extern int SYS_Linux_CheckKernelVersion(int req_major, int req_minor);

View File

@@ -46,9 +46,8 @@
#include "privops.h"
#include "util.h"
#include <dlfcn.h>
#ifdef HAVE_MACOS_SYS_TIMEX
#include <dlfcn.h>
#include "sys_netbsd.h"
static int have_ntp_adjtime = 0;
@@ -452,39 +451,6 @@ legacy_MacOSX_Finalise(void)
/* ================================================== */
#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)
{

View File

@@ -256,8 +256,10 @@ SYS_Timex_Adjust(struct timex *txc, int ignore_error)
state = NTP_ADJTIME(txc);
if (state < 0) {
LOG(ignore_error ? LOGS_DEBUG : LOGS_FATAL,
NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, strerror(errno));
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));
}
return state;

View File

@@ -21,8 +21,9 @@
=======================================================================
This file includes most system header files that the software
requires to better isolate system dependencies.
This file includes all system header files that the software
requires. This allows us to isolate system dependencies to this file
alone.
*/
#ifndef GOT_SYSINCL_H
@@ -54,6 +55,7 @@
#include <sys/un.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>

View File

@@ -84,7 +84,7 @@ read_timeout(void *arg)
FILE *f;
double temp, comp;
f = UTI_OpenFile(NULL, filename, NULL, 'r', 0);
f = fopen(filename, "r");
if (f && fscanf(f, "%lf", &temp) == 1) {
comp = get_tempcomp(temp);
@@ -122,7 +122,11 @@ read_points(const char *filename)
char line[256];
struct Point *p;
f = UTI_OpenFile(NULL, filename, NULL, 'R', 0);
f = fopen(filename, "r");
if (!f) {
LOG_FATAL("Could not open tempcomp point file %s", filename);
return;
}
points = ARR_CreateInstance(sizeof (struct Point));

View File

@@ -18,7 +18,6 @@ for opts in \
"--disable-sechash" \
"--disable-cmdmon" \
"--disable-ntp" \
"--disable-nts" \
"--disable-refclock" \
"--disable-timestamping" \
"--disable-timestamping --disable-ntp" \
@@ -27,7 +26,5 @@ for opts in \
"--disable-cmdmon --disable-ntp --disable-refclock"
do
./configure $opts || exit 1
make clean
make "$@" || exit 1
make -C test/unit check || exit 1
done

View File

@@ -14,7 +14,7 @@ fi
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,gnutls}-devel.{x86_64,i686} || exit 1
rpm -q {libseccomp,nettle,nss-softokn-freebl,libtomcrypt}-devel.{x86_64,i686} || exit 1
echo
fi
@@ -34,7 +34,6 @@ for CC in gcc clang; do
for extra_config_opts in \
"--all-privops" \
"--disable-scfilter" \
"--without-gnutls" \
"--without-nettle" \
"--without-nettle --without-nss" \
"--without-nettle --without-nss --without-tomcrypt"; \

View File

@@ -1,15 +0,0 @@
#!/bin/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

View File

@@ -23,20 +23,16 @@ EOF
keys=4
types="MD5"
check_config_h 'FEAT_SECHASH 1' && types="$types SHA1 SHA256 SHA384 SHA512"
check_config_h 'HAVE_CMAC 1' && types="$types AES128 AES256"
if check_config_h 'FEAT_SECHASH 1'; then
hashes="MD5 SHA1 SHA256 SHA384 SHA512"
else
hashes="MD5"
fi
for type in $types; do
for hash in $hashes; do
keys=$[$keys + 1]
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]))
key=$(echo $keys $hash HEX:$(tr -c -d '0-9A-F' < /dev/urandom 2> /dev/null | \
head -c $[$RANDOM % 64 * 2 + 2]))
echo "$key" >> tmp/server.keys
echo "$key" >> tmp/client.keys
done
@@ -74,18 +70,13 @@ clients=2
peers=2
max_sync_time=500
base_delay="$default_base_delay (* -1 (equal 0.1 from 3) (equal 0.1 to 1))"
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 1"
run_test || test_fail
check_chronyd_exit || test_fail
check_sync || test_fail
client_rpeer_options="key 2"
run_test || test_fail

View File

@@ -43,10 +43,12 @@ Root delay : 0\.000...... seconds
Root dispersion : 0\.000...... seconds
Update interval : [0-9]+\.. seconds
Leap status : Normal
210 Number of sources = 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
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
@@ -64,15 +66,6 @@ try: 1, refid: C0A87B01, correction: 0\.000......, skew: .\....
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
server_strata=0
chronyc_start=0
client_conf=""
@@ -82,7 +75,6 @@ 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 node1.net1.clk" \
"allow 1.2.3.4" \
"allow 1.2" \
"allow 3.4.5" \
@@ -104,7 +96,6 @@ for chronyc_conf in \
"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" \
@@ -125,7 +116,6 @@ for chronyc_conf in \
"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" \
@@ -138,7 +128,6 @@ for chronyc_conf in \
"rekey" \
"reselect" \
"reselectdist 1e-3" \
"reset" \
"settime 16:30" \
"settime 16:30:05" \
"settime Nov 21, 2015 16:30:05" \
@@ -163,7 +152,6 @@ timeout 200
retries 1
keygen
keygen 10 MD5 128
keygen 11 MD5 40
help
quit
nosuchcommand"
@@ -172,29 +160,7 @@ run_test || test_fail
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
test_pass

View File

@@ -15,10 +15,11 @@ chronyc_start="1.5"
chronyc_conf="tracking"
for year in `seq 1850 100 2300`; do
export CLKNETSIM_START_DATE=$(date -d "Jan 01 00:00:05 $year UTC" +'%s')
date="Jan 01 00:00:00 $year"
export CLKNETSIM_START_DATE=$(date -d "$date UTC" +'%s')
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^.*Ref time \(UTC\).*Jan 01 00:00:0. $year.*$" || test_fail
check_chronyc_output "^.*Ref time \(UTC\).*$date.*$" || test_fail
done
test_pass

View File

@@ -11,9 +11,9 @@ server_conf="local stratum 5 orphan
server 192.168.123.1
server 192.168.123.2
server 192.168.123.3"
max_sync_time=900
max_sync_time=500
client_start=140
chronyc_start=700
chronyc_start=300
chronyc_conf="tracking"
time_rms_limit=5e-4

View File

@@ -4,9 +4,6 @@
test_start "interleaved mode"
client_server_options="xleave"
client_conf="
logdir tmp
log rawmeasurements"
run_test || test_fail
check_chronyd_exit || test_fail
@@ -14,10 +11,6 @@ check_source_selection || test_fail
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
@@ -32,8 +25,6 @@ check_chronyd_exit || test_fail
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"
@@ -41,15 +32,6 @@ for rpoll in 4 5 6; do
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

View File

@@ -16,14 +16,6 @@ check_chronyd_exit || test_fail
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

View File

@@ -5,7 +5,7 @@
test_start "logchange directive"
time_offset=2
min_sync_time=590
min_sync_time=600
max_sync_time=700
client_server_options="maxsamples 6"
client_conf="logchange 0.1"

View File

@@ -27,10 +27,8 @@ acquisitionport 123"; do
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 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 "Received.*tss=1" 1 1 || test_fail
check_log_messages "Received.*tss=2" 390 400 || 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

View File

@@ -1,49 +0,0 @@
#!/bin/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

View File

@@ -1,34 +0,0 @@
#!/bin/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

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