]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
nts: make server and client AEAD algorithms configurable
authorMiroslav Lichvar <mlichvar@redhat.com>
Thu, 3 Oct 2024 12:25:53 +0000 (14:25 +0200)
committerMiroslav Lichvar <mlichvar@redhat.com>
Thu, 3 Oct 2024 14:09:53 +0000 (16:09 +0200)
Add ntsaeads directive to specify a list of AEAD algorithms enabled for
NTS. The list is shared between the server and client. For the client it
also specifies the order of priority. The default is "30 15", matching
the previously hardcoded preference of AES-128-GCM-SIV (30) over
AES-SIV-CMAC-256 (15).

conf.c
conf.h
doc/chrony.conf.adoc
nts_ke_client.c
nts_ke_server.c
test/simulation/139-nts

diff --git a/conf.c b/conf.c
index a06423e4f7d6782713a4f1b82efac753f6458689..778cd27417513e26ce94116230e8b4410a206744 100644 (file)
--- a/conf.c
+++ b/conf.c
@@ -57,6 +57,7 @@ static int parse_string(char *line, char **result);
 static int parse_int(char *line, int *result);
 static int parse_double(char *line, double *result);
 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_authselectmode(char *);
@@ -261,7 +262,10 @@ static char *user;
 /* Address refresh interval */
 static int refresh = 1209600; /* 2 weeks */
 
+#define DEFAULT_NTS_AEADS "30 15"
+
 /* NTS server and client configuration */
+static ARR_Instance nts_aeads; /* array of int */
 static char *nts_dump_dir = NULL;
 static char *nts_ntp_server = NULL;
 static ARR_Instance nts_server_cert_files; /* array of (char *) */
@@ -397,6 +401,8 @@ check_number_of_args(char *line, int num)
 void
 CNF_Initialise(int r, int client_only)
 {
+  char buf[10];
+
   restarted = r;
 
   hwts_interfaces = ARR_CreateInstance(sizeof (CNF_HwTsInterface));
@@ -410,6 +416,9 @@ CNF_Initialise(int r, int client_only)
   ntp_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_key_files = 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(cmd_restrictions);
 
+  ARR_DestroyInstance(nts_aeads);
   ARR_DestroyInstance(nts_server_cert_files);
   ARR_DestroyInstance(nts_server_key_files);
   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);
   } else if (!strcasecmp(command, "ntpsigndsocket")) {
     parse_string(p, &ntp_signd_socket);
+  } else if (!strcasecmp(command, "ntsaeads")) {
+    parse_ints(p, nts_aeads);
   } else if (!strcasecmp(command, "ntsratelimit")) {
     parse_ratelimit(p, &nts_ratelimit_enabled, &nts_ratelimit_interval,
                     &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
 parse_source(char *line, char *type, int fatal)
 {
@@ -2599,6 +2630,14 @@ CNF_GetRefresh(void)
 
 /* ================================================== */
 
+ARR_Instance
+CNF_GetNtsAeads(void)
+{
+  return nts_aeads;
+}
+
+/* ================================================== */
+
 char *
 CNF_GetNtsDumpDir(void)
 {
diff --git a/conf.h b/conf.h
index cc0f17ea3370d24ce4e2e20a6a40d85a1779f6bb..8136275d0528d80e04cdad41586e6ab66a220a11 100644 (file)
--- a/conf.h
+++ b/conf.h
@@ -29,6 +29,7 @@
 #define GOT_CONF_H
 
 #include "addressing.h"
+#include "array.h"
 #include "reference.h"
 #include "sources.h"
 
@@ -163,6 +164,7 @@ extern int CNF_GetPtpDomain(void);
 
 extern int CNF_GetRefresh(void);
 
+extern ARR_Instance CNF_GetNtsAeads(void);
 extern char *CNF_GetNtsDumpDir(void);
 extern char *CNF_GetNtsNtpServer(void);
 extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys);
index 2c993dba62d8d4441b6d70635dca072826fe6f56..4b7e28d3846e827883ded89398ffa6c14dd05a18 100644 (file)
@@ -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
 _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_::
 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
@@ -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
 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_::
 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
index dfd5101e88368a3e5239644a91a31acb03c2bcb7..1fcbc3e0abccd65c7fbccc620010dbbb7b15e77b 100644 (file)
@@ -100,12 +100,14 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *arg
 
 /* ================================================== */
 
+#define MAX_AEAD_ALGORITHMS 4
+
 static int
 prepare_request(NKC_Instance inst)
 {
   NKSN_Instance session = inst->session;
-  uint16_t data[2];
-  int i, length;
+  uint16_t data[MAX_AEAD_ALGORITHMS];
+  int i, aead_algorithm, length;
 
   NKSN_BeginMessage(session);
 
@@ -113,11 +115,12 @@ prepare_request(NKC_Instance inst)
   if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, sizeof (data[0])))
     return 0;
 
-  length = 0;
-  if (SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0)
-    data[length++] = htons(AEAD_AES_128_GCM_SIV);
-  if (SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) > 0)
-    data[length++] = htons(AEAD_AES_SIV_CMAC_256);
+  for (i = length = 0; i < ARR_GetSize(CNF_GetNtsAeads()) && length < MAX_AEAD_ALGORITHMS;
+       i++) {
+    aead_algorithm = *(int *)ARR_GetElement(CNF_GetNtsAeads(), i);
+    if (SIV_GetKeyLength(aead_algorithm) > 0)
+      data[length++] = htons(aead_algorithm);
+  }
   if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data,
                       length * sizeof (data[0])))
     return 0;
