[Git][NTPsec/ntpsec][master] NTS-KE-client getting close

Hal Murray gitlab at mg.gitlab.com
Sun Feb 10 14:39:49 UTC 2019


Hal Murray pushed to branch master at NTPsec / ntpsec


Commits:
08b53a4a by Hal Murray at 2019-02-10T14:03:02Z
NTS-KE-client getting close

- - - - -


5 changed files:

- include/nts.h
- include/nts_lib.h
- ntpd/nts.c
- ntpd/nts_client.c
- ntpd/nts_server.c


Changes:

=====================================
include/nts.h
=====================================
@@ -32,11 +32,14 @@ struct ntscfg_t {
 #define NTS_MAX_KEYLEN 64
 /* Client-side state per connection to server */
 struct ntsstate_t {
-    char cookies[NTS_MAX_COOKIES][NTS_COOKIELEN];
-    int current_cookie;
+    int aead;
+    int keylen;
+    int next_cookie;
     int cookie_count;
+    int cookie_length;
+    bool valid[NTS_MAX_COOKIES];
+    uint8_t cookies[NTS_MAX_COOKIES][NTS_COOKIELEN];
     uint8_t c2s[NTS_MAX_KEYLEN], s2c[NTS_MAX_KEYLEN];
-    int keylen;
 };
 
 /* Configuration data for an NTS server or client instance */
@@ -52,9 +55,42 @@ struct ntsconfig_t {
 
 extern struct ntsconfig_t ntsconfig;
 
+int get_key_length(int aead);
 bool nts_make_keys(SSL *ssl, uint8_t *c2s, uint8_t *s2c, int keylen);
+int make_cookie(uint8_t *cookie, uint16_t aead,
+  uint8_t *c2s, uint8_t *s2c, int keylen);
 
 #define NO_OLD_VERSIONS SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1
 
 
+/* buffer packing/unpacking routines.
+ * These aren't NTS specific, but I'll put them here for now.
+ * They work with NTP extensions and NTS-KE data streams.
+ *   names should probably be changed too
+ * NTP extensions are padded to word (4 byte) boundaries.
+ * NTS-KE has no padding.
+ * Maybe versions with padding should have names starting with ntp_?
+ * and use extn rather than record
+ * Note that data on the wire is big endian.
+ * buffer is wire format, not host format.
+ * Hal, 2019-Feb-09
+ */
+
+
+/* working finger into a buffer - updated by append/unpack routines */
+struct BufCtl_t {
+  uint8_t *next;  /* pointer to next data/space */
+  int      left;  /* data/space left */
+};
+typedef struct BufCtl_t BufCtl;
+
+void nts_append_record(BufCtl* buf, uint16_t type, uint16_t length);
+void nts_append_uint16(BufCtl* buf, uint16_t data);
+
+void nts_append_record_uint16(BufCtl* buf, uint16_t type, uint16_t data);
+
+uint16_t nts_next_record(BufCtl* buf, uint16_t *length);
+uint16_t nts_next_uint16(BufCtl* buf);
+
+
 #endif /* GUARD_NTS_H */


=====================================
include/nts_lib.h
=====================================
@@ -43,6 +43,7 @@ typedef struct {
   bool critical;
 } record_bits;
 
+#define CRITICAL 0x8000
 enum record_type {
   end_of_message = 0,
   next_protocol_negotiation = 1,


=====================================
ntpd/nts.c
=====================================
@@ -12,6 +12,9 @@
  * to be set by the config parser.
  */
 #include "config.h"
+
+#include <arpa/inet.h>
+
 #include "ntp_types.h"
 #include "ntpd.h"
 
@@ -120,4 +123,48 @@ int nts_decorate(struct ntscfg_t *cfg, struct ntsstate_t *state,
 	return 0;
 }
 
+/*****************************************************/
+
+void nts_append_record(BufCtl* buf, uint16_t type, uint16_t length) {
+  uint16_t * ptr = (uint16_t *)buf->next;
+  *ptr++ = htons(type);
+  *ptr++ = htons(length);
+  buf->next += sizeof(type)+sizeof(length);
+  buf->left -= sizeof(type)+sizeof(length);
+  /* leaves buf pointing to where data will go */
+  return;
+}
+
+void nts_append_uint16(BufCtl* buf, uint16_t data) {
+  uint16_t * ptr = (uint16_t *)buf->next;
+  *ptr++ = htons(data);
+  buf->next += sizeof(data);
+  buf->left -= sizeof(data);
+  return;
+}
+
+void nts_append_record_uint16(BufCtl* buf, uint16_t type, uint16_t data) {
+  nts_append_record(buf, type, sizeof(uint16_t));
+  nts_append_uint16(buf, data);
+}
+
+
+uint16_t nts_next_record(BufCtl* buf, uint16_t *length) {
+  uint16_t *ptr = (uint16_t *)buf->next;
+  uint16_t type = ntohs(*ptr++);
+  *length = ntohs(*ptr++);
+  buf->next += sizeof(type)+sizeof(*length);
+  buf->left -= sizeof(type)+sizeof(*length);
+  return type;
+}
+
+uint16_t nts_next_uint16(BufCtl* buf) {
+  uint16_t *ptr = (uint16_t *)buf->next;
+  uint16_t data = ntohs(*ptr++);
+  buf->next += sizeof(data);
+  buf->left -= sizeof(data);
+  return data;
+}
+
+
 /* end */


=====================================
ntpd/nts_client.c
=====================================
@@ -17,12 +17,15 @@
 
 #include <openssl/ssl.h>
 #include <openssl/x509.h>
+#include <openssl/x509v3.h>
 
 #include "ntp_types.h"
 #include "ntpd.h"
+#include "nts_lib.h"
 
 
 int open_TCP_socket(const char *hostname);
+bool process_recv_data(struct peer* peer, SSL *ssl);
 
 bool nts_probe(struct peer * peer) {
 
@@ -41,6 +44,8 @@ bool nts_probe(struct peer * peer) {
 
 // Fedora 29:  0x1010101fL  1.1.1a
 // Fedora 28:  0x1010009fL  1.1.0i
+// Debian 9:   0x101000afL  1.1.0j
+// CentOS 7:   0x100020bfL  1.0.2k
 // CentOS 6:   0x1000105fL  1.0.1e
 // NetBSD 8:   0x100020bfL  1.0.2k
 // FreeBSD 12: 0x1010101fL  1.1.1a-freebsd
@@ -55,8 +60,6 @@ bool nts_probe(struct peer * peer) {
    * There is similar code in nts_start_server(). */
   ctx = SSL_CTX_new(TLSv1_2_client_method());
   SSL_CTX_set_options(ctx, NO_OLD_VERSIONS);
-  if (1) // FIXME if (non-default version request)
-    msyslog(LOG_INFO, "NTSc: can't set min/max TLS versions.");
 #endif
 
   SSL_CTX_set_default_verify_paths(ctx);   // Use system root certs
@@ -75,9 +78,25 @@ bool nts_probe(struct peer * peer) {
 #endif
 
   ssl = SSL_new(ctx);
-
   SSL_set_fd(ssl, server);
-  SSL_set_tlsext_host_name(ssl, peer->hostname);
+
+// https://wiki.openssl.org/index.php/Hostname_validation
+#if (OPENSSL_VERSION_NUMBER > 0x1010000fL)
+  SSL_set_hostflags(ssl, X509_CHECK_FLAG_NO_WILDCARDS);
+  SSL_set1_host(ssl, peer->hostname);
+#elif (OPENSSL_VERSION_NUMBER > 0x1000200fL)
+{
+  X509_VERIFY_PARAM *param = SSL_get0_param(ssl);
+  X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_WILDCARDS);
+  if (1 != X509_VERIFY_PARAM_set1_host(param,
+          peer->hostname, strlen(peer->hostname))) {
+      msyslog(LOG_ERR, "NTSc: troubles setting hostflags");
+  }
+  SSL_set_verify(ssl, SSL_VERIFY_PEER, NULL);
+}
+#else
+  msyslog(LOG_ERR, "NTSc: can't check hostname/certificate");
+#endif
 
   SSL_connect(ssl);
   SSL_do_handshake(ssl);
@@ -123,40 +142,46 @@ bool nts_probe(struct peer * peer) {
     }
   }
 
-  // FIXME AEAD_AES_SIV_CMAC_256
-  /* We are using AEAD_AES_SIV_CMAC_256, from RFC 5297
-   * There is no clean API yet.
-   */
-  peer->nts_state.keylen = AEAD_AES_SIV_CMAC_256_KEYLEN;
-  nts_make_keys(ssl,
-    peer->nts_state.c2s,
-    peer->nts_state.s2c,
-    peer->nts_state.keylen);
 
   {
-    uint8_t req[16] = {
-      0x00, 0x01, 0x00, 0x02,
-      0x00, 0x00, 0x00, 0x04,
-      0x00, 0x02, 0x00, 0x0f,
-      0x80, 0x00, 0x00, 0x00 };
-    transfered = SSL_write(ssl, req, sizeof(req));
-    if (sizeof(req) != transfered) {
-      msyslog(LOG_ERR, "NTSc: write failed: %d, %m", transfered);
-      goto bail;
-    }
-    transfered = SSL_read(ssl, buff, sizeof(buff));
-    if (0 > transfered) {
-      msyslog(LOG_ERR, "NTSc: read failed: %d, %m", transfered);
+    struct BufCtl_t buf;
+    int used;
+    buf.next = buff;
+    buf.left = sizeof(buff);
+
+    /* 4.1.2 Next Protocol, 0 for NTP */
+    nts_append_record_uint16(&buf, next_protocol_negotiation, 0);
+
+    /* 4.1.5 AEAD Algorithm List
+     * AEAD_AES_SIV_CMAC_256 is the only one for now */
+    nts_append_record_uint16(&buf, algorithm_negotiation, AEAD_AES_SIV_CMAC_256);
+
+    /* 4.1.1: End, Critical */
+    nts_append_record(&buf, CRITICAL+end_of_message, 0);
+
+    used = sizeof(buff)-buf.left;
+    transfered = SSL_write(ssl, buff, used);
+    if (used != transfered) {
+      msyslog(LOG_ERR, "NTSc: write failed: %d, %d, %m", used, transfered);
       goto bail;
     }
-    msyslog(LOG_ERR, "NTSc: read %d bytes", transfered);
-  }
 
-  SSL_shutdown(ssl);
+  process_recv_data(peer, ssl);
+
+  }
 
-  // unpack buffer
+  // FIXME
+  /* We are using AEAD_AES_SIV_CMAC_256, from RFC 5297
+   * There are no alternatives and no clean API yet.
+   */
+  peer->nts_state.keylen = get_key_length(AEAD_AES_SIV_CMAC_256_KEYLEN);
+  nts_make_keys(ssl,
+    peer->nts_state.c2s,
+    peer->nts_state.s2c,
+    peer->nts_state.keylen);
 
 bail:
+  SSL_shutdown(ssl);
   SSL_free(ssl);
   close(server);
   SSL_CTX_free(ctx);
@@ -237,4 +262,120 @@ bool nts_make_keys(SSL *ssl, uint8_t *c2s, uint8_t *s2c, int keylen) {
   return true;
 }
 
+bool process_recv_data(struct peer* peer, SSL *ssl) {
+  uint8_t  buff[2000];
+  int transfered, idx;
+  struct BufCtl_t buf;
+
+  transfered = SSL_read(ssl, buff, sizeof(buff));
+  if (0 > transfered) {
+    msyslog(LOG_ERR, "NTSc: read failed: %d, %m", transfered);
+    return false;
+  }
+  msyslog(LOG_ERR, "NTSc: read %d bytes", transfered);
+
+  peer->nts_state.aead = -1;
+  peer->nts_state.keylen = 0;
+  peer->nts_state.next_cookie = 0;
+  peer->nts_state.cookie_count = 0;
+  for (int i=0; i<NTS_MAX_COOKIES; i++) peer->nts_state.valid[i] = false;
+
+  buf.next = buff;
+  buf.left = transfered;
+  while (buf.left > 0) {
+    uint16_t length, data;
+    uint16_t type = nts_next_record(&buf, &length);
+    bool critical = false;
+
+    if (CRITICAL & type) {
+      critical = true;
+      type &= ~CRITICAL;
+    }
+    switch (type) {
+      case error:
+        data = nts_next_uint16(&buf);
+        if (sizeof(data) != length)
+          msyslog(LOG_ERR, "NTSc: wrong length on error: %d", length);
+        msyslog(LOG_ERR, "NTSc: error: %d", data);
+        return false;
+      case next_protocol_negotiation:
+        data = nts_next_uint16(&buf);
+        if ((sizeof(data) != length) || (data != 0)) {
+          msyslog(LOG_ERR, "NTSc: NPN-Wrong length or bad data: %d, %d",
+              length, data);
+          return false;
+        }
+        break;
+      case algorithm_negotiation:
+        data = nts_next_uint16(&buf);
+        if ((sizeof(data) != length) || (data != AEAD_AES_SIV_CMAC_256)) {
+          msyslog(LOG_ERR, "NTSc: AN-Wrong length or bad data: %d, %d",
+              length, data);
+          return false;
+        }
+        peer->nts_state.aead = data;
+        break;
+      case new_cookie:
+        if (NTS_COOKIELEN < length) {
+          msyslog(LOG_ERR, "NTSc: NC cookie too big: %d", length);
+          return false;
+        }
+        if (0 == peer->nts_state.cookie_length)
+           peer->nts_state.cookie_length = length;
+        if (length != peer->nts_state.cookie_length) {
+          msyslog(LOG_ERR, "NTSc: Cookie length mismatch %d, %d.",
+            length, peer->nts_state.cookie_length);
+          break;
+          return false;
+        }
+        idx = peer->nts_state.next_cookie;
+        if (NTS_MAX_COOKIES <= peer->nts_state.cookie_count) {
+          msyslog(LOG_ERR, "NTSc: Extra cookie ignored.");
+          buf.next += length;
+          buf.left -= length;
+          break;
+        }
+        memcpy(&peer->nts_state.cookies[idx], buf.next, length);
+        peer->nts_state.valid[idx] = true;
+        peer->nts_state.next_cookie++;
+        peer->nts_state.cookie_count++;
+        buf.next += length;
+        buf.left -= length;
+        break;
+      case end_of_message:
+        if ((0 != length) || !critical) {
+          msyslog(LOG_ERR, "NTSc: EOM-Wrong length or not Critical: %d, %d",
+              length, critical);
+          return false;
+        }
+        if (0 != buf.left) {
+          msyslog(LOG_ERR, "NTSc: EOM not at end: %d", buf.left);
+          return false;
+        }
+        break;
+      default:
+        msyslog(LOG_ERR, "NTSc: received strange type: T=%d, C=%d, L=%d",
+          type, critical, length);
+        if (critical) return false;
+        buf.next += length;
+        buf.left -= length;
+        break;
+    } /* case */
+  }   /* while */
+
+  // FIXME lots of other checks
+  if (-1 == peer->nts_state.aead) {
+    msyslog(LOG_ERR, "NTSc: No AEAD algorithim.");
+    return false;
+  }
+  if (0 == peer->nts_state.cookie_count) {
+    msyslog(LOG_ERR, "NTSc: No cookies.");
+    return false;
+  }
+
+  msyslog(LOG_ERR, "NTSc: Got %d cookies, length %d.",
+    peer->nts_state.cookie_count, peer->nts_state.cookie_length);
+  return true;
+}
+
 /* end */


=====================================
ntpd/nts_server.c
=====================================
@@ -19,9 +19,11 @@
 #include "ntpd.h"
 #include "ntp_stdlib.h"
 
-int create_listener(int port);
-void* nts_ke_listener(void*);
-void nts_ke_request(SSL *ssl);
+static int create_listener(int port);
+static void* nts_ke_listener(void*);
+static void nts_ke_request(SSL *ssl);
+
+int nts_ke_port = 123;
 
 void nts_start_server(void) {
     SSL_CTX *ctx;
@@ -29,6 +31,9 @@ void nts_start_server(void) {
     sigset_t block_mask, saved_sig_mask;
     int rc;
 
+    msyslog(LOG_INFO, "NTSs: starting NTS-KE server listening on port %d",
+        nts_ke_port);
+
 #if (OPENSSL_VERSION_NUMBER > 0x1010000fL)
     ctx = SSL_CTX_new(TLS_server_method());
     SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);  // FIXME
@@ -40,8 +45,11 @@ void nts_start_server(void) {
      * There is similar code in nts_probe(). */
     ctx = SSL_CTX_new(TLSv1_2_server_method());
     SSL_CTX_set_options(ctx, NO_OLD_VERSIONS);
-    if (1) // FIXME if (non-default version request)
-      msyslog(LOG_INFO, "NTSc: can't set min/max TLS versions.");
+#endif
+
+#if (OPENSSL_VERSION_NUMBER > 0x1010000fL)
+    msyslog(LOG_INFO, "NTSs: OpenSSL security level is %d",
+        SSL_CTX_get_security_level(ctx));
 #endif
 
     if (1 != SSL_CTX_use_certificate_chain_file(ctx, "/etc/ntp/cert-chain.pem")) {
@@ -69,12 +77,12 @@ void nts_start_server(void) {
 
 }
 
-void nts_ke_request(SSL *ssl);
 void* nts_ke_listener(void* arg) {
     SSL_CTX *ctx = (SSL_CTX *)arg;
     int sock;
 
-    sock = create_listener(123);
+    // FIXME - need IPv6 too
+    sock = create_listener(nts_ke_port);
     if (sock < 0) return NULL;
 
     while(1) {
@@ -116,9 +124,10 @@ void* nts_ke_listener(void* arg) {
 
 void nts_ke_request(SSL *ssl) {
     uint8_t buff[1000];
-    size_t bytes_read, bytes_written;
+    int bytes_read, bytes_written;
     uint8_t c2s[NTS_MAX_KEYLEN], s2c[NTS_MAX_KEYLEN];
-    int keylen = AEAD_AES_SIV_CMAC_256_KEYLEN;
+    uint8_t cookie[NTS_COOKIELEN];
+    int aead, keylen, cookielen;
 
 
     bytes_read = SSL_read(ssl, buff, sizeof(buff));
@@ -127,6 +136,16 @@ void nts_ke_request(SSL *ssl) {
         return;
     }
 
+    aead = IANA_AEAD_AES_SIV_CMAC_256;
+
+    keylen = get_key_length(aead);
+    nts_make_keys(ssl, c2s, s2c, keylen);
+
+    for (int i=0; i<NTS_MAX_COOKIES; i++) {
+      cookielen = make_cookie(cookie, aead, c2s, s2c, keylen);
+    }
+    cookielen = cookielen;
+    
     // Hack, echo it back
     bytes_written = SSL_write(ssl, buff, bytes_read);
     if (bytes_written != bytes_read) {
@@ -134,9 +153,9 @@ void nts_ke_request(SSL *ssl) {
         return;
     }
 
-    if (!nts_make_keys(ssl, c2s, s2c, keylen))
-      return;
-
+    msyslog(LOG_INFO, "NTSs: Echoed %d bytes", bytes_written);
+    
+    return;
 }
 
 int create_listener(int port)
@@ -164,7 +183,34 @@ int create_listener(int port)
         return -1;
     }
     return sock;
-}   
+}
+
+int get_key_length(int aead) {
+  switch (aead) {
+    case IANA_AEAD_AES_SIV_CMAC_256:
+      return AEAD_AES_SIV_CMAC_256_KEYLEN;
+    default:
+      msyslog(LOG_ERR, "NTS: Strange AEAD code: %d", aead);
+      return 16;
+  }
+}
+
+/* returns actual length */
+int make_cookie(uint8_t *cookie,
+  uint16_t aead,
+  uint8_t *c2s, uint8_t *s2c, int keylen) {
+
+  int length = NTS_COOKIELEN/2;
+
+  if (keylen < length)
+    length = keylen;
+  *cookie = aead & 0xFF;
+  for (int i=0; i<length; i++) {
+    *cookie++ = *c2s++^*s2c++;
+  }
+
+  return length;
+}
 
 
 /* end */



View it on GitLab: https://gitlab.com/NTPsec/ntpsec/commit/08b53a4ab8b69f5506df419c01f6f05266ab1e8e

-- 
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/commit/08b53a4ab8b69f5506df419c01f6f05266ab1e8e
You're receiving this email because of your account on gitlab.com.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.ntpsec.org/pipermail/vc/attachments/20190210/a21b57ac/attachment-0001.html>


More information about the vc mailing list