[Git][NTPsec/ntpsec][master] 3 commits: Add attic/tls-tester.c
Hal Murray (@hal.murray)
gitlab at mg.gitlab.com
Wed Jul 30 10:23:06 UTC 2025
Hal Murray pushed to branch master at NTPsec / ntpsec
Commits:
5e1b333d by Hal Murray at 2025-07-29T12:03:43-07:00
Add attic/tls-tester.c
It's the start of a hack for testing NTS-KE servers.
Currently, it can generate timeouts and closed connections
so I can test the error logging.
- - - - -
6f4af3da by Hal Murray at 2025-07-29T12:05:47-07:00
Major work on NTS-KE logging.
The error reporting from OpenSSL is complicated.
I'm trying to make it so the server side logs one
message for each connection (or attempt).
There is still work to do on catching individual cases
and translating them to English strings, but I think the
structure now supports that.
- - - - -
82e0623c by Hal Murray at 2025-07-30T01:16:57-07:00
NTS-KE recv now merges fragments, Fix #858
attic/tls-tester has been updated to generate test cases.
- - - - -
7 changed files:
- + attic/tls-tester.c
- attic/wscript
- include/nts.h
- include/nts2.h
- ntpd/nts.c
- ntpd/nts_client.c
- ntpd/nts_server.c
Changes:
=====================================
attic/tls-tester.c
=====================================
@@ -0,0 +1,298 @@
+/* Hack to generate various NTS-KE server SSL errors.
+ * Build with:
+ * cc -g -Wall -lcrypto -lssl -o tls-tester tls-tester.c
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h> /* Needed on FreeBSD */
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+typedef union { /* On Linux, these come from: */
+ struct sockaddr sa; /* /usr/include/bits/socket.h */
+ struct sockaddr_in sa4; /* /usr/include/linux/in.h */
+ struct sockaddr_in6 sa6; /* /usr/include/linux/in6.h */
+} sockaddr_u;
+
+const char *host = "localhost";
+const char *port = "4460";
+
+bool early_disconnect = false; // before TLS
+bool early_timeout = false;
+bool TLS_disconnect = false; // after TLS
+bool TLS_timeout = false;
+bool send_chunks = false;
+
+/* working finger into a buffer - updated by append/unpack routines */
+struct BufCtl_t {
+ uint8_t *next; /* pointer to next data/space */
+ int left; /* data left or space available */
+};
+typedef struct BufCtl_t BufCtl;
+
+void append_record16(BufCtl* buf, uint16_t type, uint16_t data);
+void append_record(BufCtl* buf, uint16_t type);
+
+
+static void do_args (int argc, char *argv[]) {
+ if (1 < argc) {
+ if (false) {
+ NULL;
+ } else if (0 == strcmp(argv[1], "-early-disconnect")) {
+ early_disconnect = true;
+ argc--;
+ } else if (0 == strcmp(argv[1], "-early-timeout")) {
+ early_timeout = true;
+ argc--;
+ } else if (0 == strcmp(argv[1], "-TLS-disconnect")) {
+ TLS_disconnect = true;
+ argc--;
+ } else if (0 == strcmp(argv[1], "-TLS-timeout")) {
+ TLS_timeout = true;
+ argc--;
+ } else if (0 == strcmp(argv[1], "-send-chunks")) {
+ send_chunks = true;
+ argc--;
+ }
+ }
+
+ if (1 < argc) {
+ host = argv[1];
+ }
+ if (2 < argc) {
+ port = argv[2];
+ }
+}
+
+static int do_connect (void) {
+ int err;
+ struct addrinfo hints;
+ int gai_rc;
+ struct addrinfo *answer, *worker;
+ sockaddr_u address;
+ int address_len = 0;
+ int server;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_UNSPEC;
+ gai_rc = getaddrinfo(host, port, &hints, &answer);
+ if (0 != gai_rc) {
+ printf("### DNS error: %s:%s => %s\n", host, port, gai_strerror(gai_rc));
+ exit(1);
+ }
+
+ for (worker=answer; NULL!=worker; worker=worker->ai_next) {
+ char buff[100];
+ const char *text;
+ if (sizeof(address) < worker->ai_addrlen) {
+ printf("!!! Oops. Address size: %u, %lu\n",
+ worker->ai_addrlen, (unsigned long)sizeof(address));
+ exit(1);
+ }
+ address_len = worker->ai_addrlen;
+ memcpy(&address, worker->ai_addr, worker->ai_addrlen);
+ switch (worker->ai_family) {
+ case AF_INET:
+ text = inet_ntop(AF_INET,
+ &((struct sockaddr_in *)worker->ai_addr)->sin_addr,
+ buff, sizeof(buff));
+ break;
+ case AF_INET6:
+ text = inet_ntop(AF_INET6,
+ &((struct sockaddr_in6 *)worker->ai_addr)->sin6_addr,
+ buff, sizeof(buff));
+ break;
+ default:
+ printf("!!! Oops. Strange network family %d\n", worker->ai_family);
+ exit(1);
+ }
+ // check for NULL??
+ printf("# Found %s\n", text);
+ }
+
+ server = socket(address.sa4.sin_family, SOCK_STREAM, 0);
+ if (-1 == server) {
+ printf("!!! Oops. Can't make socket: %s\n", strerror(errno));
+ exit(1);
+ }
+ err = connect(server, &address.sa, address_len);
+ if (-1 == err) {
+ printf("!!! Oops. Can't connect: %s\n", strerror(errno));
+ exit(1);
+ }
+ freeaddrinfo(answer);
+ return server;
+}
+
+static void print_buf(uint8_t *buff, int bytes) {
+ while (0 < bytes) {
+ uint16_t *buff16 = (uint16_t *)buff;
+ uint16_t type, length;
+ type = ntohs(*buff16++); buff+= 2;
+ length = ntohs(*buff16++); buff+= 2;
+ bytes -=4;
+ buff += length;
+ bytes -= length;
+ if (2==length) {
+ uint16_t data = ntohs(*buff16++);
+ printf(" type=0x%04x, lng=%d, data=0x%0x\n", type, length, data);
+ continue;
+ }
+ printf(" type=0x%04x, lng=%d\n", type, length);
+ }
+}
+
+/* send request in chunks to test collector */
+static int chunkify(SSL *ssl, uint8_t *buff, int bytes) {
+ int total = 0;
+ while (0<bytes) {
+ int to_send, sent;
+ to_send = random() % (bytes+1);
+ if (0==total && to_send==bytes) {
+ /* avoid all in one chunk */
+ continue;
+ }
+ if (to_send<3) {
+ to_send=bytes;
+ }
+ sent = SSL_write(ssl, buff, to_send);
+ printf("Sent %d=>%d bytes\n", to_send, sent);
+ if (0>=sent) return sent;
+ buff += sent;
+ bytes -= sent;
+ total += sent;
+ }
+ return total;
+}
+
+
+int main (int argc, char *argv[]) {
+ int bytes, wrote, read;
+ int server;
+ uint8_t buff[1000];
+ struct BufCtl_t buf;
+ SSL_CTX *ctx;
+ SSL *ssl;
+
+ srandom(time(NULL));
+
+ /* I'm testing newer versions of OpenSSL.
+ * Fedora's config file needs something not in the default OpenSSL build.
+ * This avoids crashing.
+ */
+ OPENSSL_init_crypto(OPENSSL_INIT_NO_LOAD_CONFIG, NULL);
+
+ do_args(argc, argv);
+
+ printf("### testing %s:%s\n", host, port);
+
+ server = do_connect();
+
+ printf("### No SSL yet.\n");
+ if (early_disconnect) {
+ printf("### Closing...\n");
+ close(server);
+ exit(0);
+ // SSL_accept => code 1, err 0A000126 1=>SSL_ERROR_SSL
+ }
+ if (early_timeout) {
+ printf("### Sleeping...\n");
+ sleep(5);
+ exit(0);
+ // SSL_accept => code 2, err 0 2=>SSL_ERROR_WANT_READ
+ }
+
+ errno = 0;
+ ctx = SSL_CTX_new(TLS_client_method());
+ if (NULL == ctx) {
+ printf("!!! Oops. SSL_CTX_new failed.: %s\n", strerror(errno));
+ ERR_print_errors_fp(stderr);
+ exit(1);
+ }
+ ssl = SSL_new(ctx);
+ if (NULL == ssl) {
+ printf("!!! Oops. SSL_new failed.\n");
+ exit(1);
+ }
+ SSL_set_fd(ssl, server);
+
+ /* We aren't checking certificates */
+ if (1 != SSL_connect(ssl)) {
+ printf("!!! Oops. SSL_connect failed.\n");
+ exit(1);
+ }
+
+ printf("### SSL connected.\n");
+ if (TLS_disconnect) {
+ printf("### Closing...\n");
+ close(server);
+ exit(0);
+ // SSL_accept => code 1, err 0A000126 1=>SSL_ERROR_SSL
+ }
+ if (TLS_timeout) {
+ printf("### Sleeping...\n");
+ sleep(5);
+ exit(0);
+ // SSL_accept => code 2, err 0 2=>SSL_ERROR_WANT_READ
+ }
+
+ buf.next = buff;
+ buf.left = sizeof(buff);
+ append_record16(&buf, 0x8001, 0); // next protocol
+ append_record16(&buf, 4, 15); // algorithm_negotiation
+ append_record(&buf, 0x8000); // end_of_message
+ bytes = sizeof(buff)-buf.left;
+ printf("Sending %d bytes\n", bytes);
+ print_buf(buff, bytes);
+ if (send_chunks) {
+ wrote = chunkify(ssl, buff, bytes);
+ } else {
+ wrote = SSL_write(ssl, buff, bytes);
+ }
+ if (wrote != bytes) {
+ printf("!!! error from SSL_write: %d=>%d, %s\n",
+ bytes, wrote, strerror(errno));
+ exit(1);
+ }
+ read = SSL_read(ssl, buff, sizeof(buff));
+ if (-1 == read) {
+ printf("!!! error from SSL_read: %d, %s\n", read, strerror(errno));
+ exit(1);
+ }
+ printf("Received %d bytes\n", read);
+ print_buf(buff, read);
+ return 0;
+}
+
+/* no error checking, length excludes header */
+void append_record16(BufCtl* buf, uint16_t type, uint16_t data) {
+ uint16_t * ptr = (uint16_t *)buf->next;
+ *ptr++ = htons(type);
+ *ptr++ = htons(2);
+ *ptr++ = htons(data);
+ buf->next += 6;
+ buf->left -= 6;
+}
+
+void append_record(BufCtl* buf, uint16_t type) {
+ uint16_t * ptr = (uint16_t *)buf->next;
+ *ptr++ = htons(type);
+ *ptr++ = htons(0);
+ buf->next += 4;
+ buf->left -= 4;
+}
+
=====================================
attic/wscript
=====================================
@@ -13,6 +13,7 @@ def build(ctx):
if not ctx.env.DISABLE_NTS:
util.append('aes-siv-timing')
util.append('aead-timing')
+ util.append('tls-tester')
for name in util:
ctx(
@@ -20,7 +21,7 @@ def build(ctx):
features="c cprogram",
includes=[ctx.bldnode.parent.abspath(), "../include", "../libaes_siv"],
source=[name + ".c"],
- use="ntp M CRYPTO RT PTHREAD aes_siv",
+ use="ntp M CRYPTO SSL RT PTHREAD aes_siv",
install_path=None,
)
=====================================
include/nts.h
=====================================
@@ -49,9 +49,8 @@ struct BufCtl_t {
};
typedef struct BufCtl_t BufCtl;
+/* Here for test routines */
bool nts_ke_process_receive(struct BufCtl_t *buf, int *aead);
-bool nts_ke_setup_send(struct BufCtl_t *buf, int aead,
- uint8_t *c2s, uint8_t *s2c, int keylen);
/***********************************************************/
=====================================
include/nts2.h
=====================================
@@ -24,8 +24,8 @@ bool nts_load_ecdhcurves(SSL_CTX *ctx);
bool nts_set_cipher_order(SSL_CTX *ctx);
bool nts_load_versions(SSL_CTX *ctx);
-int nts_ssl_read(SSL *ssl, uint8_t *buff, int buff_length);
-int nts_ssl_write(SSL *ssl, uint8_t *buff, int buff_length);
+int nts_ssl_read(SSL *ssl, uint8_t *buff, int buff_length, const char **errtxt);
+int nts_ssl_write(SSL *ssl, uint8_t *buff, int buff_length, const char **errtxt);
void nts_log_ssl_error(void);
=====================================
ntpd/nts.c
=====================================
@@ -289,31 +289,96 @@ bool nts_load_certificate(SSL_CTX *ctx) {
return true;
}
+/* scan partial(?) buffer for end marker */
+static bool find_end_marker(uint8_t *buff, int bytes) {
+ while (NTS_KE_HDR_LNG<=bytes) {
+ uint16_t *buff16 = (uint16_t *)buff;
+ uint16_t type = ntohs(*buff16++) & ~NTS_CRITICAL;
+ uint16_t length = ntohs(*buff16++);
+ if (nts_end_of_message == type) return true;
+ buff += NTS_KE_HDR_LNG + length;
+ bytes -= NTS_KE_HDR_LNG + length;
+ }
+ return false;
+}
-int nts_ssl_read(SSL *ssl, uint8_t *buff, int buff_length) {
- int bytes_read;
- char errbuf[100];
- bytes_read = SSL_read(ssl, buff, buff_length);
- if (0 >= bytes_read) {
- ntp_strerror_r(errno, errbuf, sizeof(errbuf));
- msyslog(LOG_INFO, "NTS: SSL_read error: %s", errbuf);
- nts_log_ssl_error();
- return -1;
- }
- return bytes_read;
+
+/* Ugh. Some sites send in chunks so we have to keep reading
+ * until we find an end marker. (or fill up the buffer, or error)
+ */
+int nts_ssl_read(SSL *ssl, uint8_t *buff, int buff_length, const char** errtxt) {
+ int bytes_ready = 0;
+ uint8_t *finger = buff;
+ int bytes_left = buff_length;
+ while (0 < bytes_left) {
+ int bytes_read = SSL_read(ssl, finger, bytes_left);
+ if (0 >= bytes_read) {
+ int code = SSL_get_error(ssl, bytes_read);
+ unsigned long err = ERR_peek_error();
+ if (SSL_ERROR_ZERO_RETURN == code) {
+ *errtxt = "SSL_read Connection reset1";
+ return 0;
+ }
+ if (SSL_ERROR_WANT_READ == code) {
+ *errtxt = "SSL_read Timeout";
+ return 0;
+ }
+#ifdef SSL_R_UNEXPECTED_EOF_WHILE_READING
+ if (SSL_ERROR_SSL == code &&
+ SSL_R_UNEXPECTED_EOF_WHILE_READING == ERR_GET_REASON(err)) {
+ *errtxt = "SSL_read Connection reset2";
+ return 0;
+ }
+#else
+ // Hack for OpenSSL 1.1.1w 11 Sep 2023
+ // Debian GNU/Linux 11 (bullseye)
+ if (SSL_ERROR_SYSCALL == code) {
+ *errtxt = "SSL_read Timeout";
+ return 0;
+ }
+#endif
+ // Strange error condition
+ msyslog(LOG_INFO, "NTS: SSL_read strange error: %d bytes, code %d, err %lx",
+ bytes_read, code, err);
+ *errtxt = ERR_reason_error_string(err);
+ if (NULL == *errtxt) {
+ *errtxt = "SSL_read unknown error text";
+ }
+ nts_log_ssl_error();
+ return -1;
+ }
+ finger += bytes_read;
+ bytes_left -= bytes_read;
+ bytes_ready += bytes_read;
+ if (find_end_marker(buff, bytes_ready)) break;
+ }
+ return bytes_ready;
}
-int nts_ssl_write(SSL *ssl, uint8_t *buff, int buff_length) {
- int bytes_written;
- char errbuf[100];
- bytes_written = SSL_write(ssl, buff, buff_length);
- if (0 >= bytes_written) {
- ntp_strerror_r(errno, errbuf, sizeof(errbuf));
- msyslog(LOG_INFO, "NTS: SSL_write error: %s", errbuf);
- nts_log_ssl_error();
- return -1;
- }
- return bytes_written;
+int nts_ssl_write(SSL *ssl, uint8_t *buff, int buff_length, const char** errtxt) {
+ int bytes_written = SSL_write(ssl, buff, buff_length);
+ if (0 >= bytes_written) {
+ int code = SSL_get_error(ssl, bytes_written);
+ unsigned long err = ERR_peek_error();
+ if (SSL_ERROR_ZERO_RETURN == code) {
+ *errtxt = "SSL_Write Connection closed";
+ return 0;
+ }
+ if (SSL_ERROR_WANT_WRITE == code) {
+ *errtxt = "SSL_write Timeout";
+ return 0;
+ }
+ // Strange error condition
+ msyslog(LOG_INFO, "NTS: SSL_write strange error: %d bytes, code %d, err %lx",
+ bytes_written, code, err);
+ *errtxt = ERR_reason_error_string(err);
+ if (NULL == *errtxt) {
+ *errtxt = "SSL_write unknown error text";
+ }
+ nts_log_ssl_error();
+ return -1;
+ }
+ return bytes_written;
}
/* Each thread has it's own queue of errors */
@@ -438,5 +503,4 @@ uint16_t next_bytes(BufCtl* buf, uint8_t *data, int length) {
return length;
}
-
/* end */
=====================================
ntpd/nts_client.c
=====================================
@@ -88,12 +88,16 @@ bool nts_probe(struct peer * peer) {
inet_ntop(af, PSOCK_ADDR4(&peer->srcadr), hostbuf, sizeof(hostbuf));
break;
case AF_INET6:
- inet_ntop(af, PSOCK_ADDR6(&peer->srcadr), hostbuf, sizeof(hostbuf));
+ /* Add [] in case [xxx]:port */
+ hostbuf[0] = '[';
+ inet_ntop(af, PSOCK_ADDR6(&peer->srcadr), hostbuf+1, sizeof(hostbuf)-1);
+ strlcat(hostbuf, "]", sizeof(hostbuf));
break;
default:
return false;
}
hostname = hostbuf;
+// msyslog(LOG_INFO, "NTSc: Address Literal: %s", hostbuf);
}
server = open_TCP_socket(peer, hostname);
@@ -105,7 +109,15 @@ bool nts_probe(struct peer * peer) {
err = setsockopt(server, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
if (0 > err) {
ntp_strerror_r(errno, errbuf, sizeof(errbuf));
- msyslog(LOG_ERR, "NTSc: can't setsockopt: %s", errbuf);
+ msyslog(LOG_ERR, "NTSc: can't set recv timeout: %s", errbuf);
+ close(server);
+ ntske_cnt.probes_bad++;
+ return false;
+ }
+ err = setsockopt(server, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
+ if (0 > err) {
+ ntp_strerror_r(errno, errbuf, sizeof(errbuf));
+ msyslog(LOG_ERR, "NTSc: can't set send timeout: %s", errbuf);
close(server);
ntske_cnt.probes_bad++;
return false;
@@ -237,6 +249,22 @@ SSL_CTX* make_ssl_client_ctx(const char * filename) {
return ctx;
}
+/* FIXME - split out DNS work. */
+
+/* Note that there are 2 DNS lookups.
+ * One for the NTS-KE server and another for the NTP server.
+ * The latter is optional.
+ * The default is to use the same IP Address as NTS-KE server.
+ *
+ * In the non-NTS case, when a server name returns multiple addresses,
+ * we skip the ones that are already in use.
+ *
+ * If the NTS-KE server has multiple addresses, we also skip the ones
+ * that are already in use for NTP. That works great if the NTS-KE server
+ * is running on the same system as the NTP server which is true
+ * for most servers.
+ */
+
/* return -1 on error */
int open_TCP_socket(struct peer *peer, const char *hostname) {
char host[256], port[32];
@@ -248,16 +276,31 @@ int open_TCP_socket(struct peer *peer, const char *hostname) {
int sockfd;
struct timespec start, finish;
- /* copy avoids dancing around const warnings */
+ /* FIXME -- const bug in OpenSSL */
strlcpy(host, hostname, sizeof(host));
/* handle xxx:port case */
- tmp = strchr(host, ']');
- if (NULL == tmp) {
- tmp = strchr(host, ':');
+ if ('[' == host[0]) {
+ /* IPv6 case, drop [], start search after ] */
+ SET_AF(&peer->srcadr, AF_INET6);
+ strlcpy(host, hostname+1, sizeof(host));
+ tmp = strchr(host, ']');
+ if (NULL == tmp) {
+ msyslog(LOG_ERR, "NTSc: open_TCP_socket: missing ']': %s",
+ hostname);
+ return -1;
+ }
+ *tmp++ = 0;
+ /* We have chopped off the [] around the host literal.
+ * There should be nothing left or :<port> */
+ if ((0 != *tmp) && (':' != *tmp)) {
+ msyslog(LOG_ERR, "NTSc: open_TCP_socket: missing ':': %s",
+ hostname);
+ return -1;
+ }
+ if (0 == *tmp) tmp = NULL; /* no : */
} else {
- /* IPv6 case, start search after ] */
- tmp = strchr(tmp, ':');
+ tmp = strchr(host, ':');
}
if (NULL == tmp) {
/* simple case, no : */
@@ -266,6 +309,7 @@ int open_TCP_socket(struct peer *peer, const char *hostname) {
/* Complicated case, found a : */
*tmp++ = 0;
strlcpy(port, tmp, sizeof(port));
+ msyslog(LOG_INFO, "NTSc: open_TCP_socket: found port %s", port);
}
ZERO(hints);
@@ -275,23 +319,31 @@ int open_TCP_socket(struct peer *peer, const char *hostname) {
clock_gettime(CLOCK_MONOTONIC, &start);
gai_rc = getaddrinfo(host, port, &hints, &answer);
if (0 != gai_rc) {
- msyslog(LOG_INFO, "NTSc: open_TCP_socket: DNS error trying to contact %s: %d, %s",
+ msyslog(LOG_INFO, "NTSc: open_TCP_socket: DNS error trying to contact %s, %d, %s",
hostname, gai_rc, gai_strerror(gai_rc));
return -1;
}
clock_gettime(CLOCK_MONOTONIC, &finish);
finish = sub_tspec(finish, start);
- msyslog(LOG_INFO, "NTSc: DNS lookup of %s took %.3f sec",
- hostname, tspec_to_d(finish));
-
- /* Use first answer
- * sockaddr is global for NTP address
- * also use as temp for printing here
- */
- worker = find_best_addr(answer);
+ msyslog(LOG_INFO, "NTSc: DNS lookup of %s (%d) took %.3f sec",
+ hostname, hints.ai_family, tspec_to_d(finish));
+
+ /* sockaddr is global for NTP address
+ * also use as temp for printing here */
+ if (NULL == peer->hostname) {
+ /* Address literal case, use first/only answer */
+ worker = answer;
+ } else {
+ worker = find_best_addr(answer);
+ if (NULL == worker) {
+ msyslog(LOG_INFO, "NTSc: All addresses in use.");
+ freeaddrinfo(answer);
+ return -1;
+ }
+ }
memcpy(&sockaddr, worker->ai_addr, worker->ai_addrlen);
sockporttoa_r(&sockaddr, errbuf, sizeof(errbuf));
- msyslog(LOG_INFO, "NTSc: connecting to %s:%s => %s",
+ msyslog(LOG_INFO, "NTSc: connecting to %s+%s => %s",
host, port, errbuf);
/* setup default NTP port now
@@ -303,7 +355,6 @@ int open_TCP_socket(struct peer *peer, const char *hostname) {
ntp_strerror_r(errno, errbuf, sizeof(errbuf));
msyslog(LOG_INFO, "NTSc: open_TCP_socket: no socket: %s", errbuf);
} else {
- /* Use first IP Address */
if (!connect_TCP_socket(sockfd, worker)) {
close(sockfd);
sockfd = -1;
@@ -316,7 +367,26 @@ int open_TCP_socket(struct peer *peer, const char *hostname) {
}
struct addrinfo *find_best_addr(struct addrinfo *answer) {
- /* default to first one */
+ for ( ; NULL != answer; answer = answer->ai_next) {
+ sockaddr_u addr;
+ struct peer *pp;
+ if (sizeof(sockaddr_u) < answer->ai_addrlen)
+ continue; /* Weird */
+ memcpy(&addr, answer->ai_addr, answer->ai_addrlen);
+ /* findexistingpeer checks port too */
+ for (pp = peer_list; NULL != pp; pp = pp->p_link) {
+ if (MDF_POOL & pp->cast_flags) continue;
+ if (FLAG_LOOKUP & pp->cfg.flags) continue;
+ if (SOCK_EQ(&addr, &pp->srcadr)) break;
+ }
+ if (NULL != pp) {
+ char errbuf[200];
+ socktoa_r(&addr, errbuf, sizeof(errbuf));
+ msyslog(LOG_INFO, "NTSc: Skipping %s", errbuf);
+ continue; /* already in use */
+ }
+ break;
+ }
return(answer);
}
@@ -412,6 +482,10 @@ void set_hostname(SSL *ssl, const char *hostname) {
}
+
+// X509v3 Subject Alternative Name:
+// DNS:*.time.nl, DNS:time.nl
+
bool check_certificate(SSL *ssl, struct peer* peer) {
X509 *cert = SSL_get_peer_certificate(ssl);
X509_NAME *certname;
@@ -516,6 +590,7 @@ bool check_alpn(SSL *ssl, struct peer* peer, const char *hostname) {
return true;
}
+
bool nts_make_keys(SSL *ssl, uint16_t aead, uint8_t *c2s, uint8_t *s2c, int keylen) {
const char *label = "EXPORTER-network-time-security";
unsigned char context[5];
@@ -546,13 +621,14 @@ bool nts_client_send_request(SSL *ssl, struct peer* peer) {
uint8_t buff[1000];
int used, transferred;
bool success;
+ const char *errtxt = NULL;
success = nts_client_send_request_core(buff, sizeof(buff), &used, peer);
if (!success) {
return false;
}
- transferred = nts_ssl_write(ssl, buff, used);
+ transferred = nts_ssl_write(ssl, buff, used, &errtxt);
if (used != transferred)
return false;
@@ -596,9 +672,10 @@ bool nts_client_send_request_core(uint8_t *buff, int buf_size, int *used, struct
bool nts_client_process_response(SSL *ssl, struct peer* peer) {
uint8_t buff[2048]; /* RFC 4. says SHOULD be 65K */
int transferred;
+ const char *errtxt = NULL;
- transferred = nts_ssl_read(ssl, buff, sizeof(buff));
- if (0 > transferred)
+ transferred = nts_ssl_read(ssl, buff, sizeof(buff), &errtxt);
+ if (0 >= transferred)
return false;
msyslog(LOG_ERR, "NTSc: read %d bytes", transferred);
@@ -626,8 +703,9 @@ bool nts_client_process_response_core(uint8_t *buff, int transferred, struct pee
char server[MAX_SERVER];
type = ke_next_record(&buf, &length);
- if(buf.left < length){
- msyslog(LOG_ERR, "NTSc: length cannot be more than buf.left: %d", length);
+ if (length > buf.left){
+ msyslog(LOG_ERR, "NTSc: Chunk too big: 0x%x, %d, %d",
+ type, buf.left, length);
return false;
}
if (NTS_CRITICAL & type) {
@@ -739,6 +817,7 @@ bool nts_client_process_response_core(uint8_t *buff, int transferred, struct pee
} /* case */
} /* while */
+// FIXME: Need to check for EOM -- read more??
if (buf.left > 0)
return false;
=====================================
ntpd/nts_server.c
=====================================
@@ -35,8 +35,8 @@
static bool create_listener4(int port);
static bool create_listener6(int port);
static void* nts_ke_listener(void*);
-static bool nts_ke_request(SSL *ssl);
-static void nts_ke_accept_fail(char* addrbuf, double sec);
+static void nts_ke_request(SSL *ssl, const char **errtxt);
+static void nts_ke_accept_fail(char* hostname, double sec, int code);
static void nts_lock_certlock(void);
static void nts_unlock_certlock(void);
@@ -46,6 +46,9 @@ static SSL_CTX *server_ctx = NULL;
static int listener4_sock = -1;
static int listener6_sock = -1;
+static void nts_ke_setup_send(struct BufCtl_t *buf, int aead,
+ uint8_t *c2s, uint8_t *s2c, int keylen);
+
/* We need a lock to protect reloading our certificate.
* This seems like overkill, but it doesn't happen often. */
pthread_mutex_t certificate_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -201,8 +204,7 @@ void* nts_ke_listener(void* arg) {
char usingbuf[100];
struct timespec start, finish; /* wall clock */
l_fp wall;
- bool worked;
- const char *good;
+ const char *errtxt; /* not NULL if error */
#ifdef RUSAGE_THREAD
struct timespec start_u, finish_u; /* CPU user */
struct timespec start_s, finish_s; /* CPU system */
@@ -230,13 +232,14 @@ void* nts_ke_listener(void* arg) {
SSL *ssl;
int client, err;
+ sleep(1); /* FIXME: log clutter/DoS */
+ errtxt = NULL;
client = accept(sock, &addr.sa, &len);
if (client < 0) {
ntp_strerror_r(errno, errbuf, sizeof(errbuf));
msyslog(LOG_ERR, "NTSs: TCP accept failed: %s", errbuf);
if (EBADF == errno)
return NULL;
- sleep(1); /* avoid log clutter on bug */
continue;
}
clock_gettime(CLOCK_MONOTONIC, &start);
@@ -263,7 +266,16 @@ void* nts_ke_listener(void* arg) {
&timeout, sizeof(timeout));
if (0 > err) {
ntp_strerror_r(errno, errbuf, sizeof(errbuf));
- msyslog(LOG_ERR, "NTSs: can't setsockopt: %s", errbuf);
+ msyslog(LOG_ERR, "NTSs: can't set recv timeout: %s", errbuf);
+ close(client);
+ ntske_cnt.serves_bad++;
+ continue;
+ }
+ err = setsockopt(client, SOL_SOCKET, SO_SNDTIMEO,
+ &timeout, sizeof(timeout));
+ if (0 > err) {
+ ntp_strerror_r(errno, errbuf, sizeof(errbuf));
+ msyslog(LOG_ERR, "NTSs: can't set send timeout: %s", errbuf);
close(client);
ntske_cnt.serves_bad++;
continue;
@@ -275,10 +287,12 @@ void* nts_ke_listener(void* arg) {
nts_unlock_certlock();
SSL_set_fd(ssl, client);
- if (SSL_accept(ssl) <= 0) {
+ err = SSL_accept(ssl);
+ if (0 >= err) {
+ int code = SSL_get_error(ssl, err);
clock_gettime(CLOCK_MONOTONIC, &finish);
wall = tspec_intv_to_lfp(sub_tspec(finish, start));
- nts_ke_accept_fail(addrbuf, lfptox(wall));
+ nts_ke_accept_fail(addrbuf, lfptox(wall), code);
SSL_free(ssl);
close(client);
ntske_cnt.serves_nossl++;
@@ -303,13 +317,7 @@ void* nts_ke_listener(void* arg) {
SSL_get_cipher_name(ssl),
SSL_get_cipher_bits(ssl, NULL));
- if (nts_ke_request(ssl)) {
- worked = true;
- good = "OK";
- } else {
- worked = false;
- good = "Failed";
- }
+ nts_ke_request(ssl, &errtxt);
SSL_shutdown(ssl);
SSL_free(ssl);
@@ -317,7 +325,7 @@ void* nts_ke_listener(void* arg) {
clock_gettime(CLOCK_MONOTONIC, &finish);
wall = tspec_intv_to_lfp(sub_tspec(finish, start));
- if (worked) {
+ if (NULL == errtxt) {
ntske_cnt.serves_good++;
ntske_cnt.serves_good_wall += wall;
} else {
@@ -332,19 +340,32 @@ void* nts_ke_listener(void* arg) {
sys = tspec_intv_to_lfp(sub_tspec(finish_s, start_s));
start_u = finish_u;
start_s = finish_s;
- if (worked) {
+ if (NULL == errtxt) {
ntske_cnt.serves_good_cpu += usr;
ntske_cnt.serves_good_cpu += sys;
+ msyslog(LOG_INFO,
+ "NTSs: NTS-KE from %s, OK, Using %s, took %.3f sec, CPU: %.3f+%.3f ms",
+ addrbuf, usingbuf, lfptox(wall),
+ lfptox(usr*1000), lfptox(sys*1000));
} else {
ntske_cnt.serves_bad_cpu += usr;
ntske_cnt.serves_bad_cpu += sys;
+ msyslog(LOG_INFO,
+ "NTSs: NTS-KE from %s, Failed, Using %s, took %.3f sec, CPU: %.3f+%.3f ms, %s",
+ addrbuf, usingbuf, lfptox(wall),
+ lfptox(usr*1000), lfptox(sys*1000),
+ errtxt);
}
- msyslog(LOG_INFO, "NTSs: NTS-KE from %s, %s, Using %s, took %.3f sec, CPU: %.3f+%.3f ms",
- addrbuf, good, usingbuf, lfptox(wall),
- lfptox(usr*1000), lfptox(sys*1000));
#else
- msyslog(LOG_INFO, "NTSs: NTS-KE from %s, %s, Using %s, took %.3f sec",
- addrbuf, good, usingbuf, lfptox(wall));
+ if (NULL == errtxt) {
+ msyslog(LOG_INFO,
+ "NTSs: NTS-KE from %s, OK, Using %s, took %.3f sec",
+ addrbuf, usingbuf, lfptox(wall));
+ } else {
+ msyslog(LOG_INFO,
+ "NTSs: NTS-KE from %s, Failed, Using %s, took %.3f sec, %s",
+ addrbuf, usingbuf, lfptox(wall), errtxt);
+ }
#endif
}
return NULL;
@@ -352,31 +373,78 @@ void* nts_ke_listener(void* arg) {
/* Analyze failure from SSL_accept
* print single error message for common cases.
+ * Similar code in nts.c, nts_ssl_read() and nts_ssl_write()
*/
-void nts_ke_accept_fail(char* addrbuf, double sec) {
+void nts_ke_accept_fail(char* hostname, double sec, int code) {
unsigned long err = ERR_peek_error();
- int lib = ERR_GET_LIB(err);
- int reason = ERR_GET_REASON(err);
+ char errbuf[100];
+ char buff[200];
const char *msg = NULL;
- if (ERR_LIB_SSL == lib && SSL_R_WRONG_VERSION_NUMBER == reason)
- msg = "wrong version number";
- if (ERR_LIB_SSL == lib && SSL_R_HTTP_REQUEST == reason)
- msg = "http request";
- if (ERR_LIB_SSL == lib && SSL_R_NO_SHARED_CIPHER == reason)
- msg = "no shared cipher";
- if (ERR_LIB_SSL == lib && SSL_R_UNSUPPORTED_PROTOCOL == reason)
- msg = "unsupported protocol (TLSv1.2?)";
- if (NULL == msg) {
- msyslog(LOG_INFO, "NTSs: SSL accept from %s failed, took %.3f sec",
- addrbuf, sec);
- nts_log_ssl_error();
- return;
+ if (0 == err) {
+ switch (code) {
+ case SSL_ERROR_WANT_READ:
+ msg = "Timeout";
+ break;
+ case SSL_ERROR_SYSCALL:
+ if (ECONNRESET==errno) {
+ msg = "Connection reset1";
+ break;
+ }
+ /* fall through */
+ default:
+ ntp_strerror_r(errno, errbuf, sizeof(errbuf));
+ snprintf(buff, sizeof(buff), "code %d, errno=>%d, %s",
+ code, errno, errbuf);
+ msg = buff;
+ break;
+ }
+ } else {
+ switch (code) {
+ case SSL_ERROR_SSL:
+ switch (ERR_GET_REASON(err)) {
+ case SSL_R_HTTP_REQUEST:
+ msg = "HTTP request";
+ break;
+ case SSL_R_NO_SHARED_CIPHER:
+ msg = "no shared cipher";
+ break;
+ case SSL_R_WRONG_VERSION_NUMBER:
+ msg = "wrong version number";
+ break;
+ case SSL_R_UNSUPPORTED_PROTOCOL:
+ msg = "unsupported protocol";
+ break;
+#ifdef SSL_R_UNEXPECTED_EOF_WHILE_READING
+/* Not available in OpenSSL 1.1.1w as used by Debian 11 (bullseye), Jul 2025 */
+ case SSL_R_UNEXPECTED_EOF_WHILE_READING:
+ msg = "Connection reset2";
+ break;
+#endif
+ default:
+ // cc (Debian 8.3.0-6) 8.3.0
+ // error: label at end of compound statement
+ NULL;
+ /* fall through */
+ }
+ if (NULL != msg) {
+ err = 0;
+ break;
+ }
+ /* fall through */
+ default:
+ ntp_strerror_r(errno, errbuf, sizeof(errbuf));
+ snprintf(buff, sizeof(buff), "code %d, errno=>%d, %s, %lx=>%s",
+ code, errno, errbuf, err, ERR_reason_error_string(err));
+ msg = buff;
+ }
}
- msyslog(LOG_INFO, "NTSs: SSL accept from %s failed: %s, took %.3f sec",
- addrbuf, msg, sec);
+ msyslog(LOG_INFO, "NTSs: SSL_accept from %s, Failed, took %.3f sec, %s",
+ hostname, sec, msg);
+ if (err)
+ nts_log_ssl_error();
}
-bool nts_ke_request(SSL *ssl) {
+void nts_ke_request(SSL *ssl, const char **errtxt) {
/* RFC 4: servers must accept 1024
* Our cookies can be 104, 136, or 168 for AES_SIV_CMAC_xxx
* 8*168 fits comfortably into 2K.
@@ -388,14 +456,16 @@ bool nts_ke_request(SSL *ssl) {
int bytes_read, bytes_written;
int used;
- bytes_read = nts_ssl_read(ssl, buff, sizeof(buff));
- if (0 > bytes_read)
- return false;
+ bytes_read = nts_ssl_read(ssl, buff, sizeof(buff), errtxt);
+ if (0 >= bytes_read)
+ return;
buf.next = buff;
buf.left = bytes_read;
- if (!nts_ke_process_receive(&buf, &aead))
- return false;
+ if (!nts_ke_process_receive(&buf, &aead)) {
+ *errtxt = "xx";
+ return;
+ }
if ((NO_AEAD == aead) && (NULL != ntsconfig.aead))
aead = nts_string_to_aead(ntsconfig.aead);
@@ -403,25 +473,23 @@ bool nts_ke_request(SSL *ssl) {
aead = AEAD_AES_SIV_CMAC_256; /* default */
keylen = nts_get_key_length(aead);
- if (!nts_make_keys(ssl, aead, c2s, s2c, keylen))
- return false;
+ if (!nts_make_keys(ssl, aead, c2s, s2c, keylen)) {
+ *errtxt = "Can't make keys";
+ return;
+ }
buf.next = buff;
buf.left = sizeof(buff);
- if (!nts_ke_setup_send(&buf, aead, c2s, s2c, keylen))
- return false;
+ nts_ke_setup_send(&buf, aead, c2s, s2c, keylen);
used = sizeof(buff)-buf.left;
- bytes_written = nts_ssl_write(ssl, buff, used);
+ bytes_written = nts_ssl_write(ssl, buff, used, errtxt);
if (bytes_written != used)
- return false;
+ return;
- /* Skip logging the normal case. */
- if ((bytes_read!=16) || (aead!=15) )
- msyslog(LOG_INFO, "NTSs: Read %d, wrote %d bytes. AEAD=%d",
- bytes_read, bytes_written, aead);
+/* FIXME: Need counters for AEAD */
- return true;
+ return;
}
bool create_listener4(int port) {
@@ -530,7 +598,13 @@ bool nts_ke_process_receive(struct BufCtl_t *buf, int *aead) {
int length;
bool critical = false;
+ // FIXME: msyslogs need rate limiting
type = ke_next_record(buf, &length);
+ if (length > buf->left){
+ msyslog(LOG_ERR, "NTSs: Chunk too big: 0x%x, %d, %d",
+ type, buf->left, length);
+ return false;
+ }
if (NTS_CRITICAL & type) {
critical = true;
type &= ~NTS_CRITICAL;
@@ -597,6 +671,7 @@ bool nts_ke_process_receive(struct BufCtl_t *buf, int *aead) {
} /* case */
} /* while */
+// FIXME: check for missing EOM. Need to read more?
if (buf->left > 0)
return false;
@@ -604,7 +679,7 @@ bool nts_ke_process_receive(struct BufCtl_t *buf, int *aead) {
}
-bool nts_ke_setup_send(struct BufCtl_t *buf, int aead,
+void nts_ke_setup_send(struct BufCtl_t *buf, int aead,
uint8_t *c2s, uint8_t *s2c, int keylen) {
/* 4.1.2 Next Protocol */
@@ -625,9 +700,6 @@ bool nts_ke_setup_send(struct BufCtl_t *buf, int aead,
/* 4.1.1: End, Critical */
ke_append_record_null(buf, NTS_CRITICAL+nts_end_of_message);
-
- return true;
-
}
/* end */
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/-/compare/0bcc21d42178183b303ae8e7188e1f6a7fefbfb0...82e0623ca9ae989901e66c2bea47c792be916ce7
--
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/-/compare/0bcc21d42178183b303ae8e7188e1f6a7fefbfb0...82e0623ca9ae989901e66c2bea47c792be916ce7
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/20250730/df772fbe/attachment-0001.htm>
More information about the vc
mailing list