mirror of
https://gitlab.com/chrony/chrony.git
synced 2025-12-04 15:15:07 -05:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0ac5992fb | ||
|
|
cd65e32cf0 | ||
|
|
b9f5278846 | ||
|
|
b8b166044f | ||
|
|
42fbf41686 | ||
|
|
79a790e6b5 | ||
|
|
f5cd79d2df | ||
|
|
689605b6a2 | ||
|
|
0707865413 | ||
|
|
2adda9c12c |
14
NEWS
14
NEWS
@@ -1,3 +1,17 @@
|
|||||||
|
New in version 4.6.1
|
||||||
|
====================
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
* Add ntsaeads directive to enable only selected AEAD algorithms for NTS
|
||||||
|
|
||||||
|
Workarounds
|
||||||
|
-----------
|
||||||
|
* Negotiate use of compliant NTS keys with AES-128-GCM-SIV AEAD algorithm
|
||||||
|
(by default the keys are generated differently than in RFC 8915 for
|
||||||
|
compatibility with chrony server and client versions 4.4, 4.5, and 4.6)
|
||||||
|
* Switch to compliant NTS keys if first response from server is NTS NAK
|
||||||
|
|
||||||
New in version 4.6
|
New in version 4.6
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
|||||||
41
conf.c
41
conf.c
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Richard P. Curnow 1997-2003
|
* Copyright (C) Richard P. Curnow 1997-2003
|
||||||
* Copyright (C) Miroslav Lichvar 2009-2017, 2020
|
* Copyright (C) Miroslav Lichvar 2009-2017, 2020, 2024
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -57,6 +57,7 @@ static int parse_string(char *line, char **result);
|
|||||||
static int parse_int(char *line, int *result);
|
static int parse_int(char *line, int *result);
|
||||||
static int parse_double(char *line, double *result);
|
static int parse_double(char *line, double *result);
|
||||||
static int parse_null(char *line);
|
static int parse_null(char *line);
|
||||||
|
static int parse_ints(char *line, ARR_Instance array);
|
||||||
|
|
||||||
static void parse_allow_deny(char *line, ARR_Instance restrictions, int allow);
|
static void parse_allow_deny(char *line, ARR_Instance restrictions, int allow);
|
||||||
static void parse_authselectmode(char *);
|
static void parse_authselectmode(char *);
|
||||||
@@ -261,7 +262,10 @@ static char *user;
|
|||||||
/* Address refresh interval */
|
/* Address refresh interval */
|
||||||
static int refresh = 1209600; /* 2 weeks */
|
static int refresh = 1209600; /* 2 weeks */
|
||||||
|
|
||||||
|
#define DEFAULT_NTS_AEADS "30 15"
|
||||||
|
|
||||||
/* NTS server and client configuration */
|
/* NTS server and client configuration */
|
||||||
|
static ARR_Instance nts_aeads; /* array of int */
|
||||||
static char *nts_dump_dir = NULL;
|
static char *nts_dump_dir = NULL;
|
||||||
static char *nts_ntp_server = NULL;
|
static char *nts_ntp_server = NULL;
|
||||||
static ARR_Instance nts_server_cert_files; /* array of (char *) */
|
static ARR_Instance nts_server_cert_files; /* array of (char *) */
|
||||||
@@ -397,6 +401,8 @@ check_number_of_args(char *line, int num)
|
|||||||
void
|
void
|
||||||
CNF_Initialise(int r, int client_only)
|
CNF_Initialise(int r, int client_only)
|
||||||
{
|
{
|
||||||
|
char buf[10];
|
||||||
|
|
||||||
restarted = r;
|
restarted = r;
|
||||||
|
|
||||||
hwts_interfaces = ARR_CreateInstance(sizeof (CNF_HwTsInterface));
|
hwts_interfaces = ARR_CreateInstance(sizeof (CNF_HwTsInterface));
|
||||||
@@ -410,6 +416,9 @@ CNF_Initialise(int r, int client_only)
|
|||||||
ntp_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
|
ntp_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
|
||||||
cmd_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
|
cmd_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
|
||||||
|
|
||||||
|
nts_aeads = ARR_CreateInstance(sizeof (int));
|
||||||
|
snprintf(buf, sizeof (buf), DEFAULT_NTS_AEADS);
|
||||||
|
parse_ints(buf, nts_aeads);
|
||||||
nts_server_cert_files = ARR_CreateInstance(sizeof (char *));
|
nts_server_cert_files = ARR_CreateInstance(sizeof (char *));
|
||||||
nts_server_key_files = ARR_CreateInstance(sizeof (char *));
|
nts_server_key_files = ARR_CreateInstance(sizeof (char *));
|
||||||
nts_trusted_certs_paths = ARR_CreateInstance(sizeof (char *));
|
nts_trusted_certs_paths = ARR_CreateInstance(sizeof (char *));
|
||||||
@@ -469,6 +478,7 @@ CNF_Finalise(void)
|
|||||||
ARR_DestroyInstance(ntp_restrictions);
|
ARR_DestroyInstance(ntp_restrictions);
|
||||||
ARR_DestroyInstance(cmd_restrictions);
|
ARR_DestroyInstance(cmd_restrictions);
|
||||||
|
|
||||||
|
ARR_DestroyInstance(nts_aeads);
|
||||||
ARR_DestroyInstance(nts_server_cert_files);
|
ARR_DestroyInstance(nts_server_cert_files);
|
||||||
ARR_DestroyInstance(nts_server_key_files);
|
ARR_DestroyInstance(nts_server_key_files);
|
||||||
ARR_DestroyInstance(nts_trusted_certs_paths);
|
ARR_DestroyInstance(nts_trusted_certs_paths);
|
||||||
@@ -679,6 +689,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
|||||||
no_system_cert = parse_null(p);
|
no_system_cert = parse_null(p);
|
||||||
} else if (!strcasecmp(command, "ntpsigndsocket")) {
|
} else if (!strcasecmp(command, "ntpsigndsocket")) {
|
||||||
parse_string(p, &ntp_signd_socket);
|
parse_string(p, &ntp_signd_socket);
|
||||||
|
} else if (!strcasecmp(command, "ntsaeads")) {
|
||||||
|
parse_ints(p, nts_aeads);
|
||||||
} else if (!strcasecmp(command, "ntsratelimit")) {
|
} else if (!strcasecmp(command, "ntsratelimit")) {
|
||||||
parse_ratelimit(p, &nts_ratelimit_enabled, &nts_ratelimit_interval,
|
parse_ratelimit(p, &nts_ratelimit_enabled, &nts_ratelimit_interval,
|
||||||
&nts_ratelimit_burst, &nts_ratelimit_leak, NULL);
|
&nts_ratelimit_burst, &nts_ratelimit_leak, NULL);
|
||||||
@@ -806,6 +818,25 @@ parse_null(char *line)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
parse_ints(char *line, ARR_Instance array)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
int v;
|
||||||
|
|
||||||
|
ARR_SetSize(array, 0);
|
||||||
|
|
||||||
|
while (*line) {
|
||||||
|
s = line;
|
||||||
|
line = CPS_SplitWord(line);
|
||||||
|
parse_int(s, &v);
|
||||||
|
ARR_AppendElement(array, &v);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
parse_source(char *line, char *type, int fatal)
|
parse_source(char *line, char *type, int fatal)
|
||||||
{
|
{
|
||||||
@@ -2599,6 +2630,14 @@ CNF_GetRefresh(void)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
ARR_Instance
|
||||||
|
CNF_GetNtsAeads(void)
|
||||||
|
{
|
||||||
|
return nts_aeads;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
char *
|
char *
|
||||||
CNF_GetNtsDumpDir(void)
|
CNF_GetNtsDumpDir(void)
|
||||||
{
|
{
|
||||||
|
|||||||
2
conf.h
2
conf.h
@@ -29,6 +29,7 @@
|
|||||||
#define GOT_CONF_H
|
#define GOT_CONF_H
|
||||||
|
|
||||||
#include "addressing.h"
|
#include "addressing.h"
|
||||||
|
#include "array.h"
|
||||||
#include "reference.h"
|
#include "reference.h"
|
||||||
#include "sources.h"
|
#include "sources.h"
|
||||||
|
|
||||||
@@ -163,6 +164,7 @@ extern int CNF_GetPtpDomain(void);
|
|||||||
|
|
||||||
extern int CNF_GetRefresh(void);
|
extern int CNF_GetRefresh(void);
|
||||||
|
|
||||||
|
extern ARR_Instance CNF_GetNtsAeads(void);
|
||||||
extern char *CNF_GetNtsDumpDir(void);
|
extern char *CNF_GetNtsDumpDir(void);
|
||||||
extern char *CNF_GetNtsNtpServer(void);
|
extern char *CNF_GetNtsNtpServer(void);
|
||||||
extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys);
|
extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys);
|
||||||
|
|||||||
@@ -833,6 +833,34 @@ changes in the frequency and offset of the clock. The offsets in the
|
|||||||
<<chronyc.adoc#sourcestats,*sourcestats*>> reports (and the _tracking.log_ and
|
<<chronyc.adoc#sourcestats,*sourcestats*>> reports (and the _tracking.log_ and
|
||||||
_statistics.log_ files) may be smaller than the actual offsets.
|
_statistics.log_ files) may be smaller than the actual offsets.
|
||||||
|
|
||||||
|
[[ntsaeads1]]*ntsaeads* _ID_...::
|
||||||
|
This directive specifies a list of IDs of Authenticated Encryption with
|
||||||
|
Associated Data (AEAD) algorithms enabled for NTS authentication of NTP
|
||||||
|
messages. The algorithms are specified in decreasing order of priority.
|
||||||
|
Algorithms that are not supported by the installed version of the crypto
|
||||||
|
library (Nettle, GnuTLS) are ignored.
|
||||||
|
+
|
||||||
|
The following IDs are supported:
|
||||||
|
+
|
||||||
|
* 15: AES-SIV-CMAC-256
|
||||||
|
* 30: AES-128-GCM-SIV
|
||||||
|
{blank}::
|
||||||
|
+
|
||||||
|
The default list of IDs is _30 15_. AES-128-GCM-SIV is prefered over
|
||||||
|
AES-SIV-CMAC-256 for shorter keys, which makes NTS cookies shorter and improves
|
||||||
|
reliability of NTS in networks that block or limit rate of longer NTP messages.
|
||||||
|
+
|
||||||
|
The ID of the used algorithm is reported for each server by the
|
||||||
|
<<chronyc.adoc#authdata,*authdata*>> command.
|
||||||
|
+
|
||||||
|
An example of the directive is:
|
||||||
|
+
|
||||||
|
----
|
||||||
|
ntsaeads 15
|
||||||
|
----
|
||||||
|
+
|
||||||
|
This list is used also by the <<ntsaeads2,NTS server>>.
|
||||||
|
|
||||||
[[ntsdumpdir1]]*ntsdumpdir* _directory_::
|
[[ntsdumpdir1]]*ntsdumpdir* _directory_::
|
||||||
This directive specifies a directory for the client to save NTS cookies it
|
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
|
received from the server in order to avoid making an NTS-KE request when
|
||||||
@@ -1779,6 +1807,43 @@ per process that the NTS server will accept. The default value is 100. The
|
|||||||
maximum practical value is half of the system *FD_SETSIZE* constant (usually
|
maximum practical value is half of the system *FD_SETSIZE* constant (usually
|
||||||
1024).
|
1024).
|
||||||
|
|
||||||
|
[[ntsaeads2]]*ntsaeads* _ID_...::
|
||||||
|
This directive specifies a list of IDs of Authenticated Encryption with
|
||||||
|
Associated Data (AEAD) algorithms enabled for NTS authentication of NTP
|
||||||
|
messages. *chronyd* as a server uses the first enabled algorithm from the list
|
||||||
|
provided by the client. Algorithms that are not supported by the installed
|
||||||
|
version of the crypto library (Nettle, GnuTLS) are ignored.
|
||||||
|
+
|
||||||
|
The following IDs are supported:
|
||||||
|
+
|
||||||
|
* 15: AES-SIV-CMAC-256
|
||||||
|
* 30: AES-128-GCM-SIV
|
||||||
|
{blank}::
|
||||||
|
+
|
||||||
|
The default list of IDs is _30 15_. AES-128-GCM-SIV is prefered over
|
||||||
|
AES-SIV-CMAC-256 for shorter keys, which makes NTS cookies shorter and improves
|
||||||
|
reliability of NTS in networks that block or limit rate of longer NTP messages.
|
||||||
|
+
|
||||||
|
An example of the directive is:
|
||||||
|
+
|
||||||
|
----
|
||||||
|
ntsaeads 15
|
||||||
|
----
|
||||||
|
+
|
||||||
|
This list is used also by the <<ntsaeads1,NTS client>>.
|
||||||
|
+
|
||||||
|
Note the the NTS specification (RFC 8915) requires servers to support
|
||||||
|
AES-SIV-CMAC-256, i.e. 15 should be always included in the specified list.
|
||||||
|
+
|
||||||
|
The AES-128-GCM-SIV keys used by *chronyd* do not comply to RFC 8915 for
|
||||||
|
compatibility with older *chrony* clients unless the use of compliant keys is
|
||||||
|
negotiated with an
|
||||||
|
https://chrony-project.org/doc/spec/nts-compliant-128gcm.html[NTS-KE record].
|
||||||
|
Support for this record was added in version 4.6.1. As a client, *chronyd* can
|
||||||
|
interoperate with a server that uses compliant keys, but does not support the
|
||||||
|
negotiation, if it responds to incorrectly authenticated requests with an NTS
|
||||||
|
NAK.
|
||||||
|
|
||||||
[[ntsdumpdir2]]*ntsdumpdir* _directory_::
|
[[ntsdumpdir2]]*ntsdumpdir* _directory_::
|
||||||
This directive specifies a directory where *chronyd* operating as an NTS server
|
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
|
can save the keys which encrypt NTS cookies provided to clients. The keys are
|
||||||
@@ -2756,7 +2821,11 @@ 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
|
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
|
be avoided unless no other type is supported on the server and client, or
|
||||||
peers.
|
peers. A major weakness of MD5 for the NTP MAC is a length extension attack,
|
||||||
|
where a man-in-the-middle attacker can add arbitrary extension fields to the
|
||||||
|
NTP message and update the MAC to pass the verification of the extended
|
||||||
|
message. The *extfield* option (enabling processing of the specified extension
|
||||||
|
field) should not be used for NTP sources authenticated with an MD5 key.
|
||||||
+
|
+
|
||||||
The <<chronyc.adoc#keygen,*keygen*>> command of *chronyc* can be used to
|
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
|
generate random keys for the key file. By default, it generates 160-bit MD5 or
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
//
|
//
|
||||||
// Copyright (C) Richard P. Curnow 1997-2003
|
// Copyright (C) Richard P. Curnow 1997-2003
|
||||||
// Copyright (C) Stephen Wadeley 2016
|
// Copyright (C) Stephen Wadeley 2016
|
||||||
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2023
|
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2024
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or modify
|
// 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
|
// it under the terms of version 2 of the GNU General Public License as
|
||||||
|
|||||||
3
nts_ke.h
3
nts_ke.h
@@ -40,6 +40,7 @@
|
|||||||
#define NKE_RECORD_COOKIE 5
|
#define NKE_RECORD_COOKIE 5
|
||||||
#define NKE_RECORD_NTPV4_SERVER_NEGOTIATION 6
|
#define NKE_RECORD_NTPV4_SERVER_NEGOTIATION 6
|
||||||
#define NKE_RECORD_NTPV4_PORT_NEGOTIATION 7
|
#define NKE_RECORD_NTPV4_PORT_NEGOTIATION 7
|
||||||
|
#define NKE_RECORD_COMPLIANT_128GCM_EXPORT 1024
|
||||||
|
|
||||||
#define NKE_NEXT_PROTOCOL_NTPV4 0
|
#define NKE_NEXT_PROTOCOL_NTPV4 0
|
||||||
|
|
||||||
@@ -49,8 +50,6 @@
|
|||||||
|
|
||||||
#define NKE_ALPN_NAME "ntske/1"
|
#define NKE_ALPN_NAME "ntske/1"
|
||||||
#define NKE_EXPORTER_LABEL "EXPORTER-network-time-security"
|
#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_MESSAGE_LENGTH 16384
|
||||||
#define NKE_MAX_RECORD_BODY_LENGTH 256
|
#define NKE_MAX_RECORD_BODY_LENGTH 256
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Miroslav Lichvar 2020-2021
|
* Copyright (C) Miroslav Lichvar 2020-2021, 2024
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -50,7 +50,9 @@ struct NKC_Instance_Record {
|
|||||||
int got_response;
|
int got_response;
|
||||||
int resolving_name;
|
int resolving_name;
|
||||||
|
|
||||||
|
int compliant_128gcm;
|
||||||
NKE_Context context;
|
NKE_Context context;
|
||||||
|
NKE_Context alt_context;
|
||||||
NKE_Cookie cookies[NKE_MAX_COOKIES];
|
NKE_Cookie cookies[NKE_MAX_COOKIES];
|
||||||
int num_cookies;
|
int num_cookies;
|
||||||
char server_name[NKE_MAX_RECORD_BODY_LENGTH + 2];
|
char server_name[NKE_MAX_RECORD_BODY_LENGTH + 2];
|
||||||
@@ -98,12 +100,14 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *arg
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
#define MAX_AEAD_ALGORITHMS 4
|
||||||
|
|
||||||
static int
|
static int
|
||||||
prepare_request(NKC_Instance inst)
|
prepare_request(NKC_Instance inst)
|
||||||
{
|
{
|
||||||
NKSN_Instance session = inst->session;
|
NKSN_Instance session = inst->session;
|
||||||
uint16_t data[2];
|
uint16_t data[MAX_AEAD_ALGORITHMS];
|
||||||
int length;
|
int i, aead_algorithm, length;
|
||||||
|
|
||||||
NKSN_BeginMessage(session);
|
NKSN_BeginMessage(session);
|
||||||
|
|
||||||
@@ -111,15 +115,24 @@ prepare_request(NKC_Instance inst)
|
|||||||
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, sizeof (data[0])))
|
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, sizeof (data[0])))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
length = 0;
|
for (i = length = 0; i < ARR_GetSize(CNF_GetNtsAeads()) && length < MAX_AEAD_ALGORITHMS;
|
||||||
if (SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0)
|
i++) {
|
||||||
data[length++] = htons(AEAD_AES_128_GCM_SIV);
|
aead_algorithm = *(int *)ARR_GetElement(CNF_GetNtsAeads(), i);
|
||||||
if (SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) > 0)
|
if (SIV_GetKeyLength(aead_algorithm) > 0)
|
||||||
data[length++] = htons(AEAD_AES_SIV_CMAC_256);
|
data[length++] = htons(aead_algorithm);
|
||||||
|
}
|
||||||
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data,
|
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data,
|
||||||
length * sizeof (data[0])))
|
length * sizeof (data[0])))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < length; i++) {
|
||||||
|
if (data[i] == htons(AEAD_AES_128_GCM_SIV)) {
|
||||||
|
if (!NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0))
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!NKSN_EndMessage(session))
|
if (!NKSN_EndMessage(session))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -139,6 +152,8 @@ process_response(NKC_Instance inst)
|
|||||||
assert(sizeof (data) % sizeof (uint16_t) == 0);
|
assert(sizeof (data) % sizeof (uint16_t) == 0);
|
||||||
assert(sizeof (uint16_t) == 2);
|
assert(sizeof (uint16_t) == 2);
|
||||||
|
|
||||||
|
inst->compliant_128gcm = 0;
|
||||||
|
inst->alt_context.algorithm = AEAD_SIV_INVALID;
|
||||||
inst->num_cookies = 0;
|
inst->num_cookies = 0;
|
||||||
inst->ntp_address.ip_addr.family = IPADDR_UNSPEC;
|
inst->ntp_address.ip_addr.family = IPADDR_UNSPEC;
|
||||||
inst->ntp_address.port = 0;
|
inst->ntp_address.port = 0;
|
||||||
@@ -165,15 +180,31 @@ process_response(NKC_Instance inst)
|
|||||||
next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
|
next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
|
||||||
break;
|
break;
|
||||||
case NKE_RECORD_AEAD_ALGORITHM:
|
case NKE_RECORD_AEAD_ALGORITHM:
|
||||||
if (length != 2 || (ntohs(data[0]) != AEAD_AES_SIV_CMAC_256 &&
|
if (length != 2) {
|
||||||
ntohs(data[0]) != AEAD_AES_128_GCM_SIV) ||
|
DEBUG_LOG("Unexpected AEAD algorithm");
|
||||||
SIV_GetKeyLength(ntohs(data[0])) <= 0) {
|
|
||||||
DEBUG_LOG("Unexpected NTS-KE AEAD algorithm");
|
|
||||||
error = 1;
|
error = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
aead_algorithm = ntohs(data[0]);
|
for (i = 0; i < ARR_GetSize(CNF_GetNtsAeads()); i++) {
|
||||||
inst->context.algorithm = aead_algorithm;
|
if (ntohs(data[0]) == *(int *)ARR_GetElement(CNF_GetNtsAeads(), i) &&
|
||||||
|
SIV_GetKeyLength(ntohs(data[0])) > 0) {
|
||||||
|
aead_algorithm = ntohs(data[0]);
|
||||||
|
inst->context.algorithm = aead_algorithm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (aead_algorithm < 0) {
|
||||||
|
DEBUG_LOG("Unexpected AEAD algorithm");
|
||||||
|
error = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NKE_RECORD_COMPLIANT_128GCM_EXPORT:
|
||||||
|
if (length != 0) {
|
||||||
|
DEBUG_LOG("Non-empty compliant-128gcm record");
|
||||||
|
error = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DEBUG_LOG("Compliant AES-128-GCM-SIV export");
|
||||||
|
inst->compliant_128gcm = 1;
|
||||||
break;
|
break;
|
||||||
case NKE_RECORD_ERROR:
|
case NKE_RECORD_ERROR:
|
||||||
if (length == 2)
|
if (length == 2)
|
||||||
@@ -255,6 +286,7 @@ process_response(NKC_Instance inst)
|
|||||||
static int
|
static int
|
||||||
handle_message(void *arg)
|
handle_message(void *arg)
|
||||||
{
|
{
|
||||||
|
SIV_Algorithm exporter_algorithm;
|
||||||
NKC_Instance inst = arg;
|
NKC_Instance inst = arg;
|
||||||
|
|
||||||
if (!process_response(inst)) {
|
if (!process_response(inst)) {
|
||||||
@@ -262,8 +294,25 @@ handle_message(void *arg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!NKSN_GetKeys(inst->session, inst->context.algorithm,
|
exporter_algorithm = inst->context.algorithm;
|
||||||
&inst->context.c2s, &inst->context.s2c))
|
|
||||||
|
/* With AES-128-GCM-SIV, set the algorithm ID in the RFC5705 key exporter
|
||||||
|
context incorrectly for compatibility with older chrony servers unless
|
||||||
|
the server confirmed support for the compliant context. Generate both
|
||||||
|
sets of keys in case the server uses the compliant context, but does not
|
||||||
|
support the negotiation record, assuming it will respond with an NTS NAK
|
||||||
|
to a request authenticated with the noncompliant key. */
|
||||||
|
if (exporter_algorithm == AEAD_AES_128_GCM_SIV && !inst->compliant_128gcm) {
|
||||||
|
inst->alt_context.algorithm = inst->context.algorithm;
|
||||||
|
if (!NKSN_GetKeys(inst->session, inst->alt_context.algorithm, exporter_algorithm,
|
||||||
|
NKE_NEXT_PROTOCOL_NTPV4, &inst->alt_context.c2s, &inst->alt_context.s2c))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
exporter_algorithm = AEAD_AES_SIV_CMAC_256;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NKSN_GetKeys(inst->session, inst->context.algorithm, exporter_algorithm,
|
||||||
|
NKE_NEXT_PROTOCOL_NTPV4, &inst->context.c2s, &inst->context.s2c))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (inst->server_name[0] != '\0') {
|
if (inst->server_name[0] != '\0') {
|
||||||
@@ -428,7 +477,7 @@ NKC_IsActive(NKC_Instance inst)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
int
|
||||||
NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
|
NKC_GetNtsData(NKC_Instance inst, NKE_Context *context, NKE_Context *alt_context,
|
||||||
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
|
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
|
||||||
IPSockAddr *ntp_address)
|
IPSockAddr *ntp_address)
|
||||||
{
|
{
|
||||||
@@ -438,6 +487,7 @@ NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
*context = inst->context;
|
*context = inst->context;
|
||||||
|
*alt_context = inst->alt_context;
|
||||||
|
|
||||||
for (i = 0; i < inst->num_cookies && i < max_cookies; i++)
|
for (i = 0; i < inst->num_cookies && i < max_cookies; i++)
|
||||||
cookies[i] = inst->cookies[i];
|
cookies[i] = inst->cookies[i];
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ extern int NKC_Start(NKC_Instance inst);
|
|||||||
extern int NKC_IsActive(NKC_Instance inst);
|
extern int NKC_IsActive(NKC_Instance inst);
|
||||||
|
|
||||||
/* Get the NTS data if the session was successful */
|
/* Get the NTS data if the session was successful */
|
||||||
extern int NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
|
extern int NKC_GetNtsData(NKC_Instance inst, NKE_Context *context, NKE_Context *alt_context,
|
||||||
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
|
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
|
||||||
IPSockAddr *ntp_address);
|
IPSockAddr *ntp_address);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Miroslav Lichvar 2020, 2022
|
* Copyright (C) Miroslav Lichvar 2020, 2022, 2024
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -337,8 +337,10 @@ helper_signal(int x)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_algorithm)
|
prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_algorithm,
|
||||||
|
int compliant_128gcm)
|
||||||
{
|
{
|
||||||
|
SIV_Algorithm exporter_algorithm;
|
||||||
NKE_Context context;
|
NKE_Context context;
|
||||||
NKE_Cookie cookie;
|
NKE_Cookie cookie;
|
||||||
char *ntp_server;
|
char *ntp_server;
|
||||||
@@ -371,6 +373,11 @@ prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_a
|
|||||||
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
|
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (aead_algorithm == AEAD_AES_128_GCM_SIV && compliant_128gcm) {
|
||||||
|
if (!NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (CNF_GetNTPPort() != NTP_PORT) {
|
if (CNF_GetNTPPort() != NTP_PORT) {
|
||||||
datum = htons(CNF_GetNTPPort());
|
datum = htons(CNF_GetNTPPort());
|
||||||
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_PORT_NEGOTIATION, &datum, sizeof (datum)))
|
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_PORT_NEGOTIATION, &datum, sizeof (datum)))
|
||||||
@@ -385,8 +392,16 @@ prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_a
|
|||||||
}
|
}
|
||||||
|
|
||||||
context.algorithm = aead_algorithm;
|
context.algorithm = aead_algorithm;
|
||||||
|
exporter_algorithm = aead_algorithm;
|
||||||
|
|
||||||
if (!NKSN_GetKeys(session, aead_algorithm, &context.c2s, &context.s2c))
|
/* With AES-128-GCM-SIV, set the algorithm ID in the RFC5705 key exporter
|
||||||
|
context incorrectly for compatibility with older chrony clients unless
|
||||||
|
the client requested the compliant context */
|
||||||
|
if (exporter_algorithm == AEAD_AES_128_GCM_SIV && !compliant_128gcm)
|
||||||
|
exporter_algorithm = AEAD_AES_SIV_CMAC_256;
|
||||||
|
|
||||||
|
if (!NKSN_GetKeys(session, aead_algorithm, exporter_algorithm,
|
||||||
|
NKE_NEXT_PROTOCOL_NTPV4, &context.c2s, &context.s2c))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
for (i = 0; i < NKE_MAX_COOKIES; i++) {
|
for (i = 0; i < NKE_MAX_COOKIES; i++) {
|
||||||
@@ -411,7 +426,8 @@ process_request(NKSN_Instance session)
|
|||||||
int next_protocol_records = 0, aead_algorithm_records = 0;
|
int next_protocol_records = 0, aead_algorithm_records = 0;
|
||||||
int next_protocol_values = 0, aead_algorithm_values = 0;
|
int next_protocol_values = 0, aead_algorithm_values = 0;
|
||||||
int next_protocol = -1, aead_algorithm = -1, error = -1;
|
int next_protocol = -1, aead_algorithm = -1, error = -1;
|
||||||
int i, critical, type, length;
|
int i, j, critical, type, length;
|
||||||
|
int compliant_128gcm = 0;
|
||||||
uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)];
|
uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)];
|
||||||
|
|
||||||
assert(NKE_MAX_RECORD_BODY_LENGTH % sizeof (uint16_t) == 0);
|
assert(NKE_MAX_RECORD_BODY_LENGTH % sizeof (uint16_t) == 0);
|
||||||
@@ -446,11 +462,21 @@ process_request(NKSN_Instance session)
|
|||||||
|
|
||||||
for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
|
for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
|
||||||
aead_algorithm_values++;
|
aead_algorithm_values++;
|
||||||
/* Use the first supported algorithm */
|
/* Use the first enabled and supported algorithm */
|
||||||
if (aead_algorithm < 0 && SIV_GetKeyLength(ntohs(data[i])) > 0)
|
for (j = 0; j < ARR_GetSize(CNF_GetNtsAeads()); j++) {
|
||||||
aead_algorithm = ntohs(data[i]);
|
if (ntohs(data[i]) == *(int *)ARR_GetElement(CNF_GetNtsAeads(), j) &&
|
||||||
|
aead_algorithm < 0 && SIV_GetKeyLength(ntohs(data[i])) > 0)
|
||||||
|
aead_algorithm = ntohs(data[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case NKE_RECORD_COMPLIANT_128GCM_EXPORT:
|
||||||
|
if (length != 0) {
|
||||||
|
error = NKE_ERROR_BAD_REQUEST;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
compliant_128gcm = 1;
|
||||||
|
break;
|
||||||
case NKE_RECORD_ERROR:
|
case NKE_RECORD_ERROR:
|
||||||
case NKE_RECORD_WARNING:
|
case NKE_RECORD_WARNING:
|
||||||
case NKE_RECORD_COOKIE:
|
case NKE_RECORD_COOKIE:
|
||||||
@@ -469,7 +495,7 @@ process_request(NKSN_Instance session)
|
|||||||
error = NKE_ERROR_BAD_REQUEST;
|
error = NKE_ERROR_BAD_REQUEST;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!prepare_response(session, error, next_protocol, aead_algorithm))
|
if (!prepare_response(session, error, next_protocol, aead_algorithm, compliant_128gcm))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@@ -877,22 +877,42 @@ NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
int
|
||||||
NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c)
|
NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm algorithm, SIV_Algorithm exporter_algorithm,
|
||||||
|
int next_protocol, NKE_Key *c2s, NKE_Key *s2c)
|
||||||
{
|
{
|
||||||
int length = SIV_GetKeyLength(siv);
|
int length = SIV_GetKeyLength(algorithm);
|
||||||
|
struct {
|
||||||
|
uint16_t next_protocol;
|
||||||
|
uint16_t algorithm;
|
||||||
|
uint8_t is_s2c;
|
||||||
|
uint8_t _pad;
|
||||||
|
} context;
|
||||||
|
|
||||||
|
if (!inst->tls_session)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (length <= 0 || length > sizeof (c2s->key) || length > sizeof (s2c->key)) {
|
if (length <= 0 || length > sizeof (c2s->key) || length > sizeof (s2c->key)) {
|
||||||
DEBUG_LOG("Invalid algorithm");
|
DEBUG_LOG("Invalid algorithm");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(sizeof (context) == 6);
|
||||||
|
context.next_protocol = htons(next_protocol);
|
||||||
|
context.algorithm = htons(exporter_algorithm);
|
||||||
|
|
||||||
|
context.is_s2c = 0;
|
||||||
if (gnutls_prf_rfc5705(inst->tls_session,
|
if (gnutls_prf_rfc5705(inst->tls_session,
|
||||||
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
|
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
|
||||||
sizeof (NKE_EXPORTER_CONTEXT_C2S) - 1, NKE_EXPORTER_CONTEXT_C2S,
|
sizeof (context) - 1, (char *)&context,
|
||||||
length, (char *)c2s->key) < 0 ||
|
length, (char *)c2s->key) < 0) {
|
||||||
gnutls_prf_rfc5705(inst->tls_session,
|
DEBUG_LOG("Could not export key");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.is_s2c = 1;
|
||||||
|
if (gnutls_prf_rfc5705(inst->tls_session,
|
||||||
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
|
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
|
||||||
sizeof (NKE_EXPORTER_CONTEXT_S2C) - 1, NKE_EXPORTER_CONTEXT_S2C,
|
sizeof (context) - 1, (char *)&context,
|
||||||
length, (char *)s2c->key) < 0) {
|
length, (char *)s2c->key) < 0) {
|
||||||
DEBUG_LOG("Could not export key");
|
DEBUG_LOG("Could not export key");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -77,8 +77,11 @@ extern int NKSN_EndMessage(NKSN_Instance inst);
|
|||||||
extern int NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
|
extern int NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
|
||||||
void *body, int buffer_length);
|
void *body, int buffer_length);
|
||||||
|
|
||||||
/* Export NTS keys for a specified algorithm */
|
/* Export NTS keys for a specified algorithm (for compatibility reasons the
|
||||||
extern int NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c);
|
RFC5705 exporter context is allowed to have a different algorithm) */
|
||||||
|
extern int NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm algorithm,
|
||||||
|
SIV_Algorithm exporter_algorithm,
|
||||||
|
int next_protocol, NKE_Key *c2s, NKE_Key *s2c);
|
||||||
|
|
||||||
/* Check if the session has stopped */
|
/* Check if the session has stopped */
|
||||||
extern int NKSN_IsStopped(NKSN_Instance inst);
|
extern int NKSN_IsStopped(NKSN_Instance inst);
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ struct NNC_Instance_Record {
|
|||||||
double last_nke_success;
|
double last_nke_success;
|
||||||
|
|
||||||
NKE_Context context;
|
NKE_Context context;
|
||||||
|
NKE_Context alt_context;
|
||||||
unsigned int context_id;
|
unsigned int context_id;
|
||||||
NKE_Cookie cookies[NTS_MAX_COOKIES];
|
NKE_Cookie cookies[NTS_MAX_COOKIES];
|
||||||
int num_cookies;
|
int num_cookies;
|
||||||
@@ -105,6 +106,7 @@ reset_instance(NNC_Instance inst)
|
|||||||
inst->last_nke_success = 0.0;
|
inst->last_nke_success = 0.0;
|
||||||
|
|
||||||
memset(&inst->context, 0, sizeof (inst->context));
|
memset(&inst->context, 0, sizeof (inst->context));
|
||||||
|
memset(&inst->alt_context, 0, sizeof (inst->alt_context));
|
||||||
inst->context_id = 0;
|
inst->context_id = 0;
|
||||||
memset(inst->cookies, 0, sizeof (inst->cookies));
|
memset(inst->cookies, 0, sizeof (inst->cookies));
|
||||||
inst->num_cookies = 0;
|
inst->num_cookies = 0;
|
||||||
@@ -165,6 +167,21 @@ check_cookies(NNC_Instance inst)
|
|||||||
if (inst->num_cookies > 0 &&
|
if (inst->num_cookies > 0 &&
|
||||||
((inst->nak_response && !inst->ok_response) ||
|
((inst->nak_response && !inst->ok_response) ||
|
||||||
SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())) {
|
SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())) {
|
||||||
|
|
||||||
|
/* Before dropping the cookies, check whether there is an alternate set of
|
||||||
|
keys available (exported with the compliant context for AES-128-GCM-SIV)
|
||||||
|
and the NAK was the only valid response after the last NTS-KE session,
|
||||||
|
indicating we use incorrect keys and switching to the other set of keys
|
||||||
|
for the following NTP requests might work */
|
||||||
|
if (inst->alt_context.algorithm != AEAD_SIV_INVALID &&
|
||||||
|
inst->alt_context.algorithm == inst->context.algorithm &&
|
||||||
|
inst->nke_attempts > 0 && inst->nak_response && !inst->ok_response) {
|
||||||
|
inst->context = inst->alt_context;
|
||||||
|
inst->alt_context.algorithm = AEAD_SIV_INVALID;
|
||||||
|
DEBUG_LOG("Switched to compliant keys");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
inst->num_cookies = 0;
|
inst->num_cookies = 0;
|
||||||
DEBUG_LOG("Dropped cookies");
|
DEBUG_LOG("Dropped cookies");
|
||||||
}
|
}
|
||||||
@@ -261,7 +278,7 @@ get_cookies(NNC_Instance inst)
|
|||||||
assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES);
|
assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES);
|
||||||
|
|
||||||
/* Get the new keys, cookies and NTP address if the session was successful */
|
/* Get the new keys, cookies and NTP address if the session was successful */
|
||||||
got_data = NKC_GetNtsData(inst->nke, &inst->context,
|
got_data = NKC_GetNtsData(inst->nke, &inst->context, &inst->alt_context,
|
||||||
inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES,
|
inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES,
|
||||||
&ntp_address);
|
&ntp_address);
|
||||||
|
|
||||||
@@ -520,6 +537,7 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
|
|||||||
new NTS-KE session to be started as soon as the cookies run out. */
|
new NTS-KE session to be started as soon as the cookies run out. */
|
||||||
inst->nke_attempts = 0;
|
inst->nke_attempts = 0;
|
||||||
inst->next_nke_attempt = 0.0;
|
inst->next_nke_attempt = 0.0;
|
||||||
|
inst->alt_context.algorithm = AEAD_SIV_INVALID;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -643,6 +661,7 @@ load_cookies(NNC_Instance inst)
|
|||||||
sscanf(words[0], "%u", &context_id) != 1 || sscanf(words[1], "%d", &algorithm) != 1)
|
sscanf(words[0], "%u", &context_id) != 1 || sscanf(words[1], "%d", &algorithm) != 1)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
inst->alt_context.algorithm = AEAD_SIV_INVALID;
|
||||||
inst->context.algorithm = algorithm;
|
inst->context.algorithm = algorithm;
|
||||||
inst->context.s2c.length = UTI_HexToBytes(words[2], inst->context.s2c.key,
|
inst->context.s2c.length = UTI_HexToBytes(words[2], inst->context.s2c.key,
|
||||||
sizeof (inst->context.s2c.key));
|
sizeof (inst->context.s2c.key));
|
||||||
@@ -687,6 +706,7 @@ error:
|
|||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
memset(&inst->context, 0, sizeof (inst->context));
|
memset(&inst->context, 0, sizeof (inst->context));
|
||||||
|
memset(&inst->alt_context, 0, sizeof (inst->alt_context));
|
||||||
inst->num_cookies = 0;
|
inst->num_cookies = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
siv.h
1
siv.h
@@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
/* Identifiers of SIV algorithms following the IANA AEAD registry */
|
/* Identifiers of SIV algorithms following the IANA AEAD registry */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
AEAD_SIV_INVALID = 0,
|
||||||
AEAD_AES_SIV_CMAC_256 = 15,
|
AEAD_AES_SIV_CMAC_256 = 15,
|
||||||
AEAD_AES_SIV_CMAC_384 = 16,
|
AEAD_AES_SIV_CMAC_384 = 16,
|
||||||
AEAD_AES_SIV_CMAC_512 = 17,
|
AEAD_AES_SIV_CMAC_512 = 17,
|
||||||
|
|||||||
@@ -313,4 +313,31 @@ check_sync && test_fail
|
|||||||
check_file_messages " 3 1 .* 123 " 0 0 log.packets || test_fail
|
check_file_messages " 3 1 .* 123 " 0 0 log.packets || test_fail
|
||||||
check_file_messages " 3 2 .* 123 " 0 0 log.packets || test_fail
|
check_file_messages " 3 2 .* 123 " 0 0 log.packets || test_fail
|
||||||
|
|
||||||
|
for server_aead in "" "15" "30"; do
|
||||||
|
for client_aead in "" "15" "30"; do
|
||||||
|
server_conf="
|
||||||
|
ntsaeads $server_aead
|
||||||
|
ntsserverkey tmp/server1.key
|
||||||
|
ntsservercert tmp/server1.crt
|
||||||
|
ntsprocesses 0"
|
||||||
|
client_conf="
|
||||||
|
nosystemcert
|
||||||
|
ntsaeads $client_aead
|
||||||
|
ntstrustedcerts tmp/server1.crt
|
||||||
|
ntstrustedcerts tmp/server2.crt"
|
||||||
|
client_server_conf=""
|
||||||
|
|
||||||
|
run_test || test_fail
|
||||||
|
check_chronyd_exit || test_fail
|
||||||
|
if [ -n "$server_aead" ] && [ "$server_aead" == "$client_aead" ] &&
|
||||||
|
( [ "$server_aead" != "30" ] || check_config_h '.*_SIV_GCM 1' ); then
|
||||||
|
check_source_selection || test_fail
|
||||||
|
check_sync || test_fail
|
||||||
|
else
|
||||||
|
check_source_selection && test_fail
|
||||||
|
check_sync && test_fail
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
test_pass
|
test_pass
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Miroslav Lichvar 2020
|
* Copyright (C) Miroslav Lichvar 2020, 2024
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -23,8 +23,24 @@
|
|||||||
|
|
||||||
#ifdef FEAT_NTS
|
#ifdef FEAT_NTS
|
||||||
|
|
||||||
#include <nts_ke_client.c>
|
|
||||||
#include <local.h>
|
#include <local.h>
|
||||||
|
#include <nts_ke_session.h>
|
||||||
|
#include <util.h>
|
||||||
|
|
||||||
|
#define NKSN_GetKeys get_keys
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_keys(NKSN_Instance session, SIV_Algorithm algorithm, SIV_Algorithm exporter_algorithm,
|
||||||
|
int next_protocol, NKE_Key *c2s, NKE_Key *s2c)
|
||||||
|
{
|
||||||
|
c2s->length = SIV_GetKeyLength(algorithm);
|
||||||
|
UTI_GetRandomBytes(c2s->key, c2s->length);
|
||||||
|
s2c->length = SIV_GetKeyLength(algorithm);
|
||||||
|
UTI_GetRandomBytes(s2c->key, s2c->length);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <nts_ke_client.c>
|
||||||
|
|
||||||
static void
|
static void
|
||||||
prepare_response(NKSN_Instance session, int valid)
|
prepare_response(NKSN_Instance session, int valid)
|
||||||
@@ -88,12 +104,17 @@ prepare_response(NKSN_Instance session, int valid)
|
|||||||
|
|
||||||
if (random() % 2) {
|
if (random() % 2) {
|
||||||
length = random() % (sizeof (data) + 1);
|
length = random() % (sizeof (data) + 1);
|
||||||
TEST_CHECK(NKSN_AddRecord(session, 0, 1000 + random() % 1000, data, length));
|
TEST_CHECK(NKSN_AddRecord(session, 0, 2000 + random() % 1000, data, length));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (random() % 2)
|
||||||
|
TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0));
|
||||||
|
|
||||||
if (index != 8) {
|
if (index != 8) {
|
||||||
for (i = 0; i < NKE_MAX_COOKIES; i++) {
|
for (i = random() % NKE_MAX_COOKIES; i >= 0; i--) {
|
||||||
length = (random() % sizeof (data) + 1) / 4 * 4;
|
length = (random() % sizeof (data) + 1) / 4 * 4;
|
||||||
|
if (i == 0 && length == 0)
|
||||||
|
length = 4;
|
||||||
if (index == 9)
|
if (index == 9)
|
||||||
length += (length < sizeof (data) ? 1 : -1) * (random() % 3 + 1);
|
length += (length < sizeof (data) ? 1 : -1) * (random() % 3 + 1);
|
||||||
TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_COOKIE, data, length));
|
TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_COOKIE, data, length));
|
||||||
@@ -103,12 +124,16 @@ prepare_response(NKSN_Instance session, int valid)
|
|||||||
TEST_CHECK(NKSN_EndMessage(session));
|
TEST_CHECK(NKSN_EndMessage(session));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define MAX_COOKIES (2 * NKE_MAX_COOKIES)
|
||||||
|
|
||||||
void
|
void
|
||||||
test_unit(void)
|
test_unit(void)
|
||||||
{
|
{
|
||||||
|
NKE_Context context, alt_context;
|
||||||
|
NKE_Cookie cookies[MAX_COOKIES];
|
||||||
|
int i, j, r, valid, num_cookies;
|
||||||
NKC_Instance inst;
|
NKC_Instance inst;
|
||||||
IPSockAddr addr;
|
IPSockAddr addr;
|
||||||
int i, r, valid;
|
|
||||||
|
|
||||||
char conf[][100] = {
|
char conf[][100] = {
|
||||||
"nosystemcert",
|
"nosystemcert",
|
||||||
@@ -127,10 +152,33 @@ test_unit(void)
|
|||||||
TEST_CHECK(inst);
|
TEST_CHECK(inst);
|
||||||
|
|
||||||
for (i = 0; i < 10000; i++) {
|
for (i = 0; i < 10000; i++) {
|
||||||
|
inst->got_response = 0;
|
||||||
valid = random() % 2;
|
valid = random() % 2;
|
||||||
prepare_response(inst->session, valid);
|
prepare_response(inst->session, valid);
|
||||||
r = process_response(inst);
|
r = handle_message(inst);
|
||||||
TEST_CHECK(r == valid);
|
TEST_CHECK(r == valid);
|
||||||
|
|
||||||
|
memset(&context, 0, sizeof (context));
|
||||||
|
memset(&alt_context, 0, sizeof (alt_context));
|
||||||
|
num_cookies = 0;
|
||||||
|
|
||||||
|
r = NKC_GetNtsData(inst, &context, &alt_context,
|
||||||
|
cookies, &num_cookies, random() % MAX_COOKIES + 1, &addr);
|
||||||
|
TEST_CHECK(r == valid);
|
||||||
|
if (r) {
|
||||||
|
TEST_CHECK(context.algorithm != AEAD_SIV_INVALID);
|
||||||
|
TEST_CHECK(context.c2s.length > 0);
|
||||||
|
TEST_CHECK(context.c2s.length == SIV_GetKeyLength(context.algorithm));
|
||||||
|
TEST_CHECK(context.s2c.length == SIV_GetKeyLength(context.algorithm));
|
||||||
|
if (alt_context.algorithm != AEAD_SIV_INVALID) {
|
||||||
|
TEST_CHECK(context.c2s.length > 0);
|
||||||
|
TEST_CHECK(alt_context.c2s.length == SIV_GetKeyLength(alt_context.algorithm));
|
||||||
|
TEST_CHECK(alt_context.s2c.length == SIV_GetKeyLength(alt_context.algorithm));
|
||||||
|
}
|
||||||
|
TEST_CHECK(num_cookies > 0 && num_cookies <= NKE_MAX_COOKIES);
|
||||||
|
for (j = 0; j < num_cookies; j++)
|
||||||
|
TEST_CHECK(cookies[j].length > 0 && cookies[j].length % 4 == 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NKC_DestroyInstance(inst);
|
NKC_DestroyInstance(inst);
|
||||||
|
|||||||
@@ -30,11 +30,12 @@
|
|||||||
#define NKSN_GetKeys get_keys
|
#define NKSN_GetKeys get_keys
|
||||||
|
|
||||||
static int
|
static int
|
||||||
get_keys(NKSN_Instance session, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c)
|
get_keys(NKSN_Instance session, SIV_Algorithm algorithm, SIV_Algorithm exporter_algorithm,
|
||||||
|
int next_protocol, NKE_Key *c2s, NKE_Key *s2c)
|
||||||
{
|
{
|
||||||
c2s->length = SIV_GetKeyLength(siv);
|
c2s->length = SIV_GetKeyLength(algorithm);
|
||||||
UTI_GetRandomBytes(c2s->key, c2s->length);
|
UTI_GetRandomBytes(c2s->key, c2s->length);
|
||||||
s2c->length = SIV_GetKeyLength(siv);
|
s2c->length = SIV_GetKeyLength(algorithm);
|
||||||
UTI_GetRandomBytes(s2c->key, s2c->length);
|
UTI_GetRandomBytes(s2c->key, s2c->length);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -91,7 +92,7 @@ prepare_request(NKSN_Instance session, int valid)
|
|||||||
|
|
||||||
if (index == 8) {
|
if (index == 8) {
|
||||||
length = random() % (sizeof (data) + 1);
|
length = random() % (sizeof (data) + 1);
|
||||||
TEST_CHECK(NKSN_AddRecord(session, 1, 1000 + random() % 1000, data, length));
|
TEST_CHECK(NKSN_AddRecord(session, 1, 2000 + random() % 1000, data, length));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (random() % 2) {
|
if (random() % 2) {
|
||||||
@@ -105,9 +106,12 @@ prepare_request(NKSN_Instance session, int valid)
|
|||||||
TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_NTPV4_PORT_NEGOTIATION, data, length));
|
TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_NTPV4_PORT_NEGOTIATION, data, length));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (random() % 2)
|
||||||
|
TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0));
|
||||||
|
|
||||||
if (random() % 2) {
|
if (random() % 2) {
|
||||||
length = random() % (sizeof (data) + 1);
|
length = random() % (sizeof (data) + 1);
|
||||||
TEST_CHECK(NKSN_AddRecord(session, 0, 1000 + random() % 1000, data, length));
|
TEST_CHECK(NKSN_AddRecord(session, 0, 2000 + random() % 1000, data, length));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CHECK(NKSN_EndMessage(session));
|
TEST_CHECK(NKSN_EndMessage(session));
|
||||||
@@ -174,7 +178,8 @@ test_unit(void)
|
|||||||
|
|
||||||
for (i = 0; i < 10000; i++) {
|
for (i = 0; i < 10000; i++) {
|
||||||
context.algorithm = AEAD_AES_SIV_CMAC_256;
|
context.algorithm = AEAD_AES_SIV_CMAC_256;
|
||||||
get_keys(session, context.algorithm, &context.c2s, &context.s2c);
|
get_keys(session, context.algorithm, random() % 100, NKE_NEXT_PROTOCOL_NTPV4,
|
||||||
|
&context.c2s, &context.s2c);
|
||||||
memset(&cookie, 0, sizeof (cookie));
|
memset(&cookie, 0, sizeof (cookie));
|
||||||
TEST_CHECK(NKS_GenerateCookie(&context, &cookie));
|
TEST_CHECK(NKS_GenerateCookie(&context, &cookie));
|
||||||
TEST_CHECK(NKS_DecodeCookie(&cookie, &context2));
|
TEST_CHECK(NKS_DecodeCookie(&cookie, &context2));
|
||||||
|
|||||||
@@ -116,9 +116,19 @@ verify_message(NKSN_Instance inst)
|
|||||||
|
|
||||||
TEST_CHECK(!NKSN_GetRecord(inst, &critical, &t, &length, buffer, sizeof (buffer)));
|
TEST_CHECK(!NKSN_GetRecord(inst, &critical, &t, &length, buffer, sizeof (buffer)));
|
||||||
|
|
||||||
TEST_CHECK(NKSN_GetKeys(inst, AEAD_AES_SIV_CMAC_256, &c2s, &s2c));
|
for (i = 0; i < 10; i++) {
|
||||||
TEST_CHECK(c2s.length == SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256));
|
TEST_CHECK(NKSN_GetKeys(inst, AEAD_AES_SIV_CMAC_256, random(), random(), &c2s, &s2c));
|
||||||
TEST_CHECK(s2c.length == SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256));
|
TEST_CHECK(c2s.length == SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256));
|
||||||
|
TEST_CHECK(s2c.length == SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256));
|
||||||
|
|
||||||
|
if (SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0) {
|
||||||
|
TEST_CHECK(NKSN_GetKeys(inst, AEAD_AES_128_GCM_SIV, random(), random(), &c2s, &s2c));
|
||||||
|
TEST_CHECK(c2s.length == SIV_GetKeyLength(AEAD_AES_128_GCM_SIV));
|
||||||
|
TEST_CHECK(s2c.length == SIV_GetKeyLength(AEAD_AES_128_GCM_SIV));
|
||||||
|
} else {
|
||||||
|
TEST_CHECK(!NKSN_GetKeys(inst, AEAD_AES_128_GCM_SIV, random(), random(), &c2s, &s2c));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@@ -166,6 +176,7 @@ test_unit(void)
|
|||||||
const char *cert, *key;
|
const char *cert, *key;
|
||||||
int sock_fds[2], i;
|
int sock_fds[2], i;
|
||||||
uint32_t cert_id;
|
uint32_t cert_id;
|
||||||
|
NKE_Key c2s, s2c;
|
||||||
|
|
||||||
LCL_Initialise();
|
LCL_Initialise();
|
||||||
TST_RegisterDummyDrivers();
|
TST_RegisterDummyDrivers();
|
||||||
@@ -190,6 +201,9 @@ test_unit(void)
|
|||||||
TEST_CHECK(NKSN_StartSession(server, sock_fds[0], "client", server_cred, 4.0));
|
TEST_CHECK(NKSN_StartSession(server, sock_fds[0], "client", server_cred, 4.0));
|
||||||
TEST_CHECK(NKSN_StartSession(client, sock_fds[1], "server", client_cred, 4.0));
|
TEST_CHECK(NKSN_StartSession(client, sock_fds[1], "server", client_cred, 4.0));
|
||||||
|
|
||||||
|
TEST_CHECK(!NKSN_GetKeys(server, AEAD_AES_SIV_CMAC_256, 0, 0, &c2s, &s2c));
|
||||||
|
TEST_CHECK(!NKSN_GetKeys(client, AEAD_AES_SIV_CMAC_256, 0, 0, &c2s, &s2c));
|
||||||
|
|
||||||
send_message(client);
|
send_message(client);
|
||||||
|
|
||||||
request_received = response_received = 0;
|
request_received = response_received = 0;
|
||||||
@@ -201,6 +215,9 @@ test_unit(void)
|
|||||||
TEST_CHECK(NKSN_IsStopped(server));
|
TEST_CHECK(NKSN_IsStopped(server));
|
||||||
TEST_CHECK(NKSN_IsStopped(client));
|
TEST_CHECK(NKSN_IsStopped(client));
|
||||||
|
|
||||||
|
TEST_CHECK(!NKSN_GetKeys(server, AEAD_AES_SIV_CMAC_256, 0, 0, &c2s, &s2c));
|
||||||
|
TEST_CHECK(!NKSN_GetKeys(client, AEAD_AES_SIV_CMAC_256, 0, 0, &c2s, &s2c));
|
||||||
|
|
||||||
TEST_CHECK(request_received);
|
TEST_CHECK(request_received);
|
||||||
TEST_CHECK(response_received);
|
TEST_CHECK(response_received);
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
#define NKC_IsActive(inst) (random() % 2)
|
#define NKC_IsActive(inst) (random() % 2)
|
||||||
#define NKC_GetRetryFactor(inst) (1)
|
#define NKC_GetRetryFactor(inst) (1)
|
||||||
|
|
||||||
static int get_nts_data(NKC_Instance inst, NKE_Context *context,
|
static int get_nts_data(NKC_Instance inst, NKE_Context *context, NKE_Context *alt_context,
|
||||||
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
|
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
|
||||||
IPSockAddr *ntp_address);
|
IPSockAddr *ntp_address);
|
||||||
#define NKC_GetNtsData get_nts_data
|
#define NKC_GetNtsData get_nts_data
|
||||||
@@ -41,7 +41,7 @@ static int get_nts_data(NKC_Instance inst, NKE_Context *context,
|
|||||||
#include <nts_ntp_client.c>
|
#include <nts_ntp_client.c>
|
||||||
|
|
||||||
static int
|
static int
|
||||||
get_nts_data(NKC_Instance inst, NKE_Context *context,
|
get_nts_data(NKC_Instance inst, NKE_Context *context, NKE_Context *alt_context,
|
||||||
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
|
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
|
||||||
IPSockAddr *ntp_address)
|
IPSockAddr *ntp_address)
|
||||||
{
|
{
|
||||||
@@ -60,6 +60,14 @@ get_nts_data(NKC_Instance inst, NKE_Context *context,
|
|||||||
context->s2c.length = SIV_GetKeyLength(context->algorithm);
|
context->s2c.length = SIV_GetKeyLength(context->algorithm);
|
||||||
UTI_GetRandomBytes(context->s2c.key, context->s2c.length);
|
UTI_GetRandomBytes(context->s2c.key, context->s2c.length);
|
||||||
|
|
||||||
|
if (random() % 2) {
|
||||||
|
*alt_context = *context;
|
||||||
|
UTI_GetRandomBytes(alt_context->c2s.key, alt_context->c2s.length);
|
||||||
|
UTI_GetRandomBytes(alt_context->s2c.key, alt_context->s2c.length);
|
||||||
|
} else {
|
||||||
|
alt_context->algorithm = AEAD_SIV_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
*num_cookies = random() % max_cookies + 1;
|
*num_cookies = random() % max_cookies + 1;
|
||||||
for (i = 0; i < *num_cookies; i++) {
|
for (i = 0; i < *num_cookies; i++) {
|
||||||
cookies[i].length = random() % (sizeof (cookies[i].cookie) + 1);
|
cookies[i].length = random() % (sizeof (cookies[i].cookie) + 1);
|
||||||
|
|||||||
Reference in New Issue
Block a user