@@ -177,15 +180,22 @@ process_response(NKC_Instance inst)
         next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
         break;
       case NKE_RECORD_AEAD_ALGORITHM:
-        if (length != 2 || (ntohs(data[0]) != AEAD_AES_SIV_CMAC_256 &&
-                            ntohs(data[0]) != AEAD_AES_128_GCM_SIV) ||
-            SIV_GetKeyLength(ntohs(data[0])) <= 0) {
-          DEBUG_LOG("Unexpected NTS-KE AEAD algorithm");
+        if (length != 2) {
+          DEBUG_LOG("Unexpected AEAD algorithm");
           error = 1;
           break;
         }
-        aead_algorithm = ntohs(data[0]);
-        inst->context.algorithm = aead_algorithm;
+        for (i = 0; i < ARR_GetSize(CNF_GetNtsAeads()); i++) {
+          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) {
index eeeece3804b44b11d1ac8212bebc8d85ea20ceba..5f10bc17d8176abbe51b13ee6dd8b8a9504c8836 100644 (file)
@@ -426,8 +426,8 @@ process_request(NKSN_Instance session)
   int next_protocol_records = 0, aead_algorithm_records = 0;
   int next_protocol_values = 0, aead_algorithm_values = 0;
   int next_protocol = -1, aead_algorithm = -1, error = -1;
+  int i, j, critical, type, length;
   int compliant_128gcm = 0;
-  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);
@@ -462,9 +462,12 @@ process_request(NKSN_Instance session)
 
         for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
           aead_algorithm_values++;
-          /* Use the first supported algorithm */
-          if (aead_algorithm < 0 && SIV_GetKeyLength(ntohs(data[i])) > 0)
-            aead_algorithm = ntohs(data[i]);
+          /* Use the first enabled and supported algorithm */
+          for (j = 0; j < ARR_GetSize(CNF_GetNtsAeads()); j++) {
+            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;
       case NKE_RECORD_COMPLIANT_128GCM_EXPORT:
index f1d2de32adcdf77c7720213f341b2296c83123ef..27689b86290bd17c46c4a1a13ff4735e316461d4 100755 (executable)
@@ -313,4 +313,31 @@ check_sync && test_fail
 check_file_messages "  3       1       .*      123     " 0 0 log.packets || test_fail
 check_file_messages "  3       2       .*      123     " 0 0 log.packets || test_fail
 
+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