Commits (16)
......@@ -96,26 +96,6 @@ alpine-edge-refclocks:
- gitlab-org
<<: *job_definition
image: debian:oldoldstable-slim
- apt-get update
- apt-get install -y netbase bison gcc libssl-dev libcap-dev pps-tools python-dev
- python ./waf configure build
- gitlab-org
<<: *job_definition
image: debian:oldoldstable-slim
- apt-get update
- apt-get install -y netbase bison gcc libssl-dev libcap-dev pps-tools python-dev
- python ./waf configure --refclock=all build
- gitlab-org
<<: *job_definition
image: debian:oldstable-slim
......@@ -578,7 +558,7 @@ gentoo-hardened-refclocks:
- ./waf configure
- ./waf configure --refclock=all
- /opt/cov-analysis/bin/cov-build --dir cov-int ./waf build
- tar czf ntpsec_coverity.tgz cov-int
- curl --form token=$COVERITY_TOKEN --form email=security@ntpsec.org --form file=@ntpsec_coverity.tgz --form version="$(git rev-parse --short HEAD)" --form description="Automatic submission by gitlab-ci" https://scan.coverity.com/builds?project=ntpsec
== Quick way to get NTS working
This is a recipe, useful during the development and
stabilization phase of NTS landing, to get your NTPsec
instance talking with other instances.
This will get dated quite fast, and is neither the best
way to setup, nor the more conformant, but should be enough
to get you up.
=== Get git head
This has been tested with NTPsec_1_1_3-424-g12e409aa4 ,
21 Mar 2019. As the NTS implementation continues, this
may no longer work. YMMV.
=== Ensure you have the right dependencies
You need a very recent version of Openssl, 1.1.1a is known
to work. You can check with the following:
`openssl version`
=== ntp.conf (you are a client)
Append the keyword `nts` to the end of your `server`
lines. Do these only for servers that speak NTS. As of
late March, the following should work:
server ntpmon.dcs1.biz nts
server pi3.rellim.com nts
server kong.rellim.com nts
server ntp1.glypnod.com nts
server ntp2.glypnod.com nts
Note that these are development machines, so uptime is
Restart ntpd, and skip to <<Verification>>, below.
=== ntp.conf (you are a server)
Being an NTS server requires a well-formed SSL cert. The
easiest way to do this is if your server has a FQDN, using
LetsEncrypt. Please see the Certbot client site
[[https://certbot.eff.org/]] for instructions.
If you already have an SSL Cert for your server, and you are
serving time using the same FQDN, you can reuse that Cert.
Add the line:
`nts enable`
to your conf file.
Locate the following two files:
* Your Cert Private Key
* Your Cert Public Key, fully chained up
Then add the lines below to your ntp.conf, replacing
with your pathnames.
Example, for my server:
nts key /etc/letsencrypt/live/ntpmon.dcs1.biz/privkey.pem
nts cert /etc/letsencrypt/live/ntpmon.dcs1.biz/fullchain.pem
Restart your server, and skip to <<Verification>>, below.
=== Verification
Check your log file. You should see lines like this:
2019-03-22T08:06:32 ntpd[12915]: NTSs: starting NTS-KE server listening on port 123
2019-03-22T08:06:32 ntpd[12915]: NTSs: loaded certificate (chain) from /etc/letsencrypt/live/ntpmon.dcs1.biz/fullchain.pem
2019-03-22T08:06:32 ntpd[12915]: NTSs: loaded private key from /etc/letsencrypt/live/ntpmon.dcs1.biz/privkey.pem
2019-03-22T08:06:32 ntpd[12915]: NTSs: Private Key OK
2019-03-22T08:06:32 ntpd[12915]: NTSs: OpenSSL security level is 2
2019-03-22T08:06:32 ntpd[12915]: NTSs: listen4 worked
2019-03-22T08:06:32 ntpd[12915]: NTSs: listen6 worked
2019-03-22T08:06:32 ntpd[12915]: NTSc: Using system default root certificates.
2019-03-22T08:06:33 ntpd[12915]: DNS: dns_probe: pi3.rellim.com, cast_flags:1, flags:21801
2019-03-22T08:06:33 ntpd[12915]: NTSc: DNS lookup of pi3.rellim.com took 0.003 sec
2019-03-22T08:06:33 ntpd[12915]: NTSc: nts_probe connecting to pi3.rellim.com:ntp =>
2019-03-22T08:06:34 ntpd[12915]: NTSc: Using TLSv1.2, AES256-GCM-SHA384 (256)
2019-03-22T08:06:34 ntpd[12915]: NTSc: certificate subject name: /CN=pi3.rellim.com
2019-03-22T08:06:34 ntpd[12915]: NTSc: certificate issuer name: /C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
2019-03-22T08:06:34 ntpd[12915]: NTSc: certificate is valid.
2019-03-22T08:06:34 ntpd[12915]: NTSc: read 880 bytes
2019-03-22T08:06:34 ntpd[12915]: NTSc: Got 8 cookies, length 104, aead=15.
2019-03-22T08:06:34 ntpd[12915]: NTSc: NTS-KE req to pi3.rellim.com took 0.882 sec, OK
This is because of the
`server pi3.rellim.com nts`
line in ntp.conf. You should see similar stanzas for each server.
The logging prefix *NTSs* is for the NTS Server component, eg
initializing your keys. The *NTSc* component is for the NTS Client
part, where you are talking to *other* NTS servers.
==== Check with ntpq
The output of ntpq will be slightly different when NTS is in use,
note the `t` column. Example:
root@ntpmon:/var/www/html/ntp# ntpq -p
remote refid st t when poll reach delay offset jitter
*SHM(1) .PPS. 0 l 20 64 377 0.0000 0.0007 0.0281
xSHM(0) .GPS. 0 l 19 64 377 0.0000 233.3966 19.2212
+pi3.rellim.com .PPS. 1 8 56 64 371 197.4484 0.0932 0.9660
+kong.rellim.com 2 8 17 64 273 210.7230 -1.3924 0.6086
-ntp1.glypnod.com 2 8 50 64 277 178.5749 3.8921 0.9611
-ntp2.glypnod.com 2 8 - 64 177 185.7582 -2.6534 0.0275
2407:8000:8001:80::8 .DNS. 16 u - 1024 0 0.0000 0.0000 0.0005
-navobs1.wustl.edu .GPS. 1 u 105 64 356 221.5282 -2.4354 0.0293
The `t` column shows how many cookies your NTS client is holding for the
appropriate servers. The number should be close to 8 (the default).
==== Check with ntp variables
Try `ntpq -c nts` . This will show various counters related
to NTS. This feature is under active development, so the
format might change. An example:
root@ntpmon:/var/www/html/ntp# ntpq -c nts
NTS client sends: 7491
NTS client recvs: 6562
NTS client recvs w error: 0
NTS server recvs: 5591
NTS server recvs w error: 38
NTS server sends: 5553
NTS make cookies: 6392
NTS decode cookies: 4734
NTS decode cookies old: 819
NTS decode cookies too old: 0
NTS decode cookies error: 0
NTS KE probes: 8
NTS KE probes_bad: 0
NTS KE serves: 75
NTS KE serves_bad: 56
=== Thanks for the handholding
Much thanks to Hal Murray and Gary Miller, for most of the
stuff above, and talking me through this.
......@@ -393,6 +393,13 @@ displayed.
packets can get flagged for inclusion in exception statistics in more
than one way, for example by having both a bad length and an old version.
Display a summary of the NTS state, including
both the the NTS client and NTS server components. Note that
the format of the output text may change as this feature is
developed. This command is experimental until further notice
and clarification.
== Authentication
......@@ -131,7 +131,7 @@ decodenetnum(
either the IP address or the port is well-formed, but at
least they're unambiguously delimited from each other.
Let getaddrinfo() perform all further validation. */
retcode = getaddrinfo(ip, port_start == NULL ? "ntp" : port_start,
retcode = getaddrinfo(ip, port_start == NULL ? "123" : port_start,
&hints, &ai);
if(retcode) {
return retcode;
* lib_strbuf - library string storage
#include <pthread.h>
#include "config.h"
#include "isc_netaddr.h"
......@@ -14,8 +16,18 @@
int debug;
static pthread_mutex_t cookie_lock = PTHREAD_MUTEX_INITIALIZER;
* Macro to get a pointer to the next buffer
* Function to get a pointer to the next buffer. Needs to be thread-safe because
* it's used in callers that need to be thread-safe, notably msyslog. For the
* same reason, we don't try to log a log-acquisition failure.
* ESR: Yes, this is ugly and kludgy. I'm not getting rid of of it
* because I have an eye forward on translation to a garbage-collected
* language, at which point something with this behavior will be
* better than all the conttorions we'd have to go through to get rid
* of it in C.
char *lib_getbuf(void)
......@@ -23,8 +35,10 @@ char *lib_getbuf(void)
static int lib_nextbuf;
char *bufp;
(bufp) = &lib_stringbuf[lib_nextbuf++][0];
lib_nextbuf %= (int)COUNTOF(lib_stringbuf);
return bufp;
......@@ -170,7 +170,7 @@ static void* dns_lookup(void* arg)
hints.ai_protocol = IPPROTO_UDP;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_family = AF(&pp->srcadr);
gai_rc = getaddrinfo(pp->hostname, "ntp", &hints, &answer);
gai_rc = getaddrinfo(pp->hostname, "123", &hints, &answer);
kill(getpid(), SIGDNS);
......@@ -2284,7 +2284,10 @@ dns_take_server(
msyslog(LOG_INFO, "DNS: Server taking: %s", socktoa(rmtadr));
if (NTP_PORT == SRCPORT(rmtadr))
msyslog(LOG_INFO, "DNS: Server taking: %s", socktoa(rmtadr));
msyslog(LOG_INFO, "DNS: Server taking: %s", sockporttoa(rmtadr));
server->cfg.flags &= (unsigned)~FLAG_LOOKUP;
server->srcadr = *rmtadr;
......@@ -2850,7 +2853,7 @@ proto_clr_stats(void)
void maybe_log_junk(struct recvbuf *rbufp) {
static unsigned int noise_try = 0;
if ((noise_try>100) && (((noise_try-90)*3600/current_time) < 10))
if ((noise_try>100) && (((noise_try-90)*3600/current_time) > 10))
"JUNK: M%d V%d 0/%2x%2x%2x%2x 48/%2x%2x%2x%2x from %s, lng=%ld",
......@@ -32,6 +32,7 @@ bool nts_set_cert_search(SSL_CTX *ctx);
bool check_certificate(struct peer* peer, SSL *ssl);
bool nts_client_send_request(struct peer* peer, SSL *ssl);
bool nts_client_process_response(struct peer* peer, SSL *ssl);
bool nts_server_lookup(char *server, sockaddr_u *addr);
static SSL_CTX *client_ctx = NULL;
static sockaddr_u sockaddr;
......@@ -236,7 +237,8 @@ int open_TCP_socket(struct peer *peer) {
memcpy(&sockaddr, answer->ai_addr, answer->ai_addrlen);
msyslog(LOG_INFO, "NTSc: nts_probe connecting to %s:%s => %s",
host, port, sockporttoa(&sockaddr));
SET_PORT(&sockaddr, NTP_PORT); /* setup default NTP address */
/* switch to NTP port in case of server-name:port */
SET_PORT(&sockaddr, NTP_PORT);
sockfd = socket(answer->ai_family, SOCK_STREAM, 0);
if (-1 == sockfd) {
msyslog(LOG_INFO, "NTSc: nts_probe: no socket: %s", strerror(errno));
......@@ -311,13 +313,14 @@ bool check_certificate(struct peer* peer, SSL *ssl) {
bool nts_make_keys(SSL *ssl, uint16_t aead, uint8_t *c2s, uint8_t *s2c, int keylen) {
// char *label = "EXPORTER-network-time-security/1";
// There is a bug in OpenSSL 1.1.1a
// Until Mar-23, we were using:
// const char *label = "EXPORTER-nts/1";
// Subject: [Ntp] [NTS4NTP] info for NTS developers
// From: Martin Langer <mart.langer@ostfalia.de>
// Date: Tue, 15 Jan 2019 11:40:13 +0100
// https://mailarchive.ietf.org/arch/msg/ntp/nkc-9n6XOPt5Glgi_ueLvuD9EfY
// bug in OpenSSL 1.1.1a
const char *label = "EXPORTER-nts/1";
const char *label = "EXPORTER-network-time-security/1";
// FIXME, first 2 bytes, next protocol ID (0)
unsigned char context[5] = {0x00, 0x00, 0x00, 0x0f, 0x00};
context[2] = (aead >> 8) & 0xFF;
......@@ -403,9 +406,11 @@ bool nts_client_process_response(struct peer* peer, SSL *ssl) {
buf.next = buff;
buf.left = transferred;
while (buf.left > 0) {
uint16_t type, data;
uint16_t type, data, port;
bool critical = false;
int length, keylength;
#define MAX_SERVER 100
char server[MAX_SERVER];
type = ke_next_record(&buf, &length);
if (NTS_CRITICAL & type) {
......@@ -464,6 +469,25 @@ bool nts_client_process_response(struct peer* peer, SSL *ssl) {
peer->nts_state.writeIdx = peer->nts_state.writeIdx % NTS_MAX_COOKIES;
case nts_server_negotiation:
if (MAX_SERVER < length) {
msyslog(LOG_ERR, "NTSc: server string too long %d.", length);
return false;
next_bytes(&buf, (uint8_t *)server, length);
/* save port in case port specified before server */
port = SRCPORT(&sockaddr);
if (!nts_server_lookup(server, &sockaddr))
return false;
SET_PORT(&sockaddr, port);
msyslog(LOG_ERR, "NTSc: Using server %s=>%s", server, socktoa(&sockaddr));
case nts_port_negotiation:
// FIXME check length
port = next_uint16(&buf);
SET_PORT(&sockaddr, port);
msyslog(LOG_ERR, "NTSc: Using port %d", port);
case nts_end_of_message:
if ((0 != length) || !critical) {
msyslog(LOG_ERR, "NTSc: EOM-Wrong length or not Critical: %d, %d",
......@@ -526,4 +550,33 @@ bool nts_set_cert_search(SSL_CTX *ctx) {
return false;
bool nts_server_lookup(char *server, sockaddr_u *addr) {
struct addrinfo hints;
struct addrinfo *answer;
int gai_rc;
hints.ai_protocol = IPPROTO_UDP;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_family = AF_UNSPEC;
gai_rc = getaddrinfo(server, "123", &hints, &answer);
if (0 != gai_rc) {
msyslog(LOG_INFO, "NTSc: nts_probe: DNS error trying to lookup %s: %d, %s",
server, gai_rc, gai_strerror(gai_rc));
return false;
if (NULL == answer)
return false;
if (sizeof(sockaddr_u) >= answer->ai_addrlen)
memcpy(addr, answer->ai_addr, answer->ai_addrlen);
return true;
/* end */