]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
cmdmon: add authdata command
authorMiroslav Lichvar <mlichvar@redhat.com>
Wed, 13 May 2020 14:54:55 +0000 (16:54 +0200)
committerMiroslav Lichvar <mlichvar@redhat.com>
Thu, 14 May 2020 13:37:38 +0000 (15:37 +0200)
Add a command to display information about authentication of NTP
sources.

16 files changed:
candm.h
client.c
cmdmon.c
doc/chronyc.adoc
ntp_auth.c
ntp_auth.h
ntp_core.c
ntp_core.h
ntp_sources.c
ntp_sources.h
nts_ntp_client.c
nts_ntp_client.h
pktlength.c
reports.h
stubs.c
test/simulation/110-chronyc

diff --git a/candm.h b/candm.h
index 0b28d774715c2435b970842c3ad160604cbbd20d..0a0ea98fd46837692ee994e0601162afb2991541 100644 (file)
--- a/candm.h
+++ b/candm.h
 #define REQ_ADD_SOURCE 64
 #define REQ_NTP_SOURCE_NAME 65
 #define REQ_RESET_SOURCES 66
-#define N_REQUEST_TYPES 67
+#define REQ_AUTH_DATA 67
+#define N_REQUEST_TYPES 68
 
 /* Structure used to exchange timespecs independent of time_t size */
 typedef struct {
@@ -351,6 +352,11 @@ typedef struct {
   int32_t EOR;
 } REQ_NTPSourceName;
 
+typedef struct {
+  IPAddr ip_addr;
+  int32_t EOR;
+} REQ_AuthData;
+
 /* ================================================== */
 
 #define PKT_TYPE_CMD_REQUEST 1
@@ -454,6 +460,7 @@ typedef struct {
     REQ_SmoothTime smoothtime;
     REQ_NTPData ntp_data;
     REQ_NTPSourceName ntp_source_name;
+    REQ_AuthData auth_data;
   } data; /* Command specific parameters */
 
   /* Padding used to prevent traffic amplification.  It only defines the
@@ -491,7 +498,8 @@ typedef struct {
 #define RPY_MANUAL_TIMESTAMP2 17
 #define RPY_MANUAL_LIST2 18
 #define RPY_NTP_SOURCE_NAME 19
-#define N_REPLY_TYPES 20
+#define RPY_AUTH_DATA 20
+#define N_REPLY_TYPES 21
 
 /* Status codes */
 #define STT_SUCCESS 0
@@ -712,6 +720,22 @@ typedef struct {
   int32_t EOR;
 } RPY_NTPSourceName;
 
+#define RPY_AD_MD_NONE 0
+#define RPY_AD_MD_SYMMETRIC 1
+#define RPY_AD_MD_NTS 2
+
+typedef struct {
+  uint16_t mode;
+  uint16_t key_type;
+  uint32_t key_id;
+  uint16_t key_length;
+  uint16_t ke_attempts;
+  uint32_t last_ke_ago;
+  uint16_t cookies;
+  uint16_t nak;
+  int32_t EOR;
+} RPY_AuthData;
+
 typedef struct {
   uint8_t version;
   uint8_t pkt_type;
@@ -742,6 +766,7 @@ typedef struct {
     RPY_Smoothing smoothing;
     RPY_NTPData ntp_data;
     RPY_NTPSourceName ntp_source_name;
+    RPY_AuthData auth_data;
   } data; /* Reply specific parameters */
 
 } CMD_Reply;
index 5cbd4020c47f51d0ea6c25c4bbe1a1fca97c12b8..77c9184844a478ce17af34fa84881f53da381d82 100644 (file)
--- a/client.c
+++ b/client.c
@@ -1214,6 +1214,7 @@ give_help(void)
     "\0\0"
     "NTP sources:\0\0"
     "activity\0Check how many NTP sources are online/offline\0"
+    "authdata [-a]\0Display information about authentication\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"
@@ -1312,7 +1313,7 @@ command_name_generator(const char *text, int state)
 {
   const char *name, **names[TAB_COMPLETE_MAX_INDEX];
   const char *base_commands[] = {
-    "accheck", "activity", "add", "allow", "burst",
+    "accheck", "activity", "add", "allow", "authdata", "burst",
     "clients", "cmdaccheck", "cmdallow", "cmddeny", "cyclelogs", "delete",
     "deny", "dns", "dump", "exit", "help", "keygen", "local", "makestep",
     "manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
@@ -2363,6 +2364,82 @@ process_cmd_tracking(char *line)
 
 /* ================================================== */
 
+static int
+process_cmd_authdata(char *line)
+{
+  CMD_Request request;
+  CMD_Reply reply;
+  IPAddr ip_addr;
+  uint32_t i, source_mode, n_sources;
+  int all, verbose;
+  const char *mode_str;
+  char name[256];
+
+  parse_sources_options(line, &all, &verbose);
+
+  request.command = htons(REQ_N_SOURCES);
+  if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
+    return 0;
+
+  n_sources = ntohl(reply.data.n_sources.n_sources);
+
+  print_header("Name/IP address             Mode KeyID Type  Len Last Atmp Cook  NAK");
+
+  /*           "NNNNNNNNNNNNNNNNNNNNNNNNNNN MMMM KKKKK AAAA LLLL LLLL AAAA CCCC NNNN" */
+
+  for (i = 0; i < n_sources; i++) {
+    request.command = htons(REQ_SOURCE_DATA);
+    request.data.source_data.index = htonl(i);
+    if (!request_reply(&request, &reply, RPY_SOURCE_DATA, 0))
+      return 0;
+
+    source_mode = ntohs(reply.data.source_data.mode);
+    if (source_mode != RPY_SD_MD_CLIENT && source_mode != RPY_SD_MD_PEER)
+      continue;
+
+    UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &ip_addr);
+    if (!all && ip_addr.family == IPADDR_ID)
+      continue;
+
+    request.command = htons(REQ_AUTH_DATA);
+    request.data.auth_data.ip_addr = reply.data.source_data.ip_addr;
+    if (!request_reply(&request, &reply, RPY_AUTH_DATA, 0))
+      return 0;
+
+    format_name(name, sizeof (name), 25, 0, 0, 1, &ip_addr);
+
+    switch (ntohs(reply.data.auth_data.mode)) {
+      case RPY_AD_MD_NONE:
+        mode_str = "-";
+        break;
+      case RPY_AD_MD_SYMMETRIC:
+        mode_str = "SK";
+        break;
+      case RPY_AD_MD_NTS:
+        mode_str = "NTS";
+        break;
+      default:
+        mode_str = "?";
+        break;
+    }
+
+    print_report("%-27s %4s %5U %4d %4d %I %4d %4d %4d\n",
+                 name, mode_str,
+                 (unsigned long)ntohl(reply.data.auth_data.key_id),
+                 ntohs(reply.data.auth_data.key_type),
+                 ntohs(reply.data.auth_data.key_length),
+                 (unsigned long)ntohl(reply.data.auth_data.last_ke_ago),
+                 ntohs(reply.data.auth_data.ke_attempts),
+                 ntohs(reply.data.auth_data.cookies),
+                 ntohs(reply.data.auth_data.nak),
+                 REPORT_END);
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
 static int
 process_cmd_ntpdata(char *line)
 {
@@ -3050,6 +3127,9 @@ process_line(char *line)
     } else {
       do_normal_submit = process_cmd_allow(&tx_message, line);
     }
+  } else if (!strcmp(command, "authdata")) {
+    do_normal_submit = 0;
+    ret = process_cmd_authdata(line);
   } else if (!strcmp(command, "burst")) {
     do_normal_submit = process_cmd_burst(&tx_message, line);
   } else if (!strcmp(command, "clients")) {
index 13a0fad6e9ec0c95898bd4f047b0d05efab4f7e2..9a7927f086eccb08dd21b2a9333dd5a94668d02a 100644 (file)
--- a/cmdmon.c
+++ b/cmdmon.c
@@ -136,6 +136,7 @@ static const char permissions[] = {
   PERMIT_AUTH, /* ADD_SOURCE */
   PERMIT_OPEN, /* NTP_SOURCE_NAME */
   PERMIT_AUTH, /* RESET_SOURCES */
+  PERMIT_AUTH, /* AUTH_DATA */
 };
 
 /* ================================================== */
@@ -1235,6 +1236,46 @@ handle_reset_sources(CMD_Request *rx_message, CMD_Reply *tx_message)
   LCL_NotifyExternalTimeStep(&now, &cooked_now, 0.0, 0.0);
 }
 
+/* ================================================== */
+
+static void
+handle_auth_data(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+  RPT_AuthReport report;
+  IPAddr ip_addr;
+
+  UTI_IPNetworkToHost(&rx_message->data.auth_data.ip_addr, &ip_addr);
+
+  if (!NSR_GetAuthReport(&ip_addr, &report)) {
+    tx_message->status = htons(STT_NOSUCHSOURCE);
+    return;
+  }
+
+  tx_message->reply = htons(RPY_AUTH_DATA);
+
+  switch (report.mode) {
+    case NTP_AUTH_NONE:
+      tx_message->data.auth_data.mode = htons(RPY_AD_MD_NONE);
+      break;
+    case NTP_AUTH_SYMMETRIC:
+      tx_message->data.auth_data.mode = htons(RPY_AD_MD_SYMMETRIC);
+      break;
+    case NTP_AUTH_NTS:
+      tx_message->data.auth_data.mode = htons(RPY_AD_MD_NTS);
+      break;
+    default:
+      break;
+  }
+
+  tx_message->data.auth_data.key_type = htons(report.key_type);
+  tx_message->data.auth_data.key_id = htonl(report.key_id);
+  tx_message->data.auth_data.key_length = htons(report.key_length);
+  tx_message->data.auth_data.ke_attempts = htons(report.ke_attempts);
+  tx_message->data.auth_data.last_ke_ago = htonl(report.last_ke_ago);
+  tx_message->data.auth_data.cookies = htons(report.cookies);
+  tx_message->data.auth_data.nak = htons(report.nak);
+}
+
 /* ================================================== */
 /* Read a packet and process it */
 
@@ -1617,6 +1658,10 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
           handle_reset_sources(&rx_message, &tx_message);
           break;
 
+        case REQ_AUTH_DATA:
+          handle_auth_data(&rx_message, &tx_message);
+          break;
+
         default:
           DEBUG_LOG("Unhandled command %d", rx_command);
           tx_message.status = htons(STT_FAILED);
index 1b7513f840fb1760751887f1a2a61bc68975d9fb..514a95fae1cbb740b2f701991d89dc5ea3117147 100644 (file)
@@ -454,6 +454,73 @@ the offline state.
 the name of the server or peer was not resolved to an address yet; this source is
 not visible in the *sources* and *sourcestats* reports.
 
+[[authdata]]*authdata* [*-a*]::
+The *authdata* command displays information specific to authentication of NTP
+sources. If the *-a* option is specified, all sources are displayed, including
+those that do not have a known address yet. An example of the output is
+shown below.
++
+----
+Name/IP address             Mode KeyID Type  Len Last Atmp Cook  NAK
+====================================================================
+foo.example.com              NTS     1   15  256 135m    0    8    0
+bar.example.com               SK    30   13  128    -    0    0    0
+baz.example.com                -     0    0    0    -    0    0    0
+----
++
+The columns are as follows:
++
+*Name/IP address*:::
+This column shows the name or the IP address of the source.
+*Mode*:::
+This column shows which mechanism authenticates NTP packets received from the
+source. _NTS_ means Network Time Security, _SK_ means a symmetric key, and _-_
+means authentication is disabled.
+*KeyID*:::
+This column shows an identifier of the key used for authentication. With a
+symmetric key, it is the ID from the <<chrony.conf.adoc#keyfile,key file>>.
+With NTS, it is a number starting at zero and incremented by one with each
+successful key establishment using the NTS-KE protocol, i.e. it shows how many
+times the key establishment was performed with this source.
+*Type*:::
+This columns shows an identifier of the algorithm used for authentication.
+With a symmetric key, it is the hash function or cipher specified in the key
+file. With NTS, it is an authenticated encryption with associated data (AEAD)
+algorithm, which is negotiated in the NTS-KE protocol. The following values can
+be reported:
+* 1: MD5
+* 2: SHA1
+* 3: SHA256
+* 4: SHA384
+* 5: SHA512
+* 6: SHA3-224
+* 7: SHA3-256
+* 8: SHA3-384
+* 9: SHA3-512
+* 10: TIGER
+* 11: WHIRLPOOL
+* 13: AES128
+* 14: AES256
+* 15: AEAD-AES-SIV-CMAC-256
+*Len*:::
+This column shows the length of the key in bits.
+*Last*:::
+This column shows how long ago the last successful key establishment was
+performed. It is in seconds, or letters _m_, _h_, _d_ or _y_ indicate minutes,
+hours, days, or years.
+*Atmp*:::
+This column shows the number of attempts to perform the key establishment since
+the last successful key establishment. A number larger than 1 indicates a
+problem with the network or server.
+*Cook*:::
+This column shows the number of NTS cookies that *chronyd* currently has. If
+the key establishment was successful, a number smaller than 8 indicates a
+problem with the network or server.
+*NAK*:::
+This column shows whether an NTS NAK was received since the last authenticated
+response. A non-zero number indicates that *chronyd* has used a cookie which is
+no longer valid, or it might be under a denial-of-service attack.
+
 [[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
index 1cf55dafb5cba11e8fcef42d10893f5030cb321c..18c9ea9d05fc23d0d64b8e127cbe1255510d5c46 100644 (file)
@@ -498,3 +498,28 @@ NAU_DumpData(NAU_Instance instance)
       break;
   }
 }
+
+/* ================================================== */
+
+void
+NAU_GetReport(NAU_Instance instance, RPT_AuthReport *report)
+{
+  memset(report, 0, sizeof (*report));
+
+  report->mode = instance->mode;
+  report->last_ke_ago = -1;
+
+  switch (instance->mode) {
+    case NTP_AUTH_NONE:
+      break;
+    case NTP_AUTH_SYMMETRIC:
+      report->key_id = instance->key_id;
+      KEY_GetKeyInfo(instance->key_id, &report->key_type, &report->key_length);
+      break;
+    case NTP_AUTH_NTS:
+      NNC_GetReport(instance->nts, report);
+      break;
+    default:
+      assert(0);
+  }
+}
index 3d8014e307b3ce9482d15ef7e477c06dba9dabb5..d336b5553e061f57ea71b29d5f3c8795189948a8 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "addressing.h"
 #include "ntp.h"
+#include "reports.h"
 
 typedef struct NAU_Instance_Record *NAU_Instance;
 
@@ -89,4 +90,7 @@ extern void NAU_ChangeAddress(NAU_Instance instance, IPAddr *address);
 /* Save authentication-specific data to speed up the next start */
 extern void NAU_DumpData(NAU_Instance instance);
 
+/* Provide a report about the current authentication state */
+extern void NAU_GetReport(NAU_Instance instance, RPT_AuthReport *report);
+
 #endif
index f2a3138edb56974b819b8292acde8c6c01683ded..4d319fd33d843c78bf76a51de86dc053cc947cfe 100644 (file)
@@ -2444,6 +2444,14 @@ NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timespec *n
 
 /* ================================================== */
 
+void
+NCR_GetAuthReport(NCR_Instance inst, RPT_AuthReport *report)
+{
+  NAU_GetReport(inst->auth, report);
+}
+
+/* ================================================== */
+
 void
 NCR_GetNTPReport(NCR_Instance inst, RPT_NTPReport *report)
 {
index 32a9581ca6752aa936979705cfa0ac8ad3822039..7f86f839bdc448393d0375c39b886ed0f5d95087 100644 (file)
@@ -122,6 +122,7 @@ extern void NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target);
 extern void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples);
 
 extern void NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timespec *now);
+extern void NCR_GetAuthReport(NCR_Instance inst, RPT_AuthReport *report);
 extern void NCR_GetNTPReport(NCR_Instance inst, RPT_NTPReport *report);
 
 extern int NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all);
