[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