Skip to content
Commits on Source (3)
......@@ -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 *);
......
......@@ -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 */
......@@ -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
......
......@@ -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;
......
......@@ -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 */
}
......
......@@ -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 */
......
......@@ -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@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 */
/*
* 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 */
......@@ -57,6 +57,7 @@ def build(ctx):
"ntp_restrict.c",
"ntp_util.c",
"nts.c",
"nts_server.c",
"nts_client.c",
"nts_lib.c",
]
......