index 81e1fa311d220893e768c937adf71df1b791e53b..f6710fae58d25006f23dc0f4fc3c4ef6f353c983 100644 (file)
@@ -1249,6 +1249,24 @@ NSR_ReportSource(RPT_SourceReport *report, struct timespec *now)
   }
 }
 
+/* ================================================== */
+
+int
+NSR_GetAuthReport(IPAddr *address, RPT_AuthReport *report)
+{
+  NTP_Remote_Address rem_addr;
+  int slot, found;
+
+  rem_addr.ip_addr = *address;
+  rem_addr.port = 0;
+  find_slot(&rem_addr, &slot, &found);
+  if (!found)
+    return 0;
+
+  NCR_GetAuthReport(get_record(slot)->data, report);
+  return 1;
+}
+
 /* ================================================== */
 /* The ip address is assumed to be completed on input, that is how we
    identify the source record. */
index 77e8803ef991bb7671606ce00d774f928bf08604..adb241effdc438ad20a018f046fd00df0d8a53fa 100644 (file)
@@ -136,6 +136,8 @@ extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAd
 
 extern void NSR_ReportSource(RPT_SourceReport *report, struct timespec *now);
 
+extern int NSR_GetAuthReport(IPAddr *address, RPT_AuthReport *report);
+
 extern int NSR_GetNTPReport(RPT_NTPReport *report);
 
 extern void NSR_GetActivityReport(RPT_ActivityReport *report);
