[Git][NTPsec/ntpsec][master] 3 commits: NS-KE-client off the ground far enough to check certificates
Hal Murray
gitlab at mg.gitlab.com
Sat Feb 9 11:39:18 UTC 2019
Hal Murray pushed to branch master at NTPsec / ntpsec
Commits:
9948b076 by Hal Murray at 2019-02-08T21:07:56Z
NS-KE-client off the ground far enough to check certificates
- - - - -
5ee06547 by Hal Murray at 2019-02-08T21:07:56Z
Setup C2S and S2C
- - - - -
f89d1134 by Hal Murray at 2019-02-09T11:38:40Z
NTS-KE-server is off the ground
- - - - -
9 changed files:
- include/ntpd.h
- include/nts.h
- libntp/ssl_init.c
- ntpd/ntp_proto.c
- ntpd/ntpd.c
- ntpd/nts.c
- ntpd/nts_client.c
- + ntpd/nts_server.c
- ntpd/wscript
Changes:
=====================================
include/ntpd.h
=====================================
@@ -421,6 +421,7 @@ extern const uint8_t num_refclock_conf;
#endif
/* nts.c */
+void nts_start_server(void);
bool nts_probe(struct peer *peer);
int nts_client_ke_request(struct ntscfg_t *);
int nts_server_ke_verify(struct ntscfg_t *);
=====================================
include/nts.h
=====================================
@@ -4,6 +4,8 @@
#ifndef GUARD_NTS_H
#define GUARD_NTS_H
+#include <openssl/ssl.h>
+
#define NTS_MAX_COOKIES 8 /* RFC 4.1.6 */
#define NTS_COOKIELEN 128 /* placeholder - see RFC 6 */
@@ -21,11 +23,20 @@ struct ntscfg_t {
uint32_t expire;
};
+// FIXME AEAD_AES_SIV_CMAC_256
+// We are using AEAD_AES_SIV_CMAC_256, from RFC 5297
+// There is no clean API yet
+#define IANA_AEAD_AES_SIV_CMAC_256 15
+#define AEAD_AES_SIV_CMAC_256_KEYLEN 32
+
+#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 cookie_count;
+ uint8_t c2s[NTS_MAX_KEYLEN], s2c[NTS_MAX_KEYLEN];
+ int keylen;
};
/* Configuration data for an NTS server or client instance */
@@ -41,4 +52,7 @@ struct ntsconfig_t {
extern struct ntsconfig_t ntsconfig;
+bool nts_make_keys(SSL *ssl, uint8_t *c2s, uint8_t *s2c, int keylen);
+
+
#endif /* GUARD_NTS_H */
=====================================
libntp/ssl_init.c
=====================================
@@ -33,8 +33,10 @@ ssl_init(void)
return;
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+ SSL_library_init();
OpenSSL_add_all_digests();
OpenSSL_add_all_ciphers();
+ SSL_load_error_strings();
atexit(&atexit_ssl_cleanup);
#endif
=====================================
ntpd/ntp_proto.c
=====================================
@@ -829,6 +829,13 @@ transmit(
return;
}
+ /* Does server need NTS lookup? */
+ if (peer->cfg.nts_cfg.flags & FLAG_NTS) {
+ peer->cfg.nts_cfg.flags &= !FLAG_NTS;
+ nts_probe(peer);
+ return;
+ }
+
/* Does server need DNS lookup? */
if (peer->cfg.flags & FLAG_DNS) {
peer->outdate = current_time;
=====================================
ntpd/ntpd.c
=====================================
@@ -500,9 +500,7 @@ ntpdmain(
int pipe_fds[2];
int rc;
int exit_code;
-# ifdef SIGDANGER
struct sigaction sa;
-# endif
# endif /* HAVE_WORKING_FORK*/
int op;
@@ -629,6 +627,10 @@ ntpdmain(
# endif /* HAVE_WORKING_FORK */
}
+ /* Ignore SIGPIPE - from OpenSSL */
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sa, NULL);
+
/*
* Set up signals we pay attention to locally.
*/
@@ -902,6 +904,9 @@ ntpdmain(
msyslog(LOG_ERR, "statistics directory %s does not exist or is unwriteable, error %s", statsdir, strerror(errno));
}
+ if (ntsconfig.ntsenable)
+ nts_start_server();
+
mainloop();
/* unreachable, mainloop() never returns */
}
=====================================
ntpd/nts.c
=====================================
@@ -15,7 +15,15 @@
#include "ntp_types.h"
#include "ntpd.h"
-struct ntsconfig_t ntsconfig;
+struct ntsconfig_t ntsconfig = {
+ .ntsenable = false,
+ .mintls = 0,
+ .maxtls = 0,
+ .tlsciphers = NULL,
+ .tlsciphersuites = NULL,
+ .ca = NULL,
+ .cert = NULL
+};
/* By design, there is no per-client-side state on the server */
=====================================
ntpd/nts_client.c
=====================================
@@ -16,6 +16,7 @@
#endif
#include <openssl/ssl.h>
+#include <openssl/x509.h>
#include "ntp_types.h"
#include "ntpd.h"
@@ -28,6 +29,9 @@ bool nts_probe(struct peer * peer) {
SSL_CTX *ctx;
SSL *ssl;
int server = 0;
+ X509 *cert = NULL;
+ uint8_t buff[1000];
+ int transfered;
server = open_TCP_socket(peer->hostname);
if (-1 == server) return false;
@@ -35,33 +39,120 @@ bool nts_probe(struct peer * peer) {
// No error checking yet.
// Ugly since most SSL routines return 1 on success.
-// Fedora 29: 0x1010101fL 1.1.1a
-// Fedora 28: 0x1010009fL 1.1.0i
+// Fedora 29: 0x1010101fL 1.1.1a
+// Fedora 28: 0x1010009fL 1.1.0i
+// CentOS 6: 0x1000105fL 1.0.1e
+// NetBSD 8: 0x100020bfL 1.0.2k
+// FreeBSD 12: 0x1010101fL 1.1.1a-freebsd
#if (OPENSSL_VERSION_NUMBER > 0x1010000fL)
ctx = SSL_CTX_new(TLS_client_method());
+ SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); // FIXME
+ SSL_CTX_set_max_proto_version(ctx, 0);
#else
+ /* Older versions of OpenSSL don't support min/max version requests.
+ * That's OK, since we don't want anything older than 1.2 and
+ * they don't support anything newer. */
ctx = SSL_CTX_new(TLSv1_2_client_method());
+ 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)
- SSL_CTX_set_default_verify_file(ctx); // Use system root certs
-#else
- // FIXME
-#endif
+ SSL_CTX_set_default_verify_paths(ctx); // Use system root certs
-#if (OPENSSL_VERSION_NUMBER > 0x1010000fL)
- // FIXME
- SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
- SSL_CTX_set_max_proto_version(ctx, 0);
-#else
- // FIXME
-#endif
+ if (NULL != ntsconfig.tlsciphers) {
+ if (1 != SSL_CTX_set_cipher_list(ctx, ntsconfig.tlsciphers)) {
+ msyslog(LOG_ERR, "NTSc: error setting TLS ciphers");
+ }
+ }
+ if (NULL != ntsconfig.tlsciphersuites) {
+ if (1 != SSL_CTX_set_ciphersuites(ctx, ntsconfig.tlsciphersuites)) {
+ msyslog(LOG_ERR, "NTSc: error setting TLS ciphersuites");
+ }
+ }
ssl = SSL_new(ctx);
SSL_set_fd(ssl, server);
SSL_set_tlsext_host_name(ssl, peer->hostname);
+ SSL_connect(ssl);
+ SSL_do_handshake(ssl);
+
+ switch (SSL_version(ssl)) {
+#ifdef TLS1_3_VERSION
+ case TLS1_3_VERSION:
+ msyslog(LOG_INFO, "NTSc: Using TLS1.3");
+ break;
+#endif
+ case TLS1_2_VERSION:
+ msyslog(LOG_INFO, "NTSc: Using TLS1.2");
+ break;
+ default:
+ msyslog(LOG_INFO, "NTSc: Strange version: %d, \"%s\"",
+ SSL_version(ssl), SSL_get_version(ssl));
+ break;
+ }
+
+ /* This may be clutter, but this is how to do it. */
+ msyslog(LOG_INFO, "NTSc: Using %s with %d secret bits",
+ SSL_get_cipher_name(ssl),
+ SSL_get_cipher_bits(ssl, NULL));
+
+ cert = SSL_get_peer_certificate(ssl);
+ if (NULL == cert) {
+ msyslog(LOG_INFO, "NTSc: No certificate");
+ } else {
+ X509_NAME *certname;
+ char name[200];
+ int certok;
+ certname = X509_get_subject_name(cert);
+ X509_NAME_oneline(certname, name, sizeof(name));
+ msyslog(LOG_INFO, "NTSc: certificate subject name: %s", name);
+ certname = X509_get_issuer_name(cert);
+ X509_NAME_oneline(certname, name, sizeof(name));
+ msyslog(LOG_INFO, "NTSc: certificate issuer name: %s", name);
+ certok = SSL_get_verify_result(ssl);
+ if (X509_V_OK == certok) {
+ msyslog(LOG_INFO, "NTSc: certificate is valid.");
+ } else {
+ msyslog(LOG_ERR, "NTSc: certificate invalid: %d", certok);
+ }
+ }
+
+ // 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);
+ goto bail;
+ }
+ msyslog(LOG_ERR, "NTSc: read %d bytes", transfered);
+ }
+
+ SSL_shutdown(ssl);
+
+ // unpack buffer
+
+bail:
SSL_free(ssl);
close(server);
SSL_CTX_free(ctx);
@@ -72,32 +163,36 @@ bool nts_probe(struct peer * peer) {
int open_TCP_socket(const char *hostname) {
struct addrinfo hints;
struct addrinfo *answer;
+ sockaddr_u sockaddr;
int gai_rc, err;
int sockfd;
#ifdef HAVE_RES_INIT
- res_init();
+ res_init(); /* see comment in ntp_dns */
#endif
ZERO(hints);
hints.ai_protocol = IPPROTO_TCP;
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC;
- gai_rc = getaddrinfo(hostname, "ntp", &hints, &answer);
+ gai_rc = getaddrinfo(hostname, "8123", &hints, &answer); // FIXME
if (0 != gai_rc) {
- msyslog(LOG_INFO, "DNS: nts_probe: DNS error: %d, %s",
+ msyslog(LOG_INFO, "NTSc: nts_probe: DNS error: %d, %s",
gai_rc, gai_strerror(gai_rc));
return -1;
}
+ memcpy(&sockaddr, answer->ai_addr, answer->ai_addrlen);
+ msyslog(LOG_INFO, "NTSc: nts_probe connecting to %s=%s",
+ hostname, socktoa(&sockaddr));
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd) {
- msyslog(LOG_INFO, "DNS: nts_probe: no socket: %m");
+ msyslog(LOG_INFO, "NTSc: nts_probe: no socket: %m");
} else {
// Use first answer
err = connect(sockfd, answer->ai_addr, answer->ai_addrlen);
if (-1 == err) {
- msyslog(LOG_INFO, "DNS: nts_probe: can't connect: %m");
+ msyslog(LOG_INFO, "NTSc: nts_probe: can't connect: %m");
close(sockfd);
sockfd = -1;
}
@@ -107,4 +202,35 @@ int open_TCP_socket(const char *hostname) {
return sockfd;
}
+bool nts_make_keys(SSL *ssl, uint8_t *c2s, uint8_t *s2c, int keylen) {
+ // char *label = "EXPORTER-network-time-security/1";
+ // Subject: [Ntp] [NTS4NTP] info for NTS developers
+ // From: Martin Langer <mart.langer at ostfalia.de>
+ // Date: Tue, 15 Jan 2019 11:40:13 +0100
+ // bug in OpenSSL 1.1.1a
+ const char *label = "EXPORTER-nts/1";
+ unsigned char context[5] = {0x00, 0x00, 0x00, 0x0f, 0x00};
+ if (1 != SSL_export_keying_material(ssl, c2s, keylen,
+ label, strlen(label),
+ context, 5, 1)) {
+ msyslog(LOG_ERR, "NTS: Error making c2s\n");
+ return false;
+ // ERR_print_errors_fp(stderr);
+ }
+ context[4] = 0x01;
+ if (1 != SSL_export_keying_material(ssl, s2c, keylen,
+ label, strlen(label),
+ context, 5, 1)) {
+ msyslog(LOG_ERR, "NTS: Error making s2c\n");
+ return false;
+ // ERR_print_errors_fp(stderr);
+ }
+ // Hack for debugging - obviously not good for security
+ msyslog(LOG_INFO, "NTS: C2S %02x %02x %02x %02x %02x\n",
+ c2s[0], c2s[1], c2s[2], c2s[3], c2s[4]);
+ msyslog(LOG_INFO, "NTS: S2C %02x %02x %02x %02x %02x\n",
+ s2c[0], s2c[1], s2c[2], s2c[3], s2c[4]);
+ return true;
+}
+
/* end */
=====================================
ntpd/nts_server.c
=====================================
@@ -0,0 +1,158 @@
+/*
+ * nts_server.c - Network Time Security (NTS) server side support
+ *
+ * Section references are to
+ * https://tools.ietf.org/html/draft-ietf-ntp-using-nts-for-ntp-15
+ *
+ */
+#include "config.h"
+
+#include <signal.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/socket.h>
+
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+
+#include "ntp.h"
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+
+int create_listener(int port);
+void* nts_ke_listener(void*);
+void nts_ke_request(SSL *ssl);
+
+void nts_start_server(void) {
+ SSL_CTX *ctx;
+ pthread_t worker;
+ sigset_t block_mask, saved_sig_mask;
+ int rc;
+
+ ctx = SSL_CTX_new(TLS_server_method());
+ // FIXME set min/max versions
+
+ if (1 != SSL_CTX_use_certificate_chain_file(ctx, "/etc/ntp/cert-chain.pem")) {
+ // FIXME log SSL errors
+ msyslog(LOG_ERR, "NTSs: can't load cert-chain");
+ }
+
+ if (1 != SSL_CTX_use_PrivateKey_file(ctx, "/etc/ntp/key.pem", SSL_FILETYPE_PEM)) {
+ // FIXME log SSL errors
+ msyslog(LOG_ERR, "NTSs: can't load private key");
+ }
+ if (1 != SSL_CTX_check_private_key(ctx)) {
+ msyslog(LOG_ERR, "NTSs: Private Key doesn't work ******");
+ } else {
+ msyslog(LOG_INFO, "NTSs: Private Key OK");
+ }
+
+ sigfillset(&block_mask);
+ pthread_sigmask(SIG_BLOCK, &block_mask, &saved_sig_mask);
+ rc = pthread_create(&worker, NULL, nts_ke_listener, ctx);
+ if (rc) {
+ msyslog(LOG_ERR, "NTSs: nts_start_server: error from pthread_create: %m");
+ }
+ pthread_sigmask(SIG_SETMASK, &saved_sig_mask, NULL);
+
+}
+
+void nts_ke_request(SSL *ssl);
+void* nts_ke_listener(void* arg) {
+ SSL_CTX *ctx = (SSL_CTX *)arg;
+ int sock;
+
+ sock = create_listener(123);
+ if (sock < 0) return NULL;
+
+ while(1) {
+ struct sockaddr addr;
+ uint len = sizeof(addr);
+ SSL *ssl;
+
+ int client = accept(sock, &addr, &len);
+ if (client < 0) {
+ msyslog(LOG_ERR, "NTSs: TCP accept failed: %m");
+ continue;
+ }
+ msyslog(LOG_INFO, "NTSs: TCP accept-ed from %s",
+ socktoa((sockaddr_u *)&addr));
+
+ /* This could/should go in a new thread. */ // FIXME
+ ssl = SSL_new(ctx);
+ SSL_set_fd(ssl, client);
+
+ if (SSL_accept(ssl) <= 0) {
+ msyslog(LOG_ERR, "NTSs: SSL accept failed: %m");
+ close(client);
+ continue;
+ }
+ msyslog(LOG_INFO, "NTSs: SSL accept-ed from %s",
+ socktoa((sockaddr_u *)&addr));
+ msyslog(LOG_INFO, "NTSs: Using TLS version %s, cipher %s with %d secret bits",
+ SSL_get_version(ssl),
+ SSL_get_cipher_name(ssl),
+ SSL_get_cipher_bits(ssl, NULL));
+
+ nts_ke_request(ssl);
+
+ SSL_shutdown(ssl);
+ SSL_free(ssl);
+ close(client);
+ }
+}
+
+void nts_ke_request(SSL *ssl) {
+ uint8_t buff[1000];
+ size_t bytes_read, bytes_written;
+ uint8_t c2s[NTS_MAX_KEYLEN], s2c[NTS_MAX_KEYLEN];
+ int keylen = AEAD_AES_SIV_CMAC_256_KEYLEN;
+
+
+ bytes_read = SSL_read(ssl, buff, sizeof(buff));
+ if (0 >= bytes_read) {
+ msyslog(LOG_INFO, "NTSs: SSL_read error");
+ return;
+ }
+
+ // Hack, echo it back
+ bytes_written = SSL_write(ssl, buff, bytes_read);
+ if (bytes_written != bytes_read) {
+ msyslog(LOG_INFO, "NTSs: SSL_write error");
+ return;
+ }
+
+ if (!nts_make_keys(ssl, c2s, s2c, keylen))
+ return;
+
+}
+
+int create_listener(int port)
+{
+ int sock;
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ msyslog(LOG_ERR, "NTSs: Can't create socket: %m");
+ return -1;
+ }
+
+ if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+ msyslog(LOG_ERR, "NTSs: can't bind: %m");
+ return -1;
+ }
+
+ if (listen(sock, 1) < 0) {
+ msyslog(LOG_ERR, "NTSs: can't listen: %m");
+ return -1;
+ }
+ return sock;
+}
+
+
+/* end */
=====================================
ntpd/wscript
=====================================
@@ -57,6 +57,7 @@ def build(ctx):
"ntp_restrict.c",
"ntp_util.c",
"nts.c",
+ "nts_server.c",
"nts_client.c",
"nts_lib.c",
]
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/compare/96edbcc8aa0329b5e2887664dee8199745fa56e5...f89d113491f47096cb85b9ac60f23a7dfd4512af
--
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/compare/96edbcc8aa0329b5e2887664dee8199745fa56e5...f89d113491f47096cb85b9ac60f23a7dfd4512af
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/20190209/9842d7e2/attachment-0001.html>
More information about the vc
mailing list