index 6acedd2ce5c51ff4fb635ee9f979f934cbe8cf35..aa01b4f67a538190a81de660c5d2b53506a93a57 100644 (file)
@@ -633,3 +633,20 @@ NNC_DumpData(NNC_Instance inst)
 {
   save_cookies(inst);
 }
+
+/* ================================================== */
+
+void
+NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report)
+{
+  report->key_id = inst->context_id;
+  report->key_type = inst->context.algorithm;
+  report->key_length = 8 * inst->context.s2c.length;
+  report->ke_attempts = inst->nke_attempts;
+  if (report->key_length > 0)
+    report->last_ke_ago = SCH_GetLastEventMonoTime() - inst->last_nke_success;
+  else
+    report->last_ke_ago = -1;
+  report->cookies = inst->num_cookies;
+  report->nak = inst->nak_response;
+}
index 4c410da8c36d68722ac419378d08c4da27c379f3..18e3357d1d4081dfa6d9bb1f954a4e0206e73214 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "addressing.h"
 #include "ntp.h"
+#include "reports.h"
 
 typedef struct NNC_Instance_Record *NNC_Instance;
 
@@ -45,4 +46,6 @@ extern void NNC_ChangeAddress(NNC_Instance inst, IPAddr *address);
 
 extern void NNC_DumpData(NNC_Instance inst);
 
+extern void NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report);
+
 #endif
index 83f848f43718987ea79b6d12238c4e05e219f301..69032db29e7405ad8f1ea7bda28007d8148d65a3 100644 (file)
@@ -124,6 +124,7 @@ static const struct request_length request_lengths[] = {
   REQ_LENGTH_ENTRY(ntp_source_name,
                    ntp_source_name),            /* NTP_SOURCE_NAME */
   REQ_LENGTH_ENTRY(null, null),                 /* RESET_SOURCES */
+  REQ_LENGTH_ENTRY(auth_data, auth_data),       /* AUTH_DATA */
 };
 
 static const uint16_t reply_lengths[] = {
@@ -147,6 +148,7 @@ static const uint16_t reply_lengths[] = {
   RPY_LENGTH_ENTRY(manual_timestamp),           /* MANUAL_TIMESTAMP2 */
   RPY_LENGTH_ENTRY(manual_list),                /* MANUAL_LIST2 */
   RPY_LENGTH_ENTRY(ntp_source_name),            /* NTP_SOURCE_NAME */
+  RPY_LENGTH_ENTRY(auth_data),                  /* AUTH_DATA */
 };
 
 /* ================================================== */
index 6a2467014dd26c7d850f4460ded0b0f2ab51e33b..20881b4b025fc1be165241dfbad2528279e5f842 100644 (file)
--- a/reports.h
+++ b/reports.h
@@ -160,4 +160,15 @@ typedef struct {
   uint32_t total_valid_count;
 } RPT_NTPReport;
 
+typedef struct {
+  NTP_AuthMode mode;
+  uint32_t key_id;
+  int key_type;
+  int key_length;
+  int ke_attempts;
+  uint32_t last_ke_ago;
+  int cookies;
+  int nak;
+} RPT_AuthReport;
+
 #endif /* GOT_REPORTS_H */
diff --git a/stubs.c b/stubs.c
index 35c612f682989d4db8996602d3bb2df72564f5e5..fe1cd50e765b6c69001cf0145b5d781ae23ffa88 100644 (file)
--- a/stubs.c
+++ b/stubs.c
@@ -525,6 +525,11 @@ NNC_DumpData(NNC_Instance inst)
 {
 }
 
+void
+NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report)
+{
+}
+
 void
 NKC_Initialise(void)
 {
index 40df0871148d365c92826d964407c5b7efd78449..058c9c3779b90ebab3e971189fe55c4fd7568c35 100755 (executable)
@@ -76,6 +76,7 @@ check_chronyc_output "^Reference ID    : C0A87B01 \(node1\.net1\.clk\)" \
 server_strata=0
 chronyc_start=0
 client_conf=""
+server_conf="server 192.168.123.1"
 limit=1
 
 for chronyc_conf in \
@@ -93,6 +94,7 @@ for chronyc_conf in \
        "allow ::/0" \
        "allow" \
        "allow all 10/24" \
+       "authdata" \
        "burst 5/10" \
        "burst 3/5 255.255.255.0/1.2.3.0" \
        "burst 1/2 1.2.3.0/24" \