initial, unchanged sources from openbsd 7.6

This commit is contained in:
Wolfgang Hottgenroth 2025-02-14 12:20:20 +01:00
commit 2f4aa1c412
20 changed files with 7404 additions and 0 deletions

19
Makefile Normal file
View File

@ -0,0 +1,19 @@
# $OpenBSD: Makefile,v 1.16 2015/11/20 18:53:42 tedu Exp $
PROG= ntpd
SRCS= ntpd.c log.c ntp.c ntp_msg.c parse.y config.c \
server.c client.c sensors.c util.c ntp_dns.c \
control.c constraint.c
CFLAGS+= -Wall -I${.CURDIR}
CFLAGS+= -fstack-protector-all
CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
CFLAGS+= -Wmissing-declarations
CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
CFLAGS+= -Wsign-compare
YFLAGS=
LDADD+= -lm -lutil -ltls -lssl -lcrypto
DPADD+= ${LIBUTIL} ${LIBCRYPTO} ${LIBSSL} ${LIBTLS}
LINKS= ${BINDIR}/ntpd ${BINDIR}/ntpctl
MAN= ntpd.8 ntpd.conf.5 ntpctl.8
.include <bsd.prog.mk>

513
client.c Normal file
View File

@ -0,0 +1,513 @@
/* $OpenBSD: client.c,v 1.118 2023/12/20 15:36:36 otto Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <errno.h>
#include <md5.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "ntpd.h"
int client_update(struct ntp_peer *);
int auto_cmp(const void *, const void *);
void handle_auto(u_int8_t, double);
void set_deadline(struct ntp_peer *, time_t);
void
set_next(struct ntp_peer *p, time_t t)
{
p->next = getmonotime() + t;
p->deadline = 0;
p->poll = t;
}
void
set_deadline(struct ntp_peer *p, time_t t)
{
p->deadline = getmonotime() + t;
p->next = 0;
}
int
client_peer_init(struct ntp_peer *p)
{
p->query.fd = -1;
p->query.msg.status = MODE_CLIENT | (NTP_VERSION << 3);
p->query.xmttime = 0;
p->state = STATE_NONE;
p->shift = 0;
p->trustlevel = TRUSTLEVEL_PATHETIC;
p->lasterror = 0;
p->senderrors = 0;
return (client_addr_init(p));
}
int
client_addr_init(struct ntp_peer *p)
{
struct sockaddr_in *sa_in;
struct sockaddr_in6 *sa_in6;
struct ntp_addr *h;
for (h = p->addr; h != NULL; h = h->next) {
switch (h->ss.ss_family) {
case AF_INET:
sa_in = (struct sockaddr_in *)&h->ss;
if (ntohs(sa_in->sin_port) == 0)
sa_in->sin_port = htons(123);
p->state = STATE_DNS_DONE;
break;
case AF_INET6:
sa_in6 = (struct sockaddr_in6 *)&h->ss;
if (ntohs(sa_in6->sin6_port) == 0)
sa_in6->sin6_port = htons(123);
p->state = STATE_DNS_DONE;
break;
default:
fatalx("king bula sez: wrong AF in client_addr_init");
/* NOTREACHED */
}
}
p->query.fd = -1;
set_next(p, 0);
return (0);
}
int
client_nextaddr(struct ntp_peer *p)
{
if (p->query.fd != -1) {
close(p->query.fd);
p->query.fd = -1;
}
if (p->state == STATE_DNS_INPROGRESS)
return (-1);
if (p->addr_head.a == NULL) {
priv_dns(IMSG_HOST_DNS, p->addr_head.name, p->id);
p->state = STATE_DNS_INPROGRESS;
return (-1);
}
p->shift = 0;
p->trustlevel = TRUSTLEVEL_PATHETIC;
if (p->addr == NULL)
p->addr = p->addr_head.a;
else if ((p->addr = p->addr->next) == NULL)
return (1);
return (0);
}
int
client_query(struct ntp_peer *p)
{
int val;
if (p->addr == NULL && client_nextaddr(p) == -1) {
if (conf->settime)
set_next(p, INTERVAL_AUIO_DNSFAIL);
else
set_next(p, MAXIMUM(SETTIME_TIMEOUT,
scale_interval(INTERVAL_QUERY_AGGRESSIVE)));
return (0);
}
if (conf->status.synced && p->addr->notauth) {
peer_addr_head_clear(p);
client_nextaddr(p);
return (0);
}
if (p->state < STATE_DNS_DONE || p->addr == NULL)
return (-1);
if (p->query.fd == -1) {
struct sockaddr *sa = (struct sockaddr *)&p->addr->ss;
struct sockaddr *qa4 = (struct sockaddr *)&p->query_addr4;
struct sockaddr *qa6 = (struct sockaddr *)&p->query_addr6;
if ((p->query.fd = socket(p->addr->ss.ss_family, SOCK_DGRAM,
0)) == -1)
fatal("client_query socket");
if (p->addr->ss.ss_family == qa4->sa_family) {
if (bind(p->query.fd, qa4, SA_LEN(qa4)) == -1)
fatal("couldn't bind to IPv4 query address: %s",
log_sockaddr(qa4));
} else if (p->addr->ss.ss_family == qa6->sa_family) {
if (bind(p->query.fd, qa6, SA_LEN(qa6)) == -1)
fatal("couldn't bind to IPv6 query address: %s",
log_sockaddr(qa6));
}
if (connect(p->query.fd, sa, SA_LEN(sa)) == -1) {
if (errno == ECONNREFUSED || errno == ENETUNREACH ||
errno == EHOSTUNREACH || errno == EADDRNOTAVAIL) {
/* cycle through addresses, but do increase
senderrors */
client_nextaddr(p);
if (p->addr == NULL)
p->addr = p->addr_head.a;
set_next(p, MAXIMUM(SETTIME_TIMEOUT,
scale_interval(INTERVAL_QUERY_AGGRESSIVE)));
p->senderrors++;
return (-1);
} else
fatal("client_query connect");
}
val = IPTOS_LOWDELAY;
if (p->addr->ss.ss_family == AF_INET && setsockopt(p->query.fd,
IPPROTO_IP, IP_TOS, &val, sizeof(val)) == -1)
log_warn("setsockopt IPTOS_LOWDELAY");
val = 1;
if (setsockopt(p->query.fd, SOL_SOCKET, SO_TIMESTAMP,
&val, sizeof(val)) == -1)
fatal("setsockopt SO_TIMESTAMP");
}
/*
* Send out a random 64-bit number as our transmit time. The NTP
* server will copy said number into the originate field on the
* response that it sends us. This is totally legal per the SNTP spec.
*
* The impact of this is two fold: we no longer send out the current
* system time for the world to see (which may aid an attacker), and
* it gives us a (not very secure) way of knowing that we're not
* getting spoofed by an attacker that can't capture our traffic
* but can spoof packets from the NTP server we're communicating with.
*
* Save the real transmit timestamp locally.
*/
p->query.msg.xmttime.int_partl = arc4random();
p->query.msg.xmttime.fractionl = arc4random();
p->query.xmttime = gettime();
if (ntp_sendmsg(p->query.fd, NULL, &p->query.msg) == -1) {
p->senderrors++;
set_next(p, INTERVAL_QUERY_PATHETIC);
p->trustlevel = TRUSTLEVEL_PATHETIC;
return (-1);
}
p->senderrors = 0;
p->state = STATE_QUERY_SENT;
set_deadline(p, QUERYTIME_MAX);
return (0);
}
int
auto_cmp(const void *a, const void *b)
{
double at = *(const double *)a;
double bt = *(const double *)b;
return at < bt ? -1 : (at > bt ? 1 : 0);
}
void
handle_auto(uint8_t trusted, double offset)
{
static int count;
static double v[AUTO_REPLIES];
/*
* It happens the (constraint) resolves initially fail, don't give up
* but see if we get validated replies later.
*/
if (!trusted && conf->constraint_median == 0)
return;
if (offset < AUTO_THRESHOLD) {
/* don't bother */
priv_settime(0, "offset is negative or close enough");
return;
}
/* collect some more */
v[count++] = offset;
if (count < AUTO_REPLIES)
return;
/* we have enough */
qsort(v, count, sizeof(double), auto_cmp);
if (AUTO_REPLIES % 2 == 0)
offset = (v[AUTO_REPLIES / 2 - 1] + v[AUTO_REPLIES / 2]) / 2;
else
offset = v[AUTO_REPLIES / 2];
priv_settime(offset, "");
}
/*
* -1: Not processed, not an NTP message (e.g. icmp induced ECONNREFUSED)
* 0: Not prrocessed due to validation issues
* 1: NTP message validated and processed
*/
int
client_dispatch(struct ntp_peer *p, u_int8_t settime, u_int8_t automatic)
{
struct ntp_msg msg;
struct msghdr somsg;
struct iovec iov[1];
struct timeval tv;
char buf[NTP_MSGSIZE];
union {
struct cmsghdr hdr;
char buf[CMSG_SPACE(sizeof(tv))];
} cmsgbuf;
struct cmsghdr *cmsg;
ssize_t size;
double T1, T2, T3, T4, offset, delay;
time_t interval;
memset(&somsg, 0, sizeof(somsg));
iov[0].iov_base = buf;
iov[0].iov_len = sizeof(buf);
somsg.msg_iov = iov;
somsg.msg_iovlen = 1;
somsg.msg_control = cmsgbuf.buf;
somsg.msg_controllen = sizeof(cmsgbuf.buf);
if ((size = recvmsg(p->query.fd, &somsg, 0)) == -1) {
if (errno == EHOSTUNREACH || errno == EHOSTDOWN ||
errno == ENETUNREACH || errno == ENETDOWN ||
errno == ECONNREFUSED || errno == EADDRNOTAVAIL ||
errno == ENOPROTOOPT || errno == ENOENT) {
client_log_error(p, "recvmsg", errno);
set_next(p, error_interval());
return (-1);
} else
fatal("recvfrom");
}
if (somsg.msg_flags & MSG_TRUNC) {
client_log_error(p, "recvmsg packet", EMSGSIZE);
set_next(p, error_interval());
return (0);
}
if (somsg.msg_flags & MSG_CTRUNC) {
client_log_error(p, "recvmsg control data", E2BIG);
set_next(p, error_interval());
return (0);
}
for (cmsg = CMSG_FIRSTHDR(&somsg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&somsg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_TIMESTAMP) {
memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));
T4 = gettime_from_timeval(&tv);
break;
}
}
if (cmsg == NULL)
fatal("SCM_TIMESTAMP");
ntp_getmsg((struct sockaddr *)&p->addr->ss, buf, size, &msg);
if (msg.orgtime.int_partl != p->query.msg.xmttime.int_partl ||
msg.orgtime.fractionl != p->query.msg.xmttime.fractionl)
return (0);
if ((msg.status & LI_ALARM) == LI_ALARM || msg.stratum == 0 ||
msg.stratum > NTP_MAXSTRATUM) {
char s[16];
if ((msg.status & LI_ALARM) == LI_ALARM) {
strlcpy(s, "alarm", sizeof(s));
} else if (msg.stratum == 0) {
/* Kiss-o'-Death (KoD) packet */
strlcpy(s, "KoD", sizeof(s));
} else if (msg.stratum > NTP_MAXSTRATUM) {
snprintf(s, sizeof(s), "stratum %d", msg.stratum);
}
interval = error_interval();
set_next(p, interval);
log_info("reply from %s: not synced (%s), next query %llds",
log_ntp_addr(p->addr), s, (long long)interval);
return (0);
}
/*
* From RFC 2030 (with a correction to the delay math):
*
* Timestamp Name ID When Generated
* ------------------------------------------------------------
* Originate Timestamp T1 time request sent by client
* Receive Timestamp T2 time request received by server
* Transmit Timestamp T3 time reply sent by server
* Destination Timestamp T4 time reply received by client
*
* The roundtrip delay d and local clock offset t are defined as
*
* d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2.
*/
T1 = p->query.xmttime;
T2 = lfp_to_d(msg.rectime);
T3 = lfp_to_d(msg.xmttime);
/* Detect liars */
if (!p->trusted && conf->constraint_median != 0 &&
(constraint_check(T2) != 0 || constraint_check(T3) != 0)) {
log_info("reply from %s: constraint check failed",
log_ntp_addr(p->addr));
set_next(p, error_interval());
return (0);
}
p->reply[p->shift].offset = ((T2 - T1) + (T3 - T4)) / 2 - getoffset();
p->reply[p->shift].delay = (T4 - T1) - (T3 - T2);
p->reply[p->shift].status.stratum = msg.stratum;
if (p->reply[p->shift].delay < 0) {
interval = error_interval();
set_next(p, interval);
log_info("reply from %s: negative delay %fs, "
"next query %llds",
log_ntp_addr(p->addr),
p->reply[p->shift].delay, (long long)interval);
return (0);
}
p->reply[p->shift].error = (T2 - T1) - (T3 - T4);
p->reply[p->shift].rcvd = getmonotime();
p->reply[p->shift].good = 1;
p->reply[p->shift].status.leap = (msg.status & LIMASK);
p->reply[p->shift].status.precision = msg.precision;
p->reply[p->shift].status.rootdelay = sfp_to_d(msg.rootdelay);
p->reply[p->shift].status.rootdispersion = sfp_to_d(msg.dispersion);
p->reply[p->shift].status.refid = msg.refid;
p->reply[p->shift].status.reftime = lfp_to_d(msg.reftime);
p->reply[p->shift].status.poll = msg.ppoll;
if (p->addr->ss.ss_family == AF_INET) {
p->reply[p->shift].status.send_refid =
((struct sockaddr_in *)&p->addr->ss)->sin_addr.s_addr;
} else if (p->addr->ss.ss_family == AF_INET6) {
MD5_CTX context;
u_int8_t digest[MD5_DIGEST_LENGTH];
MD5Init(&context);
MD5Update(&context, ((struct sockaddr_in6 *)&p->addr->ss)->
sin6_addr.s6_addr, sizeof(struct in6_addr));
MD5Final(digest, &context);
memcpy((char *)&p->reply[p->shift].status.send_refid, digest,
sizeof(u_int32_t));
} else
p->reply[p->shift].status.send_refid = msg.xmttime.fractionl;
p->state = STATE_REPLY_RECEIVED;
/* every received reply which we do not discard increases trust */
if (p->trustlevel < TRUSTLEVEL_MAX) {
if (p->trustlevel < TRUSTLEVEL_BADPEER &&
p->trustlevel + 1 >= TRUSTLEVEL_BADPEER)
log_info("peer %s now valid",
log_ntp_addr(p->addr));
p->trustlevel++;
}
offset = p->reply[p->shift].offset;
delay = p->reply[p->shift].delay;
client_update(p);
if (settime) {
if (automatic)
handle_auto(p->trusted, p->reply[p->shift].offset);
else
priv_settime(p->reply[p->shift].offset, "");
}
if (p->trustlevel < TRUSTLEVEL_PATHETIC)
interval = scale_interval(INTERVAL_QUERY_PATHETIC);
else if (p->trustlevel < TRUSTLEVEL_AGGRESSIVE)
interval = (conf->settime && conf->automatic) ?
INTERVAL_QUERY_ULTRA_VIOLENCE :
scale_interval(INTERVAL_QUERY_AGGRESSIVE);
else
interval = scale_interval(INTERVAL_QUERY_NORMAL);
log_debug("reply from %s: offset %f delay %f, "
"next query %llds", log_ntp_addr(p->addr),
offset, delay, (long long)interval);
set_next(p, interval);
if (++p->shift >= OFFSET_ARRAY_SIZE)
p->shift = 0;
return (1);
}
int
client_update(struct ntp_peer *p)
{
int shift, best = -1, good = 0;
/*
* clock filter
* find the offset which arrived with the lowest delay
* use that as the peer update
* invalidate it and all older ones
*/
for (shift = 0; shift < OFFSET_ARRAY_SIZE; shift++)
if (p->reply[shift].good) {
good++;
if (best == -1 ||
p->reply[shift].delay < p->reply[best].delay)
best = shift;
}
if (best == -1 || good < 8)
return (-1);
p->update = p->reply[best];
if (priv_adjtime() == 0) {
for (shift = 0; shift < OFFSET_ARRAY_SIZE; shift++)
if (p->reply[shift].rcvd <= p->reply[best].rcvd)
p->reply[shift].good = 0;
}
return (0);
}
void
client_log_error(struct ntp_peer *peer, const char *operation, int error)
{
const char *address;
address = log_ntp_addr(peer->addr);
if (peer->lasterror == error) {
log_debug("%s %s: %s", operation, address, strerror(error));
return;
}
peer->lasterror = error;
log_warn("%s %s", operation, address);
}

186
config.c Normal file
View File

@ -0,0 +1,186 @@
/* $OpenBSD: config.c,v 1.33 2020/04/12 14:20:56 otto Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <resolv.h>
#include <unistd.h>
#include "ntpd.h"
struct ntp_addr *host_ip(const char *);
int host_dns1(const char *, struct ntp_addr **, int);
static u_int32_t maxid = 0;
static u_int32_t constraint_maxid = 0;
int non_numeric;
void
host(const char *s, struct ntp_addr **hn)
{
struct ntp_addr *h;
if (!strcmp(s, "*")) {
if ((h = calloc(1, sizeof(*h))) == NULL)
fatal(NULL);
} else {
if ((h = host_ip(s)) == NULL) {
non_numeric = 1;
return;
}
}
*hn = h;
}
struct ntp_addr *
host_ip(const char *s)
{
struct addrinfo hints, *res;
struct ntp_addr *h = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM; /*dummy*/
hints.ai_flags = AI_NUMERICHOST;
if (getaddrinfo(s, "0", &hints, &res) == 0) {
if (res->ai_family == AF_INET ||
res->ai_family == AF_INET6) {
if ((h = calloc(1, sizeof(*h))) == NULL)
fatal(NULL);
memcpy(&h->ss, res->ai_addr, res->ai_addrlen);
}
freeaddrinfo(res);
}
return (h);
}
void
host_dns_free(struct ntp_addr *hn)
{
struct ntp_addr *h = hn, *tmp;
while (h) {
tmp = h;
h = h->next;
free(tmp);
}
}
int
host_dns1(const char *s, struct ntp_addr **hn, int notauth)
{
struct addrinfo hints, *res0, *res;
int error, cnt = 0;
struct ntp_addr *h, *hh = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM; /* DUMMY */
hints.ai_flags = AI_ADDRCONFIG;
error = getaddrinfo(s, NULL, &hints, &res0);
if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME)
return (0);
if (error) {
log_warnx("could not parse \"%s\": %s", s,
gai_strerror(error));
return (-1);
}
for (res = res0; res && cnt < MAX_SERVERS_DNS; res = res->ai_next) {
if (res->ai_family != AF_INET &&
res->ai_family != AF_INET6)
continue;
if ((h = calloc(1, sizeof(*h))) == NULL)
fatal(NULL);
memcpy(&h->ss, res->ai_addr, res->ai_addrlen);
h->notauth = notauth;
h->next = hh;
hh = h;
cnt++;
}
freeaddrinfo(res0);
*hn = hh;
return (cnt);
}
int
host_dns(const char *s, int synced, struct ntp_addr **hn)
{
int error, save_opts;
log_debug("trying to resolve %s", s);
error = host_dns1(s, hn, 0);
if (!synced && error <= 0) {
log_debug("no luck, trying to resolve %s without checking", s);
save_opts = _res.options;
_res.options |= RES_USE_CD;
error = host_dns1(s, hn, 1);
_res.options = save_opts;
}
log_debug("resolve %s done: %d", s, error);
return error;
}
struct ntp_peer *
new_peer(void)
{
struct ntp_peer *p;
if ((p = calloc(1, sizeof(struct ntp_peer))) == NULL)
fatal("new_peer calloc");
p->id = ++maxid;
return (p);
}
struct ntp_conf_sensor *
new_sensor(char *device)
{
struct ntp_conf_sensor *s;
if ((s = calloc(1, sizeof(struct ntp_conf_sensor))) == NULL)
fatal("new_sensor calloc");
if ((s->device = strdup(device)) == NULL)
fatal("new_sensor strdup");
return (s);
}
struct constraint *
new_constraint(void)
{
struct constraint *p;
if ((p = calloc(1, sizeof(struct constraint))) == NULL)
fatal("new_constraint calloc");
p->id = ++constraint_maxid;
p->fd = -1;
return (p);
}

1167
constraint.c Normal file

File diff suppressed because it is too large Load Diff

452
control.c Normal file
View File

@ -0,0 +1,452 @@
/* $OpenBSD: control.c,v 1.21 2024/04/23 13:34:51 jsg Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2012 Mike Miller <mmiller@mgm51.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <err.h>
#include "ntpd.h"
#define CONTROL_BACKLOG 5
#define square(x) ((x) * (x))
int
control_check(char *path)
{
struct sockaddr_un sun;
int fd;
bzero(&sun, sizeof(sun));
sun.sun_family = AF_UNIX;
strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
log_debug("control_check: socket check");
return (-1);
}
if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
log_debug("control_check: socket in use");
close(fd);
return (-1);
}
close(fd);
return (0);
}
int
control_init(char *path)
{
struct sockaddr_un sa;
int fd;
mode_t old_umask;
if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) {
log_warn("control_init: socket");
return (-1);
}
memset(&sa, 0, sizeof(sa));
sa.sun_family = AF_UNIX;
if (strlcpy(sa.sun_path, path, sizeof(sa.sun_path)) >=
sizeof(sa.sun_path))
errx(1, "ctl socket name too long");
if (unlink(path) == -1)
if (errno != ENOENT) {
log_warn("control_init: unlink %s", path);
close(fd);
return (-1);
}
old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
log_warn("control_init: bind: %s", path);
close(fd);
umask(old_umask);
return (-1);
}
umask(old_umask);
if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
log_warn("control_init: chmod");
close(fd);
(void)unlink(path);
return (-1);
}
session_socket_nonblockmode(fd);
return (fd);
}
int
control_listen(int fd)
{
if (fd != -1 && listen(fd, CONTROL_BACKLOG) == -1) {
log_warn("control_listen: listen");
return (-1);
}
return (0);
}
void
control_shutdown(int fd)
{
close(fd);
}
int
control_accept(int listenfd)
{
int connfd;
socklen_t len;
struct sockaddr_un sa;
struct ctl_conn *ctl_conn;
len = sizeof(sa);
if ((connfd = accept(listenfd,
(struct sockaddr *)&sa, &len)) == -1) {
if (errno != EWOULDBLOCK && errno != EINTR)
log_warn("control_accept: accept");
return (0);
}
session_socket_nonblockmode(connfd);
if ((ctl_conn = calloc(1, sizeof(struct ctl_conn))) == NULL) {
log_warn("control_accept");
close(connfd);
return (0);
}
imsg_init(&ctl_conn->ibuf, connfd);
TAILQ_INSERT_TAIL(&ctl_conns, ctl_conn, entry);
return (1);
}
struct ctl_conn *
control_connbyfd(int fd)
{
struct ctl_conn *c;
TAILQ_FOREACH(c, &ctl_conns, entry) {
if (c->ibuf.fd == fd)
break;
}
return (c);
}
int
control_close(int fd)
{
struct ctl_conn *c;
if ((c = control_connbyfd(fd)) == NULL) {
log_warn("control_close: fd %d: not found", fd);
return (0);
}
msgbuf_clear(&c->ibuf.w);
TAILQ_REMOVE(&ctl_conns, c, entry);
close(c->ibuf.fd);
free(c);
return (1);
}
int
control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt)
{
struct imsg imsg;
struct ctl_conn *c;
struct ntp_peer *p;
struct ntp_sensor *s;
struct ctl_show_status c_status;
struct ctl_show_peer c_peer;
struct ctl_show_sensor c_sensor;
int cnt;
ssize_t n;
if ((c = control_connbyfd(pfd->fd)) == NULL) {
log_warn("control_dispatch_msg: fd %d: not found", pfd->fd);
return (0);
}
if (pfd->revents & POLLOUT)
if (msgbuf_write(&c->ibuf.w) <= 0 && errno != EAGAIN) {
*ctl_cnt -= control_close(pfd->fd);
return (1);
}
if (!(pfd->revents & POLLIN))
return (0);
if (((n = imsg_read(&c->ibuf)) == -1 && errno != EAGAIN) || n == 0) {
*ctl_cnt -= control_close(pfd->fd);
return (1);
}
for (;;) {
if ((n = imsg_get(&c->ibuf, &imsg)) == -1) {
*ctl_cnt -= control_close(pfd->fd);
return (1);
}
if (n == 0)
break;
switch (imsg.hdr.type) {
case IMSG_CTL_SHOW_STATUS:
build_show_status(&c_status);
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_STATUS, 0, 0, -1,
&c_status, sizeof (c_status));
break;
case IMSG_CTL_SHOW_PEERS:
cnt = 0;
TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
build_show_peer(&c_peer, p);
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_PEERS,
0, 0, -1, &c_peer, sizeof(c_peer));
cnt++;
}
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_PEERS_END,
0, 0, -1, &cnt, sizeof(cnt));
break;
case IMSG_CTL_SHOW_SENSORS:
cnt = 0;
TAILQ_FOREACH(s, &conf->ntp_sensors, entry) {
build_show_sensor(&c_sensor, s);
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_SENSORS,
0, 0, -1, &c_sensor, sizeof(c_sensor));
cnt++;
}
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_SENSORS_END,
0, 0, -1, &cnt, sizeof(cnt));
break;
case IMSG_CTL_SHOW_ALL:
build_show_status(&c_status);
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_STATUS, 0, 0, -1,
&c_status, sizeof (c_status));
cnt = 0;
TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
build_show_peer(&c_peer, p);
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_PEERS,
0, 0, -1, &c_peer, sizeof(c_peer));
cnt++;
}
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_PEERS_END,
0, 0, -1, &cnt, sizeof(cnt));
cnt = 0;
TAILQ_FOREACH(s, &conf->ntp_sensors, entry) {
build_show_sensor(&c_sensor, s);
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_SENSORS,
0, 0, -1, &c_sensor, sizeof(c_sensor));
cnt++;
}
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_SENSORS_END,
0, 0, -1, &cnt, sizeof(cnt));
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_ALL_END,
0, 0, -1, NULL, 0);
break;
default:
break;
}
imsg_free(&imsg);
}
return (0);
}
void
session_socket_nonblockmode(int fd)
{
int flags;
if ((flags = fcntl(fd, F_GETFL)) == -1)
fatal("fcntl F_GETFL");
flags |= O_NONBLOCK;
if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
fatal("fcntl F_SETFL");
}
void
build_show_status(struct ctl_show_status *cs)
{
struct ntp_peer *p;
struct ntp_sensor *s;
cs->peercnt = cs->valid_peers = 0;
cs->sensorcnt = cs->valid_sensors = 0;
TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
cs->peercnt++;
if (p->trustlevel >= TRUSTLEVEL_BADPEER)
cs->valid_peers++;
}
TAILQ_FOREACH(s, &conf->ntp_sensors, entry) {
cs->sensorcnt++;
if (s->update.good)
cs->valid_sensors++;
}
cs->synced = conf->status.synced;
cs->stratum = conf->status.stratum;
cs->clock_offset = getoffset() * 1000.0;
cs->constraints = !TAILQ_EMPTY(&conf->constraints);
cs->constraint_median = conf->constraint_median;
cs->constraint_last = conf->constraint_last;
cs->constraint_errors = conf->constraint_errors;
}
void
build_show_peer(struct ctl_show_peer *cp, struct ntp_peer *p)
{
const char *a = "not resolved";
const char *pool = "", *addr_head_name = "";
const char *auth = "";
int shift, best = -1, validdelaycnt = 0, jittercnt = 0;
time_t now;
now = getmonotime();
if (p->addr) {
a = log_ntp_addr(p->addr);
if (p->addr->notauth)
auth = " (non-dnssec lookup)";
}
if (p->addr_head.pool)
pool = "from pool ";
if (0 != strcmp(a, p->addr_head.name) || p->addr_head.pool)
addr_head_name = p->addr_head.name;
snprintf(cp->peer_desc, sizeof(cp->peer_desc),
"%s %s%s%s", a, pool, addr_head_name, auth);
cp->offset = cp->delay = 0.0;
for (shift = 0; shift < OFFSET_ARRAY_SIZE; shift++) {
if (p->reply[shift].delay > 0.0) {
cp->offset += p->reply[shift].offset;
cp->delay += p->reply[shift].delay;
if (best == -1 ||
p->reply[shift].delay < p->reply[best].delay)
best = shift;
validdelaycnt++;
}
}
if (validdelaycnt > 1) {
cp->offset /= validdelaycnt;
cp->delay /= validdelaycnt;
}
cp->jitter = 0.0;
if (best != -1) {
for (shift = 0; shift < OFFSET_ARRAY_SIZE; shift++) {
if (p->reply[shift].delay > 0.0 && shift != best) {
cp->jitter += square(p->reply[shift].delay -
p->reply[best].delay);
jittercnt++;
}
}
if (jittercnt > 1)
cp->jitter /= jittercnt;
cp->jitter = sqrt(cp->jitter);
}
if (p->shift == 0)
shift = OFFSET_ARRAY_SIZE - 1;
else
shift = p->shift - 1;
if (conf->status.synced == 1 &&
p->reply[shift].status.send_refid == conf->status.refid)
cp->syncedto = 1;
else
cp->syncedto = 0;
/* milliseconds to reduce number of leading zeroes */
cp->offset *= 1000.0;
cp->delay *= 1000.0;
cp->jitter *= 1000.0;
cp->weight = p->weight;
cp->trustlevel = p->trustlevel;
cp->stratum = p->reply[shift].status.stratum;
cp->next = p->next - now < 0 ? 0 : p->next - now;
cp->poll = p->poll;
}
void
build_show_sensor(struct ctl_show_sensor *cs, struct ntp_sensor *s)
{
time_t now;
u_int8_t shift;
u_int32_t refid;
now = getmonotime();
memcpy(&refid, SENSOR_DEFAULT_REFID, sizeof(refid));
refid = refid == s->refid ? 0 : s->refid;
snprintf(cs->sensor_desc, sizeof(cs->sensor_desc),
"%s %.4s", s->device, (char *)&refid);
if (s->shift == 0)
shift = SENSOR_OFFSETS - 1;
else
shift = s->shift - 1;
if (conf->status.synced == 1 &&
s->offsets[shift].status.send_refid == conf->status.refid)
cs->syncedto = 1;
else
cs->syncedto = 0;
cs->weight = s->weight;
cs->good = s->update.good;
cs->stratum = s->offsets[shift].status.stratum;
cs->next = s->next - now < 0 ? 0 : s->next - now;
cs->poll = SENSOR_QUERY_INTERVAL;
cs->offset = s->offsets[shift].offset * 1000.0;
cs->correction = (double)s->correction / 1000.0;
}

202
log.c Normal file
View File

@ -0,0 +1,202 @@
/* $OpenBSD: log.c,v 1.19 2019/07/03 05:04:19 otto Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <syslog.h>
#include <errno.h>
#include <time.h>
#include "log.h"
static int dest;
static int verbose;
const char *log_procname;
void
log_init(int n_dest, int n_verbose, int facility)
{
extern char *__progname;
dest = n_dest;
verbose = n_verbose;
log_procinit(__progname);
if (dest & LOG_TO_SYSLOG)
openlog(__progname, LOG_PID | LOG_NDELAY, facility);
tzset();
}
void
log_procinit(const char *procname)
{
if (procname != NULL)
log_procname = procname;
}
void
log_setverbose(int v)
{
verbose = v;
}
int
log_getverbose(void)
{
return (verbose);
}
void
logit(int pri, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vlog(pri, fmt, ap);
va_end(ap);
}
void
vlog(int pri, const char *fmt, va_list ap)
{
char *nfmt;
int saved_errno = errno;
va_list ap2;
va_copy(ap2, ap);
if (dest & LOG_TO_STDERR) {
/* best effort in out of mem situations */
if (asprintf(&nfmt, "%s\n", fmt) == -1) {
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
} else {
vfprintf(stderr, nfmt, ap);
free(nfmt);
}
fflush(stderr);
}
if (dest & LOG_TO_SYSLOG)
vsyslog(pri, fmt, ap2);
va_end(ap2);
errno = saved_errno;
}
void
log_warn(const char *emsg, ...)
{
char *nfmt;
va_list ap;
int saved_errno = errno;
/* best effort to even work in out of memory situations */
if (emsg == NULL)
logit(LOG_ERR, "%s", strerror(saved_errno));
else {
va_start(ap, emsg);
if (asprintf(&nfmt, "%s: %s", emsg,
strerror(saved_errno)) == -1) {
/* we tried it... */
vlog(LOG_ERR, emsg, ap);
logit(LOG_ERR, "%s", strerror(saved_errno));
} else {
vlog(LOG_ERR, nfmt, ap);
free(nfmt);
}
va_end(ap);
}
errno = saved_errno;
}
void
log_warnx(const char *emsg, ...)
{
va_list ap;
va_start(ap, emsg);
vlog(LOG_ERR, emsg, ap);
va_end(ap);
}
void
log_info(const char *emsg, ...)
{
va_list ap;
va_start(ap, emsg);
vlog(LOG_INFO, emsg, ap);
va_end(ap);
}
void
log_debug(const char *emsg, ...)
{
va_list ap;
if (verbose > 1) {
va_start(ap, emsg);
vlog(LOG_DEBUG, emsg, ap);
va_end(ap);
}
}
static void
vfatalc(int code, const char *emsg, va_list ap)
{
static char s[BUFSIZ];
const char *sep;
if (emsg != NULL) {
(void)vsnprintf(s, sizeof(s), emsg, ap);
sep = ": ";
} else {
s[0] = '\0';
sep = "";
}
if (code)
logit(LOG_CRIT, "%s: %s%s%s",
log_procname, s, sep, strerror(code));
else
logit(LOG_CRIT, "%s%s%s", log_procname, sep, s);
}
void
fatal(const char *emsg, ...)
{
va_list ap;
va_start(ap, emsg);
vfatalc(errno, emsg, ap);
va_end(ap);
exit(1);
}
void
fatalx(const char *emsg, ...)
{
va_list ap;
va_start(ap, emsg);
vfatalc(0, emsg, ap);
va_end(ap);
exit(1);
}

48
log.h Normal file
View File

@ -0,0 +1,48 @@
/* $OpenBSD: log.h,v 1.6 2021/12/13 18:28:40 deraadt Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LOG_H
#define LOG_H
#include <stdarg.h>
#define LOG_TO_STDERR (1<<0)
#define LOG_TO_SYSLOG (1<<1)
void log_init(int, int, int);
void log_procinit(const char *);
void log_setverbose(int);
int log_getverbose(void);
void log_warn(const char *, ...)
__attribute__((__format__ (printf, 1, 2)));
void log_warnx(const char *, ...)
__attribute__((__format__ (printf, 1, 2)));
void log_info(const char *, ...)
__attribute__((__format__ (printf, 1, 2)));
void log_debug(const char *, ...)
__attribute__((__format__ (printf, 1, 2)));
void logit(int, const char *, ...)
__attribute__((__format__ (printf, 2, 3)));
void vlog(int, const char *, va_list)
__attribute__((__format__ (printf, 2, 0)));
__dead void fatal(const char *, ...)
__attribute__((__format__ (printf, 1, 2)));
__dead void fatalx(const char *, ...)
__attribute__((__format__ (printf, 1, 2)));
#endif /* LOG_H */

907
ntp.c Normal file
View File

@ -0,0 +1,907 @@
/* $OpenBSD: ntp.c,v 1.174 2024/02/21 03:31:28 deraadt Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
#include <poll.h>
#include <pwd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#include <err.h>
#include "ntpd.h"
#define PFD_PIPE_MAIN 0
#define PFD_PIPE_DNS 1
#define PFD_SOCK_CTL 2
#define PFD_MAX 3
volatile sig_atomic_t ntp_quit = 0;
struct imsgbuf *ibuf_main;
static struct imsgbuf *ibuf_dns;
struct ntpd_conf *conf;
struct ctl_conns ctl_conns;
u_int peer_cnt;
u_int sensors_cnt;
extern u_int constraint_cnt;
void ntp_sighdlr(int);
int ntp_dispatch_imsg(void);
int ntp_dispatch_imsg_dns(void);
void peer_add(struct ntp_peer *);
void peer_remove(struct ntp_peer *);
int inpool(struct sockaddr_storage *,
struct sockaddr_storage[MAX_SERVERS_DNS], size_t);
void
ntp_sighdlr(int sig)
{
switch (sig) {
case SIGINT:
case SIGTERM:
ntp_quit = 1;
break;
}
}
void
ntp_main(struct ntpd_conf *nconf, struct passwd *pw, int argc, char **argv)
{
int a, b, nfds, i, j, idx_peers, timeout;
int nullfd, pipe_dns[2], idx_clients;
int ctls;
int fd_ctl;
int clear_cdns;
u_int pfd_elms = 0, idx2peer_elms = 0;
u_int listener_cnt, new_cnt, sent_cnt, trial_cnt;
u_int ctl_cnt;
struct pollfd *pfd = NULL;
struct servent *se;
struct listen_addr *la;
struct ntp_peer *p;
struct ntp_peer **idx2peer = NULL;
struct ntp_sensor *s, *next_s;
struct constraint *cstr;
struct timespec tp;
struct stat stb;
struct ctl_conn *cc;
time_t nextaction, last_sensor_scan = 0, now;
time_t last_action = 0, interval, last_cdns_reset = 0;
void *newp;
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, PF_UNSPEC,
pipe_dns) == -1)
fatal("socketpair");
start_child(NTPDNS_PROC_NAME, pipe_dns[1], argc, argv);
log_init(nconf->debug ? LOG_TO_STDERR : LOG_TO_SYSLOG, nconf->verbose,
LOG_DAEMON);
if (!nconf->debug && setsid() == -1)
fatal("setsid");
log_procinit("ntp");
if ((se = getservbyname("ntp", "udp")) == NULL)
fatal("getservbyname");
/* Start control socket. */
if ((fd_ctl = control_init(CTLSOCKET)) == -1)
fatalx("control socket init failed");
if (control_listen(fd_ctl) == -1)
fatalx("control socket listen failed");
if ((nullfd = open("/dev/null", O_RDWR)) == -1)
fatal(NULL);
if (stat(pw->pw_dir, &stb) == -1) {
fatal("privsep dir %s could not be opened", pw->pw_dir);
}
if (stb.st_uid != 0 || (stb.st_mode & (S_IWGRP|S_IWOTH)) != 0) {
fatalx("bad privsep dir %s permissions: %o",
pw->pw_dir, stb.st_mode);
}
if (chroot(pw->pw_dir) == -1)
fatal("chroot");
if (chdir("/") == -1)
fatal("chdir(\"/\")");
if (!nconf->debug) {
dup2(nullfd, STDIN_FILENO);
dup2(nullfd, STDOUT_FILENO);
dup2(nullfd, STDERR_FILENO);
}
close(nullfd);
setproctitle("ntp engine");
conf = nconf;
setup_listeners(se, conf, &listener_cnt);
if (setgroups(1, &pw->pw_gid) ||
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
fatal("can't drop privileges");
endservent();
/* The ntp process will want to open NTP client sockets -> "inet" */
if (pledge("stdio inet", NULL) == -1)
err(1, "pledge");
signal(SIGTERM, ntp_sighdlr);
signal(SIGINT, ntp_sighdlr);
signal(SIGPIPE, SIG_IGN);
signal(SIGHUP, SIG_IGN);
signal(SIGCHLD, SIG_DFL);
if ((ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL)
fatal(NULL);
imsg_init(ibuf_main, PARENT_SOCK_FILENO);
if ((ibuf_dns = malloc(sizeof(struct imsgbuf))) == NULL)
fatal(NULL);
imsg_init(ibuf_dns, pipe_dns[0]);
constraint_cnt = 0;
conf->constraint_median = 0;
conf->constraint_last = getmonotime();
TAILQ_FOREACH(cstr, &conf->constraints, entry)
constraint_cnt += constraint_init(cstr);
TAILQ_FOREACH(p, &conf->ntp_peers, entry)
client_peer_init(p);
memset(&conf->status, 0, sizeof(conf->status));
conf->freq.num = 0;
conf->freq.samples = 0;
conf->freq.x = 0.0;
conf->freq.xx = 0.0;
conf->freq.xy = 0.0;
conf->freq.y = 0.0;
conf->freq.overall_offset = 0.0;
conf->status.synced = 0;
clock_getres(CLOCK_REALTIME, &tp);
b = 1000000000 / tp.tv_nsec; /* convert to Hz */
for (a = 0; b > 1; a--, b >>= 1)
;
conf->status.precision = a;
conf->scale = 1;
TAILQ_INIT(&ctl_conns);
sensor_init();
log_info("ntp engine ready");
ctl_cnt = 0;
peer_cnt = 0;
TAILQ_FOREACH(p, &conf->ntp_peers, entry)
peer_cnt++;
while (ntp_quit == 0) {
if (peer_cnt > idx2peer_elms) {
if ((newp = reallocarray(idx2peer, peer_cnt,
sizeof(*idx2peer))) == NULL) {
/* panic for now */
log_warn("could not resize idx2peer from %u -> "
"%u entries", idx2peer_elms, peer_cnt);
fatalx("exiting");
}
idx2peer = newp;
idx2peer_elms = peer_cnt;
}
new_cnt = PFD_MAX +
peer_cnt + listener_cnt + ctl_cnt;
if (new_cnt > pfd_elms) {
if ((newp = reallocarray(pfd, new_cnt,
sizeof(*pfd))) == NULL) {
/* panic for now */
log_warn("could not resize pfd from %u -> "
"%u entries", pfd_elms, new_cnt);
fatalx("exiting");
}
pfd = newp;
pfd_elms = new_cnt;
}
memset(pfd, 0, sizeof(*pfd) * pfd_elms);
memset(idx2peer, 0, sizeof(*idx2peer) * idx2peer_elms);
nextaction = getmonotime() + 900;
pfd[PFD_PIPE_MAIN].fd = ibuf_main->fd;
pfd[PFD_PIPE_MAIN].events = POLLIN;
pfd[PFD_PIPE_DNS].fd = ibuf_dns->fd;
pfd[PFD_PIPE_DNS].events = POLLIN;
pfd[PFD_SOCK_CTL].fd = fd_ctl;
pfd[PFD_SOCK_CTL].events = POLLIN;
i = PFD_MAX;
TAILQ_FOREACH(la, &conf->listen_addrs, entry) {
pfd[i].fd = la->fd;
pfd[i].events = POLLIN;
i++;
}
idx_peers = i;
sent_cnt = trial_cnt = 0;
TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
if (!p->trusted && constraint_cnt &&
conf->constraint_median == 0)
continue;
if (p->next > 0 && p->next <= getmonotime()) {
if (p->state > STATE_DNS_INPROGRESS)
trial_cnt++;
if (client_query(p) == 0)
sent_cnt++;
}
if (p->deadline > 0 && p->deadline <= getmonotime()) {
timeout = 300;
log_debug("no reply from %s received in time, "
"next query %ds", log_ntp_addr( p->addr),
timeout);
if (p->trustlevel >= TRUSTLEVEL_BADPEER &&
(p->trustlevel /= 2) < TRUSTLEVEL_BADPEER)
log_info("peer %s now invalid",
log_ntp_addr(p->addr));
if (client_nextaddr(p) == 1) {
peer_addr_head_clear(p);
client_nextaddr(p);
}
set_next(p, timeout);
}
if (p->senderrors > MAX_SEND_ERRORS) {
log_debug("failed to send query to %s, "
"next query %ds", log_ntp_addr(p->addr),
INTERVAL_QUERY_PATHETIC);
p->senderrors = 0;
if (client_nextaddr(p) == 1) {
peer_addr_head_clear(p);
client_nextaddr(p);
}
set_next(p, INTERVAL_QUERY_PATHETIC);
}
if (p->next > 0 && p->next < nextaction)
nextaction = p->next;
if (p->deadline > 0 && p->deadline < nextaction)
nextaction = p->deadline;
if (p->state == STATE_QUERY_SENT &&
p->query.fd != -1) {
pfd[i].fd = p->query.fd;
pfd[i].events = POLLIN;
idx2peer[i - idx_peers] = p;
i++;
}
}
idx_clients = i;
if (!TAILQ_EMPTY(&conf->ntp_conf_sensors) &&
(conf->trusted_sensors || constraint_cnt == 0 ||
conf->constraint_median != 0)) {
if (last_sensor_scan == 0 ||
last_sensor_scan + SENSOR_SCAN_INTERVAL <= getmonotime()) {
sensors_cnt = sensor_scan();
last_sensor_scan = getmonotime();
}
if (sensors_cnt == 0 &&
nextaction > last_sensor_scan + SENSOR_SCAN_INTERVAL)
nextaction = last_sensor_scan + SENSOR_SCAN_INTERVAL;
sensors_cnt = 0;
TAILQ_FOREACH(s, &conf->ntp_sensors, entry) {
if (conf->settime && s->offsets[0].offset)
priv_settime(s->offsets[0].offset, NULL);
sensors_cnt++;
if (s->next > 0 && s->next < nextaction)
nextaction = s->next;
}
}
if (conf->settime &&
((trial_cnt > 0 && sent_cnt == 0) ||
(peer_cnt == 0 && sensors_cnt == 0)))
priv_settime(0, "no valid peers configured");
clear_cdns = 1;
TAILQ_FOREACH(cstr, &conf->constraints, entry) {
constraint_query(cstr, conf->status.synced);
if (cstr->state <= STATE_QUERY_SENT)
clear_cdns = 0;
}
if (ibuf_main->w.queued > 0)
pfd[PFD_PIPE_MAIN].events |= POLLOUT;
if (ibuf_dns->w.queued > 0)
pfd[PFD_PIPE_DNS].events |= POLLOUT;
TAILQ_FOREACH(cc, &ctl_conns, entry) {
pfd[i].fd = cc->ibuf.fd;
pfd[i].events = POLLIN;
if (cc->ibuf.w.queued > 0)
pfd[i].events |= POLLOUT;
i++;
}
ctls = i;
now = getmonotime();
if (conf->constraint_median == 0 && clear_cdns &&
now - last_cdns_reset > CONSTRAINT_SCAN_INTERVAL) {
log_debug("Reset constraint info");
constraint_reset();
last_cdns_reset = now;
nextaction = now + CONSTRAINT_RETRY_INTERVAL;
}
timeout = nextaction - now;
if (timeout < 0)
timeout = 0;
if ((nfds = poll(pfd, i, timeout ? timeout * 1000 : 1)) == -1)
if (errno != EINTR) {
log_warn("poll error");
ntp_quit = 1;
}
if (nfds > 0 && (pfd[PFD_PIPE_MAIN].revents & POLLOUT))
if (msgbuf_write(&ibuf_main->w) <= 0 &&
errno != EAGAIN) {
log_warn("pipe write error (to parent)");
ntp_quit = 1;
}
if (nfds > 0 && pfd[PFD_PIPE_MAIN].revents & (POLLIN|POLLERR)) {
nfds--;
if (ntp_dispatch_imsg() == -1) {
log_debug("pipe read error (from main)");
ntp_quit = 1;
}
}
if (nfds > 0 && (pfd[PFD_PIPE_DNS].revents & POLLOUT))
if (msgbuf_write(&ibuf_dns->w) <= 0 &&
errno != EAGAIN) {
log_warn("pipe write error (to dns engine)");
ntp_quit = 1;
}
if (nfds > 0 && pfd[PFD_PIPE_DNS].revents & (POLLIN|POLLERR)) {
nfds--;
if (ntp_dispatch_imsg_dns() == -1) {
log_warn("pipe read error (from dns engine)");
ntp_quit = 1;
}
}
if (nfds > 0 && pfd[PFD_SOCK_CTL].revents & (POLLIN|POLLERR)) {
nfds--;
ctl_cnt += control_accept(fd_ctl);
}
for (j = PFD_MAX; nfds > 0 && j < idx_peers; j++)
if (pfd[j].revents & (POLLIN|POLLERR)) {
nfds--;
if (server_dispatch(pfd[j].fd, conf) == -1) {
log_warn("pipe write error (conf)");
ntp_quit = 1;
}
}
for (; nfds > 0 && j < idx_clients; j++) {
if (pfd[j].revents & (POLLIN|POLLERR)) {
struct ntp_peer *pp = idx2peer[j - idx_peers];
nfds--;
switch (client_dispatch(pp, conf->settime,
conf->automatic)) {
case -1:
log_debug("no reply from %s "
"received", log_ntp_addr(pp->addr));
if (pp->trustlevel >=
TRUSTLEVEL_BADPEER &&
(pp->trustlevel /= 2) <
TRUSTLEVEL_BADPEER)
log_info("peer %s now invalid",
log_ntp_addr(pp->addr));
break;
case 0: /* invalid replies are ignored */
break;
case 1:
last_action = now;
break;
}
}
}
for (; nfds > 0 && j < ctls; j++) {
nfds -= control_dispatch_msg(&pfd[j], &ctl_cnt);
}
for (s = TAILQ_FIRST(&conf->ntp_sensors); s != NULL;
s = next_s) {
next_s = TAILQ_NEXT(s, entry);
if (s->next <= now) {
last_action = now;
sensor_query(s);
}
}
/*
* Compute maximum of scale_interval(INTERVAL_QUERY_NORMAL),
* if we did not process a time message for three times that
* interval, stop advertising we're synced.
*/
interval = INTERVAL_QUERY_NORMAL * conf->scale;
interval += SCALE_INTERVAL(interval) - 1;
if (conf->status.synced && last_action + 3 * interval < now) {
log_info("clock is now unsynced due to lack of replies");
conf->status.synced = 0;
conf->scale = 1;
priv_dns(IMSG_UNSYNCED, NULL, 0);
}
}
msgbuf_write(&ibuf_main->w);
msgbuf_clear(&ibuf_main->w);
free(ibuf_main);
msgbuf_write(&ibuf_dns->w);
msgbuf_clear(&ibuf_dns->w);
free(ibuf_dns);
log_info("ntp engine exiting");
exit(0);
}
int
ntp_dispatch_imsg(void)
{
struct imsg imsg;
int n;
if (((n = imsg_read(ibuf_main)) == -1 && errno != EAGAIN) || n == 0)
return (-1);
for (;;) {
if ((n = imsg_get(ibuf_main, &imsg)) == -1)
return (-1);
if (n == 0)
break;
switch (imsg.hdr.type) {
case IMSG_ADJTIME:
memcpy(&n, imsg.data, sizeof(n));
if (n == 1 && !conf->status.synced) {
log_info("clock is now synced");
conf->status.synced = 1;
priv_dns(IMSG_SYNCED, NULL, 0);
constraint_reset();
} else if (n == 0 && conf->status.synced) {
log_info("clock is now unsynced");
conf->status.synced = 0;
priv_dns(IMSG_UNSYNCED, NULL, 0);
}
break;
case IMSG_CONSTRAINT_RESULT:
constraint_msg_result(imsg.hdr.peerid,
imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
break;
case IMSG_CONSTRAINT_CLOSE:
constraint_msg_close(imsg.hdr.peerid,
imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
break;
default:
break;
}
imsg_free(&imsg);
}
return (0);
}
int
inpool(struct sockaddr_storage *a,
struct sockaddr_storage old[MAX_SERVERS_DNS], size_t n)
{
size_t i;
for (i = 0; i < n; i++) {
if (a->ss_family != old[i].ss_family)
continue;
if (a->ss_family == AF_INET) {
if (((struct sockaddr_in *)a)->sin_addr.s_addr ==
((struct sockaddr_in *)&old[i])->sin_addr.s_addr)
return 1;
} else if (memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
&((struct sockaddr_in6 *)&old[i])->sin6_addr,
sizeof(struct in6_addr)) == 0) {
return 1;
}
}
return 0;
}
int
ntp_dispatch_imsg_dns(void)
{
struct imsg imsg;
struct sockaddr_storage existing[MAX_SERVERS_DNS];
struct ntp_peer *peer, *npeer, *tmp;
u_int16_t dlen;
u_char *p;
struct ntp_addr *h;
size_t addrcount, peercount;
int n;
if (((n = imsg_read(ibuf_dns)) == -1 && errno != EAGAIN) || n == 0)
return (-1);
for (;;) {
if ((n = imsg_get(ibuf_dns, &imsg)) == -1)
return (-1);
if (n == 0)
break;
switch (imsg.hdr.type) {
case IMSG_HOST_DNS:
TAILQ_FOREACH(peer, &conf->ntp_peers, entry)
if (peer->id == imsg.hdr.peerid)
break;
if (peer == NULL) {
log_warnx("IMSG_HOST_DNS with invalid peerID");
break;
}
if (peer->addr != NULL) {
log_warnx("IMSG_HOST_DNS but addr != NULL!");
break;
}
if (peer->addr_head.pool) {
n = 0;
peercount = 0;
TAILQ_FOREACH_SAFE(npeer, &conf->ntp_peers,
entry, tmp) {
if (npeer->addr_head.pool !=
peer->addr_head.pool)
continue;
peercount++;
if (npeer->id == peer->id)
continue;
if (npeer->addr != NULL)
existing[n++] = npeer->addr->ss;
}
}
dlen = imsg.hdr.len - IMSG_HEADER_SIZE;
if (dlen == 0) { /* no data -> temp error */
log_debug("DNS lookup tempfail");
peer->state = STATE_DNS_TEMPFAIL;
if (conf->tmpfail++ == TRIES_AUTO_DNSFAIL)
priv_settime(0, "of dns failures");
break;
}
p = (u_char *)imsg.data;
addrcount = dlen / (sizeof(struct sockaddr_storage) +
sizeof(int));
while (dlen >= sizeof(struct sockaddr_storage) +
sizeof(int)) {
if ((h = calloc(1, sizeof(struct ntp_addr))) ==
NULL)
fatal(NULL);
memcpy(&h->ss, p, sizeof(h->ss));
p += sizeof(h->ss);
dlen -= sizeof(h->ss);
memcpy(&h->notauth, p, sizeof(int));
p += sizeof(int);
dlen -= sizeof(int);
if (peer->addr_head.pool) {
if (peercount > addrcount) {
free(h);
continue;
}
if (inpool(&h->ss, existing,
n)) {
free(h);
continue;
}
log_debug("Adding address %s to %s",
log_ntp_addr(h), peer->addr_head.name);
npeer = new_peer();
npeer->weight = peer->weight;
npeer->query_addr4 = peer->query_addr4;
npeer->query_addr6 = peer->query_addr6;
h->next = NULL;
npeer->addr = h;
npeer->addr_head.a = h;
npeer->addr_head.name =
peer->addr_head.name;
npeer->addr_head.pool =
peer->addr_head.pool;
client_peer_init(npeer);
npeer->state = STATE_DNS_DONE;
peer_add(npeer);
peercount++;
} else {
h->next = peer->addr;
peer->addr = h;
peer->addr_head.a = peer->addr;
peer->state = STATE_DNS_DONE;
}
}
if (dlen != 0)
fatalx("IMSG_HOST_DNS: dlen != 0");
if (peer->addr_head.pool)
peer_remove(peer);
else
client_addr_init(peer);
break;
case IMSG_CONSTRAINT_DNS:
constraint_msg_dns(imsg.hdr.peerid,
imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
break;
case IMSG_PROBE_ROOT:
dlen = imsg.hdr.len - IMSG_HEADER_SIZE;
if (dlen != sizeof(int))
fatalx("IMSG_PROBE_ROOT");
memcpy(&n, imsg.data, sizeof(int));
if (n < 0)
priv_settime(0, "dns probe failed");
break;
default:
break;
}
imsg_free(&imsg);
}
return (0);
}
void
peer_add(struct ntp_peer *p)
{
TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry);
peer_cnt++;
}
void
peer_remove(struct ntp_peer *p)
{
TAILQ_REMOVE(&conf->ntp_peers, p, entry);
free(p);
peer_cnt--;
}
void
peer_addr_head_clear(struct ntp_peer *p)
{
host_dns_free(p->addr_head.a);
p->addr_head.a = NULL;
p->addr = NULL;
}
static void
priv_adjfreq(double offset)
{
double curtime, freq;
if (!conf->status.synced){
conf->freq.samples = 0;
return;
}
conf->freq.samples++;
if (conf->freq.samples <= 0)
return;
conf->freq.overall_offset += offset;
offset = conf->freq.overall_offset;
curtime = gettime_corrected();
conf->freq.xy += offset * curtime;
conf->freq.x += curtime;
conf->freq.y += offset;
conf->freq.xx += curtime * curtime;
if (conf->freq.samples % FREQUENCY_SAMPLES != 0)
return;
freq =
(conf->freq.xy - conf->freq.x * conf->freq.y / conf->freq.samples)
/
(conf->freq.xx - conf->freq.x * conf->freq.x / conf->freq.samples);
if (freq > MAX_FREQUENCY_ADJUST)
freq = MAX_FREQUENCY_ADJUST;
else if (freq < -MAX_FREQUENCY_ADJUST)
freq = -MAX_FREQUENCY_ADJUST;
imsg_compose(ibuf_main, IMSG_ADJFREQ, 0, 0, -1, &freq, sizeof(freq));
conf->filters |= FILTER_ADJFREQ;
conf->freq.xy = 0.0;
conf->freq.x = 0.0;
conf->freq.y = 0.0;
conf->freq.xx = 0.0;
conf->freq.samples = 0;
conf->freq.overall_offset = 0.0;
conf->freq.num++;
}
int
priv_adjtime(void)
{
struct ntp_peer *p;
struct ntp_sensor *s;
int offset_cnt = 0, i = 0, j;
struct ntp_offset **offsets;
double offset_median;
TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
if (p->trustlevel < TRUSTLEVEL_BADPEER)
continue;
if (!p->update.good)
return (1);
offset_cnt += p->weight;
}
TAILQ_FOREACH(s, &conf->ntp_sensors, entry) {
if (!s->update.good)
continue;
offset_cnt += s->weight;
}
if (offset_cnt == 0)
return (1);
if ((offsets = calloc(offset_cnt, sizeof(struct ntp_offset *))) == NULL)
fatal("calloc priv_adjtime");
TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
if (p->trustlevel < TRUSTLEVEL_BADPEER)
continue;
for (j = 0; j < p->weight; j++)
offsets[i++] = &p->update;
}
TAILQ_FOREACH(s, &conf->ntp_sensors, entry) {
if (!s->update.good)
continue;
for (j = 0; j < s->weight; j++)
offsets[i++] = &s->update;
}
qsort(offsets, offset_cnt, sizeof(struct ntp_offset *), offset_compare);
i = offset_cnt / 2;
if (offset_cnt % 2 == 0)
if (offsets[i - 1]->delay < offsets[i]->delay)
i -= 1;
offset_median = offsets[i]->offset;
conf->status.rootdelay = offsets[i]->delay;
conf->status.stratum = offsets[i]->status.stratum;
conf->status.leap = offsets[i]->status.leap;
imsg_compose(ibuf_main, IMSG_ADJTIME, 0, 0, -1,
&offset_median, sizeof(offset_median));
priv_adjfreq(offset_median);
conf->status.reftime = gettime();
conf->status.stratum++; /* one more than selected peer */
if (conf->status.stratum > NTP_MAXSTRATUM)
conf->status.stratum = NTP_MAXSTRATUM;
update_scale(offset_median);
conf->status.refid = offsets[i]->status.send_refid;
free(offsets);
TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
for (i = 0; i < OFFSET_ARRAY_SIZE; i++)
p->reply[i].offset -= offset_median;
p->update.good = 0;
}
TAILQ_FOREACH(s, &conf->ntp_sensors, entry) {
for (i = 0; i < SENSOR_OFFSETS; i++)
s->offsets[i].offset -= offset_median;
s->update.offset -= offset_median;
}
return (0);
}
int
offset_compare(const void *aa, const void *bb)
{
const struct ntp_offset * const *a;
const struct ntp_offset * const *b;
a = aa;
b = bb;
if ((*a)->offset < (*b)->offset)
return (-1);
else if ((*a)->offset > (*b)->offset)
return (1);
else
return (0);
}
void
priv_settime(double offset, char *msg)
{
if (offset == 0)
log_info("cancel settime because %s", msg);
imsg_compose(ibuf_main, IMSG_SETTIME, 0, 0, -1,
&offset, sizeof(offset));
conf->settime = 0;
}
void
priv_dns(int cmd, char *name, u_int32_t peerid)
{
u_int16_t dlen = 0;
if (name != NULL)
dlen = strlen(name) + 1;
imsg_compose(ibuf_dns, cmd, peerid, 0, -1, name, dlen);
}
void
update_scale(double offset)
{
offset += getoffset();
if (offset < 0)
offset = -offset;
if (offset > QSCALE_OFF_MAX || !conf->status.synced ||
conf->freq.num < 3)
conf->scale = 1;
else if (offset < QSCALE_OFF_MIN)
conf->scale = QSCALE_OFF_MAX / QSCALE_OFF_MIN;
else
conf->scale = QSCALE_OFF_MAX / offset;
}
time_t
scale_interval(time_t requested)
{
time_t interval, r;
interval = requested * conf->scale;
r = arc4random_uniform(SCALE_INTERVAL(interval));
return (interval + r);
}
time_t
error_interval(void)
{
time_t interval, r;
interval = INTERVAL_QUERY_PATHETIC * QSCALE_OFF_MAX / QSCALE_OFF_MIN;
r = arc4random_uniform(interval / 10);
return (interval + r);
}

163
ntp.h Normal file
View File

@ -0,0 +1,163 @@
/* $OpenBSD: ntp.h,v 1.15 2023/11/15 15:52:09 otto Exp $ */
/*
* Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _NTP_H_
#define _NTP_H_
/* Style borrowed from NTP ref/tcpdump and updated for SNTPv4 (RFC2030). */
/*
* RFC Section 3
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Integer Part |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Fraction Part |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Integer Part | Fraction Part |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
struct l_fixedpt {
u_int32_t int_partl;
u_int32_t fractionl;
};
#define L_DENOMINATOR (UINT32_MAX + 1ULL)
struct s_fixedpt {
u_int16_t int_parts;
u_int16_t fractions;
};
#define S_DENOMINATOR (UINT16_MAX + 1)
/* RFC Section 4
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |LI | VN | Mode| Stratum | Poll | Precision |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Synchronizing Distance |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Synchronizing Dispersion |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Reference Clock Identifier |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | |
* | Reference Timestamp (64 bits) |
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | |
* | Originate Timestamp (64 bits) |
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | |
* | Receive Timestamp (64 bits) |
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | |
* | Transmit Timestamp (64 bits) |
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Key Identifier (optional) (32) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | |
* | |
* | Message Digest (optional) (128) |
* | |
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
*/
#define NTP_DIGESTSIZE 16
#define NTP_MSGSIZE_NOAUTH 48
#define NTP_MSGSIZE (NTP_MSGSIZE_NOAUTH + 4 + NTP_DIGESTSIZE)
struct ntp_msg {
u_int8_t status; /* status of local clock and leap info */
u_int8_t stratum; /* Stratum level */
u_int8_t ppoll; /* poll value */
int8_t precision;
struct s_fixedpt rootdelay;
struct s_fixedpt dispersion;
u_int32_t refid;
struct l_fixedpt reftime;
struct l_fixedpt orgtime;
struct l_fixedpt rectime;
struct l_fixedpt xmttime;
} __packed;
struct ntp_query {
int fd;
struct ntp_msg msg;
double xmttime;
};
/*
* Leap Second Codes (high order two bits)
*/
#define LI_NOWARNING (0 << 6) /* no warning */
#define LI_PLUSSEC (1 << 6) /* add a second (61 seconds) */
#define LI_MINUSSEC (2 << 6) /* minus a second (59 seconds) */
#define LI_ALARM (3 << 6) /* alarm condition */
/*
* Status Masks
*/
#define MODEMASK (7 << 0)
#define VERSIONMASK (7 << 3)
#define LIMASK (3 << 6)
/*
* Mode values
*/
#define MODE_RES0 0 /* reserved */
#define MODE_SYM_ACT 1 /* symmetric active */
#define MODE_SYM_PAS 2 /* symmetric passive */
#define MODE_CLIENT 3 /* client */
#define MODE_SERVER 4 /* server */
#define MODE_BROADCAST 5 /* broadcast */
#define MODE_RES1 6 /* reserved for NTP control message */
#define MODE_RES2 7 /* reserved for private use */
#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */
/*
* The era we're in if we have no reason to assume otherwise.
* If lfp_to_d() sees an offset <= INT32_MAX the era is is assumed to be
* NTP_ERA + 1.
* Once the actual year is well into era 1, (after 2036) define NTP_ERA to 1
* and adapt (remove) the test in lfp_to_d().
* Once more than half of era 1 has elapsed (after 2104), re-inroduce the test
* to move to era 2 if offset <= INT32_MAX, repeat for each half era.
*/
#define NTP_ERA 0
#define SECS_IN_ERA (UINT32_MAX + 1ULL)
#define NTP_VERSION 4
#define NTP_MAXSTRATUM 15
#endif /* _NTP_H_ */

251
ntp_dns.c Normal file
View File

@ -0,0 +1,251 @@
/* $OpenBSD: ntp_dns.c,v 1.28 2023/04/19 12:58:16 jsg Exp $ */
/*
* Copyright (c) 2003-2008 Henning Brauer <henning@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <err.h>
#include <errno.h>
#include <poll.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "ntpd.h"
volatile sig_atomic_t quit_dns = 0;
static struct imsgbuf *ibuf_dns;
extern int non_numeric;
void sighdlr_dns(int);
int dns_dispatch_imsg(struct ntpd_conf *);
int probe_root_ns(void);
void probe_root(void);
void
sighdlr_dns(int sig)
{
switch (sig) {
case SIGTERM:
case SIGINT:
quit_dns = 1;
break;
}
}
void
ntp_dns(struct ntpd_conf *nconf, struct passwd *pw)
{
struct pollfd pfd[1];
int nfds, nullfd;
res_init();
if (setpriority(PRIO_PROCESS, 0, 0) == -1)
log_warn("could not set priority");
log_init(nconf->debug ? LOG_TO_STDERR : LOG_TO_SYSLOG, nconf->verbose,
LOG_DAEMON);
if (!nconf->debug && setsid() == -1)
fatal("setsid");
log_procinit("dns");
if ((nullfd = open("/dev/null", O_RDWR)) == -1)
fatal(NULL);
if (!nconf->debug) {
dup2(nullfd, STDIN_FILENO);
dup2(nullfd, STDOUT_FILENO);
dup2(nullfd, STDERR_FILENO);
}
close(nullfd);
setproctitle("dns engine");
if (setgroups(1, &pw->pw_gid) ||
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
fatal("can't drop privileges");
signal(SIGTERM, sighdlr_dns);
signal(SIGINT, sighdlr_dns);
signal(SIGHUP, SIG_IGN);
if ((ibuf_dns = malloc(sizeof(struct imsgbuf))) == NULL)
fatal(NULL);
imsg_init(ibuf_dns, PARENT_SOCK_FILENO);
if (pledge("stdio dns", NULL) == -1)
err(1, "pledge");
if (non_numeric)
probe_root();
else
log_debug("all addresses numeric, no dns probe");
while (quit_dns == 0) {
pfd[0].fd = ibuf_dns->fd;
pfd[0].events = POLLIN;
if (ibuf_dns->w.queued)
pfd[0].events |= POLLOUT;
if ((nfds = poll(pfd, 1, INFTIM)) == -1)
if (errno != EINTR) {
log_warn("poll error");
quit_dns = 1;
}
if (nfds > 0 && (pfd[0].revents & POLLOUT))
if (msgbuf_write(&ibuf_dns->w) <= 0 &&
errno != EAGAIN) {
log_warn("pipe write error (to ntp engine)");
quit_dns = 1;
}
if (nfds > 0 && pfd[0].revents & POLLIN) {
nfds--;
if (dns_dispatch_imsg(nconf) == -1)
quit_dns = 1;
}
}
msgbuf_clear(&ibuf_dns->w);
free(ibuf_dns);
exit(0);
}
int
dns_dispatch_imsg(struct ntpd_conf *nconf)
{
struct imsg imsg;
int n, cnt;
char *name;
struct ntp_addr *h, *hn;
struct ibuf *buf;
const char *str;
size_t len;
if (((n = imsg_read(ibuf_dns)) == -1 && errno != EAGAIN) || n == 0)
return (-1);
for (;;) {
if ((n = imsg_get(ibuf_dns, &imsg)) == -1)
return (-1);
if (n == 0)
break;
switch (imsg.hdr.type) {
case IMSG_HOST_DNS:
case IMSG_CONSTRAINT_DNS:
if (imsg.hdr.type == IMSG_HOST_DNS)
str = "IMSG_HOST_DNS";
else
str = "IMSG_CONSTRAINT_DNS";
name = imsg.data;
if (imsg.hdr.len < 1 + IMSG_HEADER_SIZE)
fatalx("invalid %s received", str);
len = imsg.hdr.len - 1 - IMSG_HEADER_SIZE;
if (name[len] != '\0' ||
strlen(name) != len)
fatalx("invalid %s received", str);
if ((cnt = host_dns(name, nconf->status.synced,
&hn)) == -1)
break;
buf = imsg_create(ibuf_dns, imsg.hdr.type,
imsg.hdr.peerid, 0,
cnt * (sizeof(struct sockaddr_storage) + sizeof(int)));
if (cnt > 0) {
if (buf) {
for (h = hn; h != NULL; h = h->next) {
if (imsg_add(buf, &h->ss,
sizeof(h->ss)) == -1) {
buf = NULL;
break;
}
if (imsg_add(buf, &h->notauth,
sizeof(int)) == -1) {
buf = NULL;
break;
}
}
}
host_dns_free(hn);
hn = NULL;
}
if (buf)
imsg_close(ibuf_dns, buf);
break;
case IMSG_SYNCED:
nconf->status.synced = 1;
break;
case IMSG_UNSYNCED:
nconf->status.synced = 0;
break;
default:
break;
}
imsg_free(&imsg);
}
return (0);
}
int
probe_root_ns(void)
{
int ret;
int old_retrans, old_retry, old_options;
unsigned char buf[4096];
old_retrans = _res.retrans;
old_retry = _res.retry;
old_options = _res.options;
_res.retrans = 1;
_res.retry = 1;
_res.options |= RES_USE_CD;
ret = res_query(".", C_IN, T_NS, buf, sizeof(buf));
_res.retrans = old_retrans;
_res.retry = old_retry;
_res.options = old_options;
return ret;
}
void
probe_root(void)
{
int n;
n = probe_root_ns();
if (n < 0) {
/* give programs like unwind a second chance */
sleep(1);
n = probe_root_ns();
}
if (imsg_compose(ibuf_dns, IMSG_PROBE_ROOT, 0, 0, -1, &n,
sizeof(int)) == -1)
fatalx("probe_root");
}

71
ntp_msg.c Normal file
View File

@ -0,0 +1,71 @@
/* $OpenBSD: ntp_msg.c,v 1.22 2016/09/03 11:52:06 reyk Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "ntpd.h"
int
ntp_getmsg(struct sockaddr *sa, char *p, ssize_t len, struct ntp_msg *msg)
{
if (len != NTP_MSGSIZE_NOAUTH && len != NTP_MSGSIZE) {
log_debug("malformed packet received from %s",
log_sockaddr(sa));
return (-1);
}
memcpy(msg, p, sizeof(*msg));
return (0);
}
int
ntp_sendmsg(int fd, struct sockaddr *sa, struct ntp_msg *msg)
{
socklen_t sa_len;
ssize_t n;
if (sa != NULL)
sa_len = SA_LEN(sa);
else
sa_len = 0;
n = sendto(fd, msg, sizeof(*msg), 0, sa, sa_len);
if (n == -1) {
if (errno == ENOBUFS || errno == EHOSTUNREACH ||
errno == ENETDOWN || errno == EHOSTDOWN) {
/* logging is futile */
return (-1);
}
log_warn("sendto");
return (-1);
}
if (n != sizeof(*msg)) {
log_warnx("ntp_sendmsg: only %zd of %zu bytes sent", n,
sizeof(*msg));
return (-1);
}
return (0);
}

79
ntpctl.8 Normal file
View File

@ -0,0 +1,79 @@
.\" $OpenBSD: ntpctl.8,v 1.9 2023/03/02 17:09:53 jmc Exp $
.\"
.\" Copyright (c) 2012 Mike Miller <mmiller@mgm51.com>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
.\" AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: March 2 2023 $
.Dt NTPCTL 8
.Os
.Sh NAME
.Nm ntpctl
.Nd control the NTP daemon
.Sh SYNOPSIS
.Nm ntpctl
.Fl s Cm all | peers | Sensors | status
.Sh DESCRIPTION
The
.Nm
program displays information about the running
.Xr ntpd 8
daemon.
.Pp
The options are as follows:
.Bl -tag -width "-s modifierX"
.It Fl s Cm all | peers | Sensors | status
Used to display information about the running daemon.
Keywords may be abbreviated.
.Pp
.Cm all
shows all data available.
.Pp
.Cm peers
shows the following information about each peer: weight, trustlevel,
stratum, number of seconds until the next poll, polling interval
in seconds, and offset, network delay and network jitter in milliseconds.
When the system clock is synced to a peer, an asterisk
is displayed to the left of the weight column for that peer.
.Pp
.Cm Sensors
shows the following information about each sensor: weight, sensor "good"
status, stratum, and offset and the configured correction in
milliseconds.
When the system clock is synced to a sensor, an asterisk
is displayed to the left of the weight column for that sensor.
.Pp
.Cm status
shows the status of peers and sensors, and whether the system clock is synced.
When the system clock is synced, the stratum is displayed.
When the system clock is not synced, the offset of the system clock,
as reported by the
.Xr adjtime 2
system call, is displayed.
When the median constraint is set, the offset to the local time is displayed.
.El
.Sh FILES
.Bl -tag -width "/var/run/ntpd.sockXXX" -compact
.It Pa /var/run/ntpd.sock
Socket file for communication with
.Xr ntpd 8 .
.El
.Sh SEE ALSO
.Xr adjtime 2 ,
.Xr ntpd.conf 5 ,
.Xr ntpd 8
.Sh HISTORY
The
.Nm
program first appeared in
.Ox 5.5 .

159
ntpd.8 Normal file
View File

@ -0,0 +1,159 @@
.\" $OpenBSD: ntpd.8,v 1.49 2023/03/02 17:09:53 jmc Exp $
.\"
.\" Copyright (c) 2003, 2004, 2006 Henning Brauer <henning@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
.\" AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: March 2 2023 $
.Dt NTPD 8
.Os
.Sh NAME
.Nm ntpd
.Nd Network Time Protocol (NTP) daemon
.Sh SYNOPSIS
.Nm ntpd
.Bk -words
.Op Fl dnv
.Op Fl f Ar file
.Ek
.Sh DESCRIPTION
The
.Nm
daemon synchronizes the local clock to one or more remote NTP servers
or local timedelta sensors.
.Nm
can also act as an NTP server itself,
redistributing the local time.
It implements the Simple Network Time Protocol version 4,
as described in RFC 5905,
and the Network Time Protocol version 3,
as described in RFC 1305.
Time can also be fetched from TLS HTTPS servers to reduce the
impact of unauthenticated NTP
man-in-the-middle attacks.
.Pp
The options are as follows:
.Bl -tag -width "-f fileXXX"
.It Fl d
Do not daemonize.
If this option is specified,
.Nm
will run in the foreground and log to
.Em stderr .
.It Fl f Ar file
Use
.Ar file
as the configuration file,
instead of the default
.Pa /etc/ntpd.conf .
.It Fl n
Configtest mode.
Only check the configuration file for validity.
.It Fl v
This option allows
.Nm
to send DEBUG priority messages to syslog.
.El
.Pp
.Nm
uses the
.Xr adjtime 2
system call to correct the local system time without causing time jumps.
Adjustments of 32ms and greater are logged using
.Xr syslog 3 .
The threshold value is chosen to avoid having local clock drift
thrash the log files.
Should
.Nm
be started with the
.Fl d
or
.Fl v
option, all calls to
.Xr adjtime 2
will be logged.
.Pp
At boot,
.Nm
will stay for a maximum of 15 seconds in the foreground and make efforts to
verify and correct the time if constraints are configured and
satisfied or if trusted servers or sensors return results,
and if the clock is not being moved backwards.
.Pp
After the local clock is synchronized,
.Nm
adjusts the clock frequency using the
.Xr adjfreq 2
system call to compensate for systematic drift.
.Pp
.Nm
is started at boot time by default via
.Va ntpd_flags
in
.Pa /etc/rc.conf .
See
.Xr rc 8
and
.Xr rc.conf 8
for more information on the boot process
and enabling daemons.
.Pp
When
.Nm
starts up, it reads settings from its configuration file,
typically
.Xr ntpd.conf 5 ,
and its initial clock drift from
.Pa /var/db/ntpd.drift .
Clock drift is periodically written to the drift file thereafter.
.Sh FILES
.Bl -tag -width "/var/db/ntpd.driftXXX" -compact
.It Pa /etc/ntpd.conf
Default configuration file.
.It Pa /var/db/ntpd.drift
Drift file.
.It Pa /var/run/ntpd.sock
Socket file for communication with
.Xr ntpctl 8 .
.El
.Sh SEE ALSO
.Xr date 1 ,
.Xr adjfreq 2 ,
.Xr adjtime 2 ,
.Xr ntpd.conf 5 ,
.Xr ntpctl 8 ,
.Xr rc 8 ,
.Xr rc.conf 8 ,
.Xr rdate 8
.Sh STANDARDS
.Rs
.%A David L. Mills
.%D March 1992
.%R RFC 1305
.%T Network Time Protocol (Version 3): Specification, Implementation and Analysis
.Re
.Pp
.Rs
.%A David L. Mills
.%A Jim Martin
.%A Jack Burbank
.%A William Kasch
.%D June 2010
.%R RFC 5905
.%T Network Time Protocol Version 4: Protocol and Algorithms Specification
.Re
.Sh HISTORY
The
.Nm
program first appeared in
.Ox 3.6 .

924
ntpd.c Normal file
View File

@ -0,0 +1,924 @@
/* $OpenBSD: ntpd.c,v 1.133 2024/05/21 05:00:48 jsg Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2012 Mike Miller <mmiller@mgm51.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/wait.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <errno.h>
#include <poll.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <tls.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <err.h>
#include "ntpd.h"
void sighdlr(int);
__dead void usage(void);
int auto_preconditions(const struct ntpd_conf *);
int main(int, char *[]);
void check_child(void);
int dispatch_imsg(struct ntpd_conf *, int, char **);
void reset_adjtime(void);
int ntpd_adjtime(double);
void ntpd_adjfreq(double, int);
void ntpd_settime(double);
void readfreq(void);
int writefreq(double);
void ctl_main(int, char*[]);
const char *ctl_lookup_option(char *, const char **);
void show_status_msg(struct imsg *);
void show_peer_msg(struct imsg *, int);
void show_sensor_msg(struct imsg *, int);
volatile sig_atomic_t quit = 0;
volatile sig_atomic_t reconfig = 0;
volatile sig_atomic_t sigchld = 0;
struct imsgbuf *ibuf;
int timeout = INFTIM;
extern u_int constraint_cnt;
const char *showopt;
static const char *ctl_showopt_list[] = {
"peers", "Sensors", "status", "all", NULL
};
void
sighdlr(int sig)
{
switch (sig) {
case SIGTERM:
case SIGINT:
quit = 1;
break;
case SIGCHLD:
sigchld = 1;
break;
case SIGHUP:
reconfig = 1;
break;
}
}
__dead void
usage(void)
{
extern char *__progname;
if (strcmp(__progname, "ntpctl") == 0)
fprintf(stderr,
"usage: ntpctl -s all | peers | Sensors | status\n");
else
fprintf(stderr, "usage: %s [-dnv] [-f file]\n",
__progname);
exit(1);
}
int
auto_preconditions(const struct ntpd_conf *cnf)
{
int mib[2] = { CTL_KERN, KERN_SECURELVL };
int constraints, securelevel;
size_t sz = sizeof(int);
if (sysctl(mib, 2, &securelevel, &sz, NULL, 0) == -1)
err(1, "sysctl");
constraints = !TAILQ_EMPTY(&cnf->constraints);
return !cnf->settime && (constraints || cnf->trusted_peers ||
conf->trusted_sensors) && securelevel == 0;
}
#define POLL_MAX 8
#define PFD_PIPE 0
#define PFD_MAX 1
int
main(int argc, char *argv[])
{
struct ntpd_conf lconf;
struct pollfd *pfd = NULL;
pid_t pid;
const char *conffile;
int ch, nfds, i, j;
int pipe_chld[2];
extern char *__progname;
u_int pfd_elms = 0, new_cnt;
struct constraint *cstr;
struct passwd *pw;
void *newp;
int argc0 = argc, logdest;
char **argv0 = argv;
char *pname = NULL;
time_t settime_deadline;
int sopt = 0;
if (strcmp(__progname, "ntpctl") == 0) {
ctl_main(argc, argv);
/* NOTREACHED */
}
conffile = CONFFILE;
memset(&lconf, 0, sizeof(lconf));
while ((ch = getopt(argc, argv, "df:nP:sSv")) != -1) {
switch (ch) {
case 'd':
lconf.debug = 1;
break;
case 'f':
conffile = optarg;
break;
case 'n':
lconf.debug = 1;
lconf.noaction = 1;
break;
case 'P':
pname = optarg;
break;
case 's':
case 'S':
sopt = ch;
break;
case 'v':
lconf.verbose++;
break;
default:
usage();
/* NOTREACHED */
}
}
/* log to stderr until daemonized */
logdest = LOG_TO_STDERR;
if (!lconf.debug)
logdest |= LOG_TO_SYSLOG;
log_init(logdest, lconf.verbose, LOG_DAEMON);
if (sopt) {
log_warnx("-%c option no longer works and will be removed soon.",
sopt);
log_warnx("Please reconfigure to use constraints or trusted servers.");
}
argc -= optind;
argv += optind;
if (argc > 0)
usage();
if (parse_config(conffile, &lconf))
exit(1);
if (lconf.noaction) {
fprintf(stderr, "configuration OK\n");
exit(0);
}
if (geteuid())
errx(1, "need root privileges");
if ((pw = getpwnam(NTPD_USER)) == NULL)
errx(1, "unknown user %s", NTPD_USER);
lconf.automatic = auto_preconditions(&lconf);
if (lconf.automatic)
lconf.settime = 1;
if (pname != NULL) {
/* Remove our proc arguments, so child doesn't need to. */
if (sanitize_argv(&argc0, &argv0) == -1)
fatalx("sanitize_argv");
if (strcmp(NTP_PROC_NAME, pname) == 0)
ntp_main(&lconf, pw, argc0, argv0);
else if (strcmp(NTPDNS_PROC_NAME, pname) == 0)
ntp_dns(&lconf, pw);
else if (strcmp(CONSTRAINT_PROC_NAME, pname) == 0)
priv_constraint_child(pw->pw_dir, pw->pw_uid,
pw->pw_gid);
else
fatalx("%s: invalid process name '%s'", __func__,
pname);
fatalx("%s: process '%s' failed", __func__, pname);
} else {
if ((control_check(CTLSOCKET)) == -1)
fatalx("ntpd already running");
}
if (setpriority(PRIO_PROCESS, 0, -20) == -1)
warn("can't set priority");
reset_adjtime();
logdest = lconf.debug ? LOG_TO_STDERR : LOG_TO_SYSLOG;
if (!lconf.settime) {
log_init(logdest, lconf.verbose, LOG_DAEMON);
if (!lconf.debug)
if (daemon(1, 0))
fatal("daemon");
} else {
settime_deadline = getmonotime();
timeout = 100;
}
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, PF_UNSPEC,
pipe_chld) == -1)
fatal("socketpair");
if (chdir("/") == -1)
fatal("chdir(\"/\")");
signal(SIGCHLD, sighdlr);
/* fork child process */
start_child(NTP_PROC_NAME, pipe_chld[1], argc0, argv0);
log_procinit("[priv]");
readfreq();
signal(SIGTERM, sighdlr);
signal(SIGINT, sighdlr);
signal(SIGHUP, sighdlr);
constraint_purge();
if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
fatal(NULL);
imsg_init(ibuf, pipe_chld[0]);
constraint_cnt = 0;
/*
* Constraint processes are forked with certificates in memory,
* then privdrop into chroot before speaking to the outside world.
*/
if (unveil("/usr/sbin/ntpd", "x") == -1)
err(1, "unveil /usr/sbin/ntpd");
if (pledge("stdio settime proc exec", NULL) == -1)
err(1, "pledge");
while (quit == 0) {
new_cnt = PFD_MAX + constraint_cnt;
if (new_cnt > pfd_elms) {
if ((newp = reallocarray(pfd, new_cnt,
sizeof(*pfd))) == NULL) {
/* panic for now */
log_warn("could not resize pfd from %u -> "
"%u entries", pfd_elms, new_cnt);
fatalx("exiting");
}
pfd = newp;
pfd_elms = new_cnt;
}
memset(pfd, 0, sizeof(*pfd) * pfd_elms);
pfd[PFD_PIPE].fd = ibuf->fd;
pfd[PFD_PIPE].events = POLLIN;
if (ibuf->w.queued)
pfd[PFD_PIPE].events |= POLLOUT;
i = PFD_MAX;
TAILQ_FOREACH(cstr, &conf->constraints, entry) {
pfd[i].fd = cstr->fd;
pfd[i].events = POLLIN;
i++;
}
if ((nfds = poll(pfd, i, timeout)) == -1)
if (errno != EINTR) {
log_warn("poll error");
quit = 1;
}
if (nfds == 0 && lconf.settime &&
getmonotime() > settime_deadline + SETTIME_TIMEOUT) {
lconf.settime = 0;
timeout = INFTIM;
log_init(logdest, lconf.verbose, LOG_DAEMON);
log_warnx("no reply received in time, skipping initial "
"time setting");
if (!lconf.debug)
if (daemon(1, 0))
fatal("daemon");
}
if (nfds > 0 && (pfd[PFD_PIPE].revents & POLLOUT))
if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) {
log_warn("pipe write error (to child)");
quit = 1;
}
if (nfds > 0 && pfd[PFD_PIPE].revents & POLLIN) {
nfds--;
if (dispatch_imsg(&lconf, argc0, argv0) == -1)
quit = 1;
}
for (j = PFD_MAX; nfds > 0 && j < i; j++) {
nfds -= priv_constraint_dispatch(&pfd[j]);
}
if (sigchld) {
check_child();
sigchld = 0;
}
}
signal(SIGCHLD, SIG_DFL);
/* Close socket and start shutdown. */
close(ibuf->fd);
do {
if ((pid = wait(NULL)) == -1 &&
errno != EINTR && errno != ECHILD)
fatal("wait");
} while (pid != -1 || (pid == -1 && errno == EINTR));
msgbuf_clear(&ibuf->w);
free(ibuf);
log_info("Terminating");
return (0);
}
void
check_child(void)
{
int status;
pid_t pid;
do {
pid = waitpid(WAIT_ANY, &status, WNOHANG);
if (pid <= 0)
continue;
priv_constraint_check_child(pid, status);
} while (pid > 0 || (pid == -1 && errno == EINTR));
}
int
dispatch_imsg(struct ntpd_conf *lconf, int argc, char **argv)
{
struct imsg imsg;
int n;
double d;
if (((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) || n == 0)
return (-1);
for (;;) {
if ((n = imsg_get(ibuf, &imsg)) == -1)
return (-1);
if (n == 0)
break;
switch (imsg.hdr.type) {
case IMSG_ADJTIME:
if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(d))
fatalx("invalid IMSG_ADJTIME received");
memcpy(&d, imsg.data, sizeof(d));
n = ntpd_adjtime(d);
imsg_compose(ibuf, IMSG_ADJTIME, 0, 0, -1,
&n, sizeof(n));
break;
case IMSG_ADJFREQ:
if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(d))
fatalx("invalid IMSG_ADJFREQ received");
memcpy(&d, imsg.data, sizeof(d));
ntpd_adjfreq(d, 1);
break;
case IMSG_SETTIME:
if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(d))
fatalx("invalid IMSG_SETTIME received");
if (!lconf->settime)
break;
log_init(lconf->debug ? LOG_TO_STDERR : LOG_TO_SYSLOG,
lconf->verbose, LOG_DAEMON);
memcpy(&d, imsg.data, sizeof(d));
ntpd_settime(d);
/* daemonize now */
if (!lconf->debug)
if (daemon(1, 0))
fatal("daemon");
lconf->settime = 0;
timeout = INFTIM;
break;
case IMSG_CONSTRAINT_QUERY:
priv_constraint_msg(imsg.hdr.peerid,
imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE,
argc, argv);
break;
case IMSG_CONSTRAINT_KILL:
priv_constraint_kill(imsg.hdr.peerid);
break;
default:
break;
}
imsg_free(&imsg);
}
return (0);
}
void
reset_adjtime(void)
{
struct timeval tv;
timerclear(&tv);
if (adjtime(&tv, NULL) == -1)
log_warn("reset adjtime failed");
}
int
ntpd_adjtime(double d)
{
struct timeval tv, olddelta;
int synced = 0;
static int firstadj = 1;
d += getoffset();
if (d >= (double)LOG_NEGLIGIBLE_ADJTIME / 1000 ||
d <= -1 * (double)LOG_NEGLIGIBLE_ADJTIME / 1000)
log_info("adjusting local clock by %fs", d);
else
log_debug("adjusting local clock by %fs", d);
d_to_tv(d, &tv);
if (adjtime(&tv, &olddelta) == -1)
log_warn("adjtime failed");
else if (!firstadj && olddelta.tv_sec == 0 && olddelta.tv_usec == 0)
synced = 1;
firstadj = 0;
return (synced);
}
void
ntpd_adjfreq(double relfreq, int wrlog)
{
int64_t curfreq;
double ppmfreq;
int r;
if (adjfreq(NULL, &curfreq) == -1) {
log_warn("adjfreq failed");
return;
}
/*
* adjfreq's unit is ns/s shifted left 32; convert relfreq to
* that unit before adding. We log values in part per million.
*/
curfreq += relfreq * 1e9 * (1LL << 32);
r = writefreq(curfreq / 1e9 / (1LL << 32));
ppmfreq = relfreq * 1e6;
if (wrlog) {
if (ppmfreq >= LOG_NEGLIGIBLE_ADJFREQ ||
ppmfreq <= -LOG_NEGLIGIBLE_ADJFREQ)
log_info("adjusting clock frequency by %f to %fppm%s",
ppmfreq, curfreq / 1e3 / (1LL << 32),
r ? "" : " (no drift file)");
else
log_debug("adjusting clock frequency by %f to %fppm%s",
ppmfreq, curfreq / 1e3 / (1LL << 32),
r ? "" : " (no drift file)");
}
if (adjfreq(&curfreq, NULL) == -1)
log_warn("adjfreq failed");
}
void
ntpd_settime(double d)
{
struct timeval tv, curtime;
char buf[80];
time_t tval;
if (d == 0)
return;
if (gettimeofday(&curtime, NULL) == -1) {
log_warn("gettimeofday");
return;
}
d_to_tv(d, &tv);
curtime.tv_usec += tv.tv_usec + 1000000;
curtime.tv_sec += tv.tv_sec - 1 + (curtime.tv_usec / 1000000);
curtime.tv_usec %= 1000000;
if (settimeofday(&curtime, NULL) == -1) {
log_warn("settimeofday");
return;
}
tval = curtime.tv_sec;
strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %Z %Y",
localtime(&tval));
log_info("set local clock to %s (offset %fs)", buf, d);
}
static FILE *freqfp;
void
readfreq(void)
{
int64_t current;
int fd;
double d;
fd = open(DRIFTFILE, O_RDWR);
if (fd == -1) {
log_warnx("creating new %s", DRIFTFILE);
current = 0;
if (adjfreq(&current, NULL) == -1)
log_warn("adjfreq reset failed");
freqfp = fopen(DRIFTFILE, "w");
return;
}
freqfp = fdopen(fd, "r+");
/* if we're adjusting frequency already, don't override */
if (adjfreq(NULL, &current) == -1)
log_warn("adjfreq failed");
else if (current == 0 && freqfp) {
if (fscanf(freqfp, "%lf", &d) == 1) {
d /= 1e6; /* scale from ppm */
ntpd_adjfreq(d, 0);
} else
log_warnx("%s is empty", DRIFTFILE);
}
}
int
writefreq(double d)
{
int r;
static int warnonce = 1;
if (freqfp == NULL)
return 0;
rewind(freqfp);
r = fprintf(freqfp, "%.3f\n", d * 1e6); /* scale to ppm */
if (r < 0 || fflush(freqfp) != 0) {
if (warnonce) {
log_warnx("can't write %s", DRIFTFILE);
warnonce = 0;
}
clearerr(freqfp);
return 0;
}
ftruncate(fileno(freqfp), ftello(freqfp));
fsync(fileno(freqfp));
return 1;
}
void
ctl_main(int argc, char *argv[])
{
struct sockaddr_un sa;
struct imsg imsg;
struct imsgbuf *ibuf_ctl;
int fd, n, done, ch, action;
char *sockname;
sockname = CTLSOCKET;
if (argc < 2) {
usage();
/* NOTREACHED */
}
while ((ch = getopt(argc, argv, "s:")) != -1) {
switch (ch) {
case 's':
showopt = ctl_lookup_option(optarg, ctl_showopt_list);
if (showopt == NULL) {
warnx("Unknown show modifier '%s'", optarg);
usage();
}
break;
default:
usage();
/* NOTREACHED */
}
}
action = -1;
if (showopt != NULL) {
switch (*showopt) {
case 'p':
action = CTL_SHOW_PEERS;
break;
case 's':
action = CTL_SHOW_STATUS;
break;
case 'S':
action = CTL_SHOW_SENSORS;
break;
case 'a':
action = CTL_SHOW_ALL;
break;
}
}
if (action == -1)
usage();
/* NOTREACHED */
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
err(1, "ntpctl: socket");
memset(&sa, 0, sizeof(sa));
sa.sun_family = AF_UNIX;
if (strlcpy(sa.sun_path, sockname, sizeof(sa.sun_path)) >=
sizeof(sa.sun_path))
errx(1, "ctl socket name too long");
if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1)
err(1, "connect: %s", sockname);
if (pledge("stdio", NULL) == -1)
err(1, "pledge");
if ((ibuf_ctl = malloc(sizeof(struct imsgbuf))) == NULL)
err(1, NULL);
imsg_init(ibuf_ctl, fd);
switch (action) {
case CTL_SHOW_STATUS:
imsg_compose(ibuf_ctl, IMSG_CTL_SHOW_STATUS,
0, 0, -1, NULL, 0);
break;
case CTL_SHOW_PEERS:
imsg_compose(ibuf_ctl, IMSG_CTL_SHOW_PEERS,
0, 0, -1, NULL, 0);
break;
case CTL_SHOW_SENSORS:
imsg_compose(ibuf_ctl, IMSG_CTL_SHOW_SENSORS,
0, 0, -1, NULL, 0);
break;
case CTL_SHOW_ALL:
imsg_compose(ibuf_ctl, IMSG_CTL_SHOW_ALL,
0, 0, -1, NULL, 0);
break;
default:
errx(1, "invalid action");
break; /* NOTREACHED */
}
while (ibuf_ctl->w.queued)
if (msgbuf_write(&ibuf_ctl->w) <= 0 && errno != EAGAIN)
err(1, "ibuf_ctl: msgbuf_write error");
done = 0;
while (!done) {
if ((n = imsg_read(ibuf_ctl)) == -1 && errno != EAGAIN)
err(1, "ibuf_ctl: imsg_read error");
if (n == 0)
errx(1, "ntpctl: pipe closed");
while (!done) {
if ((n = imsg_get(ibuf_ctl, &imsg)) == -1)
err(1, "ibuf_ctl: imsg_get error");
if (n == 0)
break;
switch (action) {
case CTL_SHOW_STATUS:
show_status_msg(&imsg);
done = 1;
break;
case CTL_SHOW_PEERS:
show_peer_msg(&imsg, 0);
if (imsg.hdr.type ==
IMSG_CTL_SHOW_PEERS_END)
done = 1;
break;
case CTL_SHOW_SENSORS:
show_sensor_msg(&imsg, 0);
if (imsg.hdr.type ==
IMSG_CTL_SHOW_SENSORS_END)
done = 1;
break;
case CTL_SHOW_ALL:
switch (imsg.hdr.type) {
case IMSG_CTL_SHOW_STATUS:
show_status_msg(&imsg);
break;
case IMSG_CTL_SHOW_PEERS:
show_peer_msg(&imsg, 1);
break;
case IMSG_CTL_SHOW_SENSORS:
show_sensor_msg(&imsg, 1);
break;
case IMSG_CTL_SHOW_PEERS_END:
case IMSG_CTL_SHOW_SENSORS_END:
/* do nothing */
break;
case IMSG_CTL_SHOW_ALL_END:
done=1;
break;
default:
/* no action taken */
break;
}
default:
/* no action taken */
break;
}
imsg_free(&imsg);
}
}
close(fd);
free(ibuf_ctl);
exit(0);
}
const char *
ctl_lookup_option(char *cmd, const char **list)
{
const char *item = NULL;
if (cmd != NULL && *cmd)
for (; *list; list++)
if (!strncmp(cmd, *list, strlen(cmd))) {
if (item == NULL)
item = *list;
else
errx(1, "%s is ambiguous", cmd);
}
return (item);
}
void
show_status_msg(struct imsg *imsg)
{
struct ctl_show_status *cstatus;
double clock_offset;
struct timeval tv;
if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(struct ctl_show_status))
fatalx("invalid IMSG_CTL_SHOW_STATUS received");
cstatus = (struct ctl_show_status *)imsg->data;
if (cstatus->peercnt > 0)
printf("%d/%d peers valid, ",
cstatus->valid_peers, cstatus->peercnt);
if (cstatus->sensorcnt > 0)
printf("%d/%d sensors valid, ",
cstatus->valid_sensors, cstatus->sensorcnt);
if (cstatus->constraint_median) {
tv.tv_sec = cstatus->constraint_median +
(getmonotime() - cstatus->constraint_last);
tv.tv_usec = 0;
d_to_tv(gettime_from_timeval(&tv) - gettime(), &tv);
printf("constraint offset %llds", (long long)tv.tv_sec);
if (cstatus->constraint_errors)
printf(" (%d errors)",
cstatus->constraint_errors);
printf(", ");
} else if (cstatus->constraints)
printf("constraints configured but none available, ");
if (cstatus->peercnt + cstatus->sensorcnt == 0)
printf("no peers and no sensors configured\n");
if (cstatus->synced == 1)
printf("clock synced, stratum %u\n", cstatus->stratum);
else {
printf("clock unsynced");
clock_offset = cstatus->clock_offset < 0 ?
-1.0 * cstatus->clock_offset : cstatus->clock_offset;
if (clock_offset > 5e-7)
printf(", clock offset is %.3fms\n",
cstatus->clock_offset);
else
printf("\n");
}
}
void
show_peer_msg(struct imsg *imsg, int calledfromshowall)
{
struct ctl_show_peer *cpeer;
int cnt;
char stratum[3];
static int firsttime = 1;
if (imsg->hdr.type == IMSG_CTL_SHOW_PEERS_END) {
if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(cnt))
fatalx("invalid IMSG_CTL_SHOW_PEERS_END received");
memcpy(&cnt, imsg->data, sizeof(cnt));
if (cnt == 0)
printf("no peers configured\n");
return;
}
if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(struct ctl_show_peer))
fatalx("invalid IMSG_CTL_SHOW_PEERS received");
cpeer = (struct ctl_show_peer *)imsg->data;
if (strlen(cpeer->peer_desc) > MAX_DISPLAY_WIDTH - 1)
fatalx("peer_desc is too long");
if (firsttime) {
firsttime = 0;
if (calledfromshowall)
printf("\n");
printf("peer\n wt tl st next poll "
"offset delay jitter\n");
}
if (cpeer->stratum > 0)
snprintf(stratum, sizeof(stratum), "%2u", cpeer->stratum);
else
strlcpy(stratum, " -", sizeof (stratum));
printf("%s\n %1s %2u %2u %2s %4llds %4llds",
cpeer->peer_desc, cpeer->syncedto == 1 ? "*" : " ",
cpeer->weight, cpeer->trustlevel, stratum,
(long long)cpeer->next, (long long)cpeer->poll);
if (cpeer->trustlevel >= TRUSTLEVEL_BADPEER)
printf(" %12.3fms %9.3fms %8.3fms\n", cpeer->offset,
cpeer->delay, cpeer->jitter);
else
printf(" ---- peer not valid ----\n");
}
void
show_sensor_msg(struct imsg *imsg, int calledfromshowall)
{
struct ctl_show_sensor *csensor;
int cnt;
static int firsttime = 1;
if (imsg->hdr.type == IMSG_CTL_SHOW_SENSORS_END) {
if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(cnt))
fatalx("invalid IMSG_CTL_SHOW_SENSORS_END received");
memcpy(&cnt, imsg->data, sizeof(cnt));
if (cnt == 0)
printf("no sensors configured\n");
return;
}
if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(struct ctl_show_sensor))
fatalx("invalid IMSG_CTL_SHOW_SENSORS received");
csensor = (struct ctl_show_sensor *)imsg->data;
if (strlen(csensor->sensor_desc) > MAX_DISPLAY_WIDTH - 1)
fatalx("sensor_desc is too long");
if (firsttime) {
firsttime = 0;
if (calledfromshowall)
printf("\n");
printf("sensor\n wt gd st next poll "
"offset correction\n");
}
printf("%s\n %1s %2u %2u %2u %4llds %4llds",
csensor->sensor_desc, csensor->syncedto == 1 ? "*" : " ",
csensor->weight, csensor->good, csensor->stratum,
(long long)csensor->next, (long long)csensor->poll);
if (csensor->good == 1)
printf(" %11.3fms %9.3fms\n",
csensor->offset, csensor->correction);
else
printf(" - sensor not valid -\n");
}

265
ntpd.conf.5 Normal file
View File

@ -0,0 +1,265 @@
.\" $OpenBSD: ntpd.conf.5,v 1.49 2023/03/02 17:09:53 jmc Exp $
.\"
.\" Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
.\" AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: March 2 2023 $
.Dt NTPD.CONF 5
.Os
.Sh NAME
.Nm ntpd.conf
.Nd NTP daemon configuration file
.Sh DESCRIPTION
This manual page describes the format of the
.Xr ntpd 8
configuration file.
.Pp
.Nm
has the following format:
.Pp
Empty lines and lines beginning with the
.Sq #
character are ignored.
.Pp
Keywords may be specified multiple times within the configuration file.
The basic configuration options are as follows:
.Bl -tag -width Ds
.It Xo Ic listen on Ar address
.Op Ic rtable Ar table-id
.Xc
Specify a local IP address or a hostname the
.Xr ntpd 8
daemon should listen on.
If it appears multiple times,
.Xr ntpd 8
will listen on each given address.
If
.Sq *
is given as an address,
.Xr ntpd 8
will listen on all local addresses using the specified routing table.
.Xr ntpd 8
does not listen on any address by default.
The optional
.Ic rtable
keyword will specify which routing table to listen on.
By default
.Xr ntpd 8
will listen using the current routing table.
For example:
.Bd -literal -offset indent
listen on *
.Ed
.Pp
or
.Bd -literal -offset indent
listen on 127.0.0.1
listen on ::1
listen on 127.0.0.1 rtable 4
.Ed
.It Ic query from Ar sourceaddr
Specify a local IP address the
.Xr ntpd 8
daemon should use for outgoing queries to subsequently specified servers,
which is useful on machines with multiple interfaces.
For example:
.Bd -literal -offset indent
query from 192.0.2.1
query from 2001:db8::1
.Ed
.It Xo Ic sensor Ar device
.Op Ic correction Ar microseconds
.Op Ic refid Ar ID-string
.Op Ic stratum Ar stratum-value
.Op Ic trusted
.Op Ic weight Ar weight-value
.Xc
Specify a timedelta sensor device
.Xr ntpd 8
should use.
The sensor can be specified multiple times:
.Xr ntpd 8
will use each given sensor that actually exists.
Non-existent sensors are ignored.
If
.Sq *
is given as device name,
.Xr ntpd 8
will use all timedelta sensors it finds.
.Xr ntpd 8
does not use any timedelta sensor by default.
For example:
.Bd -literal -offset indent
sensor *
sensor nmea0
.Ed
.Pp
A
.Ic correction
in microseconds can be given to compensate
for the sensor's offset.
The maximum correction is 127 seconds.
For example, if a DCF77 receiver is lagging 70ms behind
actual time:
.Bd -literal -offset indent
sensor udcf0 correction 70000
.Ed
.Pp
A
.Ic refid
.Ar ID-string
of up to 4 ASCII characters can be
given to publish the sensor type to clients.
RFC 2030 suggests some common reference identifiers, but new identifiers
"can be contrived as appropriate."
If an
.Ar ID-string
is not given,
.Xr ntpd 8
will use a generic reference ID.
For example:
.Bd -literal -offset indent
sensor nmea0 refid GPS
.Ed
.Pp
The
.Ic stratum
keyword can be used to change the stratum value from the default of 1.
.Pp
The
.Ic trusted
keyword indicates the time learned is secure, trustworthy,
and not vulnerable to man-in-the-middle attacks, so
.Ic constraints
validation is skipped.
This is useful for boot-time correction in environments where
.Ic constraints
cannot be used.
.Pp
The
.Ic weight
keyword permits finer control over the relative importance
of time sources (servers or sensor devices).
Weights are specified in the range 1 to 10;
if no weight is given,
the default is 1.
A server with a weight of 5, for example,
will have five times more influence on time offset calculation
than a server with a weight of 1.
.It Xo Ic server Ar address
.Op Ic trusted
.Op Ic weight Ar weight-value
.Xc
Specify the IP address or the hostname of an NTP
server to synchronize to.
If it appears multiple times,
.Xr ntpd 8
will try to synchronize to all of the servers specified.
If a hostname resolves to multiple IPv4 and/or IPv6 addresses,
.Xr ntpd 8
uses the first address.
If it does not get a reply,
.Xr ntpd 8
retries with the next address and continues to do so until a working address
is found.
For example:
.Bd -literal -offset indent
server 10.0.0.2 weight 5
server ntp.example.org weight 1
.Ed
.Pp
To provide redundancy, it is good practice to configure multiple servers.
In general, best accuracy is obtained by using servers that have a low
network latency.
.It Xo Ic servers Ar address
.Op Ic trusted
.Op Ic weight Ar weight-value
.Xc
As with
.Cm server ,
specify the IP address or hostname of an NTP server to synchronize to.
If it appears multiple times,
.Xr ntpd 8
will try to synchronize to all of the servers specified.
Should the hostname resolve to multiple IP addresses,
.Xr ntpd 8
will try to synchronize to all of them.
For example:
.Bd -literal -offset indent
servers pool.ntp.org
servers pool.ntp.org weight 5
.Ed
.El
.Sh CONSTRAINTS
.Xr ntpd 8
can be configured to query the
.Sq Date
from trusted HTTPS servers via TLS.
This time information is not used for precision but acts as an
authenticated constraint,
thereby reducing the impact of unauthenticated NTP
man-in-the-middle attacks.
Received NTP packets with time information falling outside of a range
near the constraint will be discarded and such NTP servers
will be marked as invalid.
.Bl -tag -width Ds
.It Ic constraint from Ar url [ip...]
Specify the URL, IP address or the hostname of an HTTPS server to
provide a constraint.
If the url is followed by one or more addresses, the url and addresses will be
tried until a working one is found.
The url path and expected certificate name is always taken from the
url specified.
If
.Ic constraint from
is used more than once,
.Xr ntpd 8
will calculate a median constraint from all the servers specified.
.Bd -literal -offset indent
server ntp.example.org
constraint from www.example.com
constraint from "https://9.9.9.9" "2620:fe::9"
.Ed
.It Ic constraints from Ar url
As with
.Ic constraint from ,
specify the URL, IP address or the hostname of an HTTPS server to
provide a constraint.
Should the hostname resolve to multiple IP addresses,
.Xr ntpd 8
will calculate a median constraint from all of them.
For example:
.Bd -literal -offset indent
servers pool.ntp.org
constraints from "https://www.google.com/"
.Ed
.El
.Sh FILES
.Bl -tag -width /etc/examples/ntpd.conf -compact
.It Pa /etc/ntpd.conf
Default
.Xr ntpd 8
configuration file.
.It Pa /etc/examples/ntpd.conf
Example configuration file.
.El
.Sh SEE ALSO
.Xr ntpctl 8 ,
.Xr ntpd 8 ,
.Xr sysctl 8
.Sh HISTORY
The
.Nm
file format first appeared in
.Ox 3.6 .

436
ntpd.h Normal file
View File

@ -0,0 +1,436 @@
/* $OpenBSD: ntpd.h,v 1.154 2024/05/21 05:00:48 jsg Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2012 Mike Miller <mmiller@mgm51.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pwd.h>
#include <stdarg.h>
#include <poll.h>
#include <imsg.h>
#include "ntp.h"
#include "log.h"
#define MAXIMUM(a, b) ((a) > (b) ? (a) : (b))
#define NTPD_USER "_ntp"
#define CONFFILE "/etc/ntpd.conf"
#define DRIFTFILE "/var/db/ntpd.drift"
#define CTLSOCKET "/var/run/ntpd.sock"
#define INTERVAL_QUERY_NORMAL 30 /* sync to peers every n secs */
#define INTERVAL_QUERY_PATHETIC 60
#define INTERVAL_QUERY_AGGRESSIVE 5
#define INTERVAL_QUERY_ULTRA_VIOLENCE 1 /* used at startup for auto */
#define TRUSTLEVEL_BADPEER 6
#define TRUSTLEVEL_PATHETIC 2
#define TRUSTLEVEL_AGGRESSIVE 8
#define TRUSTLEVEL_MAX 10
#define MAX_SERVERS_DNS 8
#define QSCALE_OFF_MIN 0.001
#define QSCALE_OFF_MAX 0.050
#define QUERYTIME_MAX 15 /* single query might take n secs max */
#define OFFSET_ARRAY_SIZE 8
#define SENSOR_OFFSETS 6
#define SETTIME_TIMEOUT 15 /* max seconds to wait with -s */
#define LOG_NEGLIGIBLE_ADJTIME 32 /* negligible drift to not log (ms) */
#define LOG_NEGLIGIBLE_ADJFREQ 0.05 /* negligible rate to not log (ppm) */
#define FREQUENCY_SAMPLES 8 /* samples for est. of permanent drift */
#define MAX_FREQUENCY_ADJUST 128e-5 /* max correction per iteration */
#define MAX_SEND_ERRORS 3 /* max send errors before reconnect */
#define MAX_DISPLAY_WIDTH 80 /* max chars in ctl_show report line */
#define FILTER_ADJFREQ 0x01 /* set after doing adjfreq */
#define AUTO_REPLIES 4 /* # of ntp replies we want for auto */
#define AUTO_THRESHOLD 60 /* dont bother auto setting < this */
#define INTERVAL_AUIO_DNSFAIL 1 /* DNS tmpfail interval for auto */
#define TRIES_AUTO_DNSFAIL 4 /* DNS tmpfail quick retries */
#define SENSOR_DATA_MAXAGE (15*60)
#define SENSOR_QUERY_INTERVAL 15
#define SENSOR_QUERY_INTERVAL_SETTIME (SETTIME_TIMEOUT/3)
#define SENSOR_SCAN_INTERVAL (1*60)
#define SENSOR_DEFAULT_REFID "HARD"
#define CONSTRAINT_ERROR_MARGIN (4)
#define CONSTRAINT_RETRY_INTERVAL (15)
#define CONSTRAINT_SCAN_INTERVAL (15*60)
#define CONSTRAINT_SCAN_TIMEOUT (10)
#define CONSTRAINT_MARGIN (2.0*60)
#define CONSTRAINT_PORT "443" /* HTTPS port */
#define CONSTRAINT_MAXHEADERLENGTH 8192
#define CONSTRAINT_PASSFD (STDERR_FILENO + 1)
#define PARENT_SOCK_FILENO CONSTRAINT_PASSFD
#define NTP_PROC_NAME "ntp_main"
#define NTPDNS_PROC_NAME "ntp_dns"
#define CONSTRAINT_PROC_NAME "constraint"
enum client_state {
STATE_NONE,
STATE_DNS_INPROGRESS,
STATE_DNS_TEMPFAIL,
STATE_DNS_DONE,
STATE_QUERY_SENT,
STATE_REPLY_RECEIVED,
STATE_TIMEOUT,
STATE_INVALID
};
struct listen_addr {
TAILQ_ENTRY(listen_addr) entry;
struct sockaddr_storage sa;
int fd;
int rtable;
};
struct ntp_addr {
struct ntp_addr *next;
struct sockaddr_storage ss;
int notauth;
};
struct ntp_addr_wrap {
char *name;
char *path;
struct ntp_addr *a;
u_int8_t pool;
};
struct ntp_addr_msg {
struct ntp_addr a;
size_t namelen;
size_t pathlen;
u_int8_t synced;
};
struct ntp_status {
double rootdelay;
double rootdispersion;
double reftime;
u_int32_t refid;
u_int32_t send_refid;
u_int8_t synced;
u_int8_t leap;
int8_t precision;
u_int8_t poll;
u_int8_t stratum;
};
struct ntp_offset {
struct ntp_status status;
double offset;
double delay;
double error;
time_t rcvd;
u_int8_t good;
};
struct ntp_peer {
TAILQ_ENTRY(ntp_peer) entry;
struct ntp_addr_wrap addr_head;
struct ntp_query query;
struct ntp_addr *addr;
struct ntp_offset reply[OFFSET_ARRAY_SIZE];
struct ntp_offset update;
struct sockaddr_in query_addr4;
struct sockaddr_in6 query_addr6;
enum client_state state;
time_t next;
time_t deadline;
time_t poll;
u_int32_t id;
u_int8_t shift;
u_int8_t trustlevel;
u_int8_t weight;
u_int8_t trusted;
int lasterror;
int senderrors;
};
struct ntp_sensor {
TAILQ_ENTRY(ntp_sensor) entry;
struct ntp_offset offsets[SENSOR_OFFSETS];
struct ntp_offset update;
time_t next;
time_t last;
char *device;
u_int32_t refid;
int sensordevid;
int correction;
u_int8_t stratum;
u_int8_t weight;
u_int8_t shift;
u_int8_t trusted;
};
struct constraint {
TAILQ_ENTRY(constraint) entry;
struct ntp_addr_wrap addr_head;
struct ntp_addr *addr;
int senderrors;
enum client_state state;
u_int32_t id;
int fd;
pid_t pid;
struct imsgbuf ibuf;
time_t last;
time_t constraint;
int dnstries;
};
struct ntp_conf_sensor {
TAILQ_ENTRY(ntp_conf_sensor) entry;
char *device;
char *refstr;
int correction;
u_int8_t stratum;
u_int8_t weight;
u_int8_t trusted;
};
struct ntp_freq {
double overall_offset;
double x, y;
double xx, xy;
int samples;
u_int num;
};
struct ntpd_conf {
TAILQ_HEAD(listen_addrs, listen_addr) listen_addrs;
TAILQ_HEAD(ntp_peers, ntp_peer) ntp_peers;
TAILQ_HEAD(ntp_sensors, ntp_sensor) ntp_sensors;
TAILQ_HEAD(ntp_conf_sensors, ntp_conf_sensor) ntp_conf_sensors;
TAILQ_HEAD(constraints, constraint) constraints;
struct ntp_status status;
struct ntp_freq freq;
struct sockaddr_in query_addr4;
struct sockaddr_in6 query_addr6;
u_int32_t scale;
int debug;
int verbose;
u_int8_t listen_all;
u_int8_t settime;
u_int8_t automatic;
u_int8_t noaction;
u_int8_t filters;
u_int8_t trusted_peers;
u_int8_t trusted_sensors;
time_t constraint_last;
time_t constraint_median;
u_int constraint_errors;
u_int8_t *ca;
size_t ca_len;
int tmpfail;
};
struct ctl_show_status {
time_t constraint_median;
time_t constraint_last;
double clock_offset;
u_int peercnt;
u_int sensorcnt;
u_int valid_peers;
u_int valid_sensors;
u_int constraint_errors;
u_int8_t synced;
u_int8_t stratum;
u_int8_t constraints;
};
struct ctl_show_peer {
char peer_desc[MAX_DISPLAY_WIDTH];
u_int8_t syncedto;
u_int8_t weight;
u_int8_t trustlevel;
u_int8_t stratum;
time_t next;
time_t poll;
double offset;
double delay;
double jitter;
};
struct ctl_show_sensor {
char sensor_desc[MAX_DISPLAY_WIDTH];
u_int8_t syncedto;
u_int8_t weight;
u_int8_t good;
u_int8_t stratum;
time_t next;
time_t poll;
double offset;
double correction;
};
struct ctl_conn {
TAILQ_ENTRY(ctl_conn) entry;
struct imsgbuf ibuf;
};
TAILQ_HEAD(ctl_conns, ctl_conn) ;
enum imsg_type {
IMSG_NONE,
IMSG_ADJTIME,
IMSG_ADJFREQ,
IMSG_SETTIME,
IMSG_HOST_DNS,
IMSG_CONSTRAINT_DNS,
IMSG_CONSTRAINT_QUERY,
IMSG_CONSTRAINT_RESULT,
IMSG_CONSTRAINT_CLOSE,
IMSG_CONSTRAINT_KILL,
IMSG_CTL_SHOW_STATUS,
IMSG_CTL_SHOW_PEERS,
IMSG_CTL_SHOW_PEERS_END,
IMSG_CTL_SHOW_SENSORS,
IMSG_CTL_SHOW_SENSORS_END,
IMSG_CTL_SHOW_ALL,
IMSG_CTL_SHOW_ALL_END,
IMSG_SYNCED,
IMSG_UNSYNCED,
IMSG_PROBE_ROOT
};
enum ctl_actions {
CTL_SHOW_STATUS,
CTL_SHOW_PEERS,
CTL_SHOW_SENSORS,
CTL_SHOW_ALL
};
/* prototypes */
/* ntp.c */
void ntp_main(struct ntpd_conf *, struct passwd *, int, char **);
void peer_addr_head_clear(struct ntp_peer *);
int priv_adjtime(void);
void priv_settime(double, char *);
void priv_dns(int, char *, u_int32_t);
int offset_compare(const void *, const void *);
void update_scale(double);
time_t scale_interval(time_t);
time_t error_interval(void);
extern struct ntpd_conf *conf;
extern struct ctl_conns ctl_conns;
#define SCALE_INTERVAL(x) MAXIMUM(5, (x) / 10)
/* parse.y */
int parse_config(const char *, struct ntpd_conf *);
/* config.c */
void host(const char *, struct ntp_addr **);
int host_dns(const char *, int, struct ntp_addr **);
void host_dns_free(struct ntp_addr *);
struct ntp_peer *new_peer(void);
struct ntp_conf_sensor *new_sensor(char *);
struct constraint *new_constraint(void);
/* ntp_msg.c */
int ntp_getmsg(struct sockaddr *, char *, ssize_t, struct ntp_msg *);
int ntp_sendmsg(int, struct sockaddr *, struct ntp_msg *);
/* server.c */
int setup_listeners(struct servent *, struct ntpd_conf *, u_int *);
int server_dispatch(int, struct ntpd_conf *);
/* client.c */
int client_peer_init(struct ntp_peer *);
int client_addr_init(struct ntp_peer *);
int client_nextaddr(struct ntp_peer *);
int client_query(struct ntp_peer *);
int client_dispatch(struct ntp_peer *, u_int8_t, u_int8_t);
void client_log_error(struct ntp_peer *, const char *, int);
void set_next(struct ntp_peer *, time_t);
/* constraint.c */
void constraint_add(struct constraint *);
void constraint_remove(struct constraint *);
void constraint_purge(void);
void constraint_reset(void);
int constraint_init(struct constraint *);
int constraint_query(struct constraint *, int);
int constraint_check(double);
void constraint_msg_dns(u_int32_t, u_int8_t *, size_t);
void constraint_msg_result(u_int32_t, u_int8_t *, size_t);
void constraint_msg_close(u_int32_t, u_int8_t *, size_t);
void priv_constraint_msg(u_int32_t, u_int8_t *, size_t, int, char **);
void priv_constraint_child(const char *, uid_t, gid_t);
void priv_constraint_kill(u_int32_t);
int priv_constraint_dispatch(struct pollfd *);
void priv_constraint_check_child(pid_t, int);
char *get_string(u_int8_t *, size_t);
/* util.c */
double gettime_corrected(void);
double gettime_from_timeval(struct timeval *);
double getoffset(void);
double gettime(void);
time_t getmonotime(void);
void d_to_tv(double, struct timeval *);
double lfp_to_d(struct l_fixedpt);
struct l_fixedpt d_to_lfp(double);
double sfp_to_d(struct s_fixedpt);
struct s_fixedpt d_to_sfp(double);
char *print_rtable(int);
const char *log_sockaddr(struct sockaddr *);
const char *log_ntp_addr(struct ntp_addr *);
pid_t start_child(char *, int, int, char **);
int sanitize_argv(int *, char ***);
/* sensors.c */
void sensor_init(void);
int sensor_scan(void);
void sensor_query(struct ntp_sensor *);
/* ntp_dns.c */
void ntp_dns(struct ntpd_conf *, struct passwd *);
/* control.c */
int control_check(char *);
int control_init(char *);
int control_listen(int);
void control_shutdown(int);
int control_accept(int);
struct ctl_conn *control_connbyfd(int);
int control_close(int);
int control_dispatch_msg(struct pollfd *, u_int *);
void session_socket_nonblockmode(int);
void build_show_status(struct ctl_show_status *);
void build_show_peer(struct ctl_show_peer *,
struct ntp_peer *);
void build_show_sensor(struct ctl_show_sensor *,
struct ntp_sensor *);

841
parse.y Normal file
View File

@ -0,0 +1,841 @@
/* $OpenBSD: parse.y,v 1.78 2021/10/15 15:01:28 naddy Exp $ */
/*
* Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2001 Markus Friedl. All rights reserved.
* Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
* Copyright (c) 2001 Theo de Raadt. All rights reserved.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
%{
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include "ntpd.h"
TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
static struct file {
TAILQ_ENTRY(file) entry;
FILE *stream;
char *name;
int lineno;
int errors;
} *file, *topfile;
struct file *pushfile(const char *);
int popfile(void);
int yyparse(void);
int yylex(void);
int yyerror(const char *, ...)
__attribute__((__format__ (printf, 1, 2)))
__attribute__((__nonnull__ (1)));
int kw_cmp(const void *, const void *);
int lookup(char *);
int lgetc(int);
int lungetc(int);
int findeol(void);
struct sockaddr_in query_addr4;
struct sockaddr_in6 query_addr6;
int poolseqnum;
struct opts {
int weight;
int correction;
int stratum;
int rtable;
int trusted;
char *refstr;
} opts;
void opts_default(void);
typedef struct {
union {
int64_t number;
char *string;
struct ntp_addr_wrap *addr;
struct opts opts;
} v;
int lineno;
} YYSTYPE;
%}
%token LISTEN ON CONSTRAINT CONSTRAINTS FROM QUERY TRUSTED
%token SERVER SERVERS SENSOR CORRECTION RTABLE REFID STRATUM WEIGHT
%token ERROR
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.addr> address url urllist
%type <v.opts> listen_opts listen_opts_l listen_opt
%type <v.opts> server_opts server_opts_l server_opt
%type <v.opts> sensor_opts sensor_opts_l sensor_opt
%type <v.opts> correction
%type <v.opts> rtable
%type <v.opts> refid
%type <v.opts> stratum
%type <v.opts> weight
%type <v.opts> trusted
%%
grammar : /* empty */
| grammar '\n'
| grammar main '\n'
| grammar error '\n' { file->errors++; }
;
main : LISTEN ON address listen_opts {
struct listen_addr *la;
struct ntp_addr *h, *next;
if ((h = $3->a) == NULL &&
(host_dns($3->name, 0, &h) == -1 || !h)) {
yyerror("could not resolve \"%s\"", $3->name);
free($3->name);
free($3);
YYERROR;
}
for (; h != NULL; h = next) {
next = h->next;
la = calloc(1, sizeof(struct listen_addr));
if (la == NULL)
fatal("listen on calloc");
la->fd = -1;
la->rtable = $4.rtable;
memcpy(&la->sa, &h->ss,
sizeof(struct sockaddr_storage));
TAILQ_INSERT_TAIL(&conf->listen_addrs, la,
entry);
free(h);
}
free($3->name);
free($3);
}
| QUERY FROM STRING {
struct sockaddr_in sin4;
struct sockaddr_in6 sin6;
memset(&sin4, 0, sizeof(sin4));
sin4.sin_family = AF_INET;
sin4.sin_len = sizeof(struct sockaddr_in);
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_len = sizeof(struct sockaddr_in6);
if (inet_pton(AF_INET, $3, &sin4.sin_addr) == 1)
memcpy(&query_addr4, &sin4, sin4.sin_len);
else if (inet_pton(AF_INET6, $3, &sin6.sin6_addr) == 1)
memcpy(&query_addr6, &sin6, sin6.sin6_len);
else {
yyerror("invalid IPv4 or IPv6 address: %s\n",
$3);
free($3);
YYERROR;
}
free($3);
}
| SERVERS address server_opts {
struct ntp_peer *p;
struct ntp_addr *h, *next;
h = $2->a;
do {
if (h != NULL) {
next = h->next;
if (h->ss.ss_family != AF_INET &&
h->ss.ss_family != AF_INET6) {
yyerror("IPv4 or IPv6 address "
"or hostname expected");
free(h);
free($2->name);
free($2);
YYERROR;
}
h->next = NULL;
} else
next = NULL;
p = new_peer();
p->weight = $3.weight;
p->trusted = $3.trusted;
conf->trusted_peers = conf->trusted_peers ||
$3.trusted;
p->query_addr4 = query_addr4;
p->query_addr6 = query_addr6;
p->addr = h;
p->addr_head.a = h;
p->addr_head.pool = ++poolseqnum;
p->addr_head.name = strdup($2->name);
if (p->addr_head.name == NULL)
fatal(NULL);
if (p->addr != NULL)
p->state = STATE_DNS_DONE;
TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry);
h = next;
} while (h != NULL);
free($2->name);
free($2);
}
| SERVER address server_opts {
struct ntp_peer *p;
struct ntp_addr *h, *next;
p = new_peer();
for (h = $2->a; h != NULL; h = next) {
next = h->next;
if (h->ss.ss_family != AF_INET &&
h->ss.ss_family != AF_INET6) {
yyerror("IPv4 or IPv6 address "
"or hostname expected");
free(h);
free(p);
free($2->name);
free($2);
YYERROR;
}
h->next = p->addr;
p->addr = h;
}
p->weight = $3.weight;
p->trusted = $3.trusted;
conf->trusted_peers = conf->trusted_peers ||
$3.trusted;
p->query_addr4 = query_addr4;
p->query_addr6 = query_addr6;
p->addr_head.a = p->addr;
p->addr_head.pool = 0;
p->addr_head.name = strdup($2->name);
if (p->addr_head.name == NULL)
fatal(NULL);
if (p->addr != NULL)
p->state = STATE_DNS_DONE;
TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry);
free($2->name);
free($2);
}
| CONSTRAINTS FROM url {
struct constraint *p;
struct ntp_addr *h, *next;
h = $3->a;
do {
if (h != NULL) {
next = h->next;
if (h->ss.ss_family != AF_INET &&
h->ss.ss_family != AF_INET6) {
yyerror("IPv4 or IPv6 address "
"or hostname expected");
free(h);
free($3->name);
free($3->path);
free($3);
YYERROR;
}
h->next = NULL;
} else
next = NULL;
p = new_constraint();
p->addr = h;
p->addr_head.a = h;
p->addr_head.pool = ++poolseqnum;
p->addr_head.name = strdup($3->name);
p->addr_head.path = strdup($3->path);
if (p->addr_head.name == NULL ||
p->addr_head.path == NULL)
fatal(NULL);
if (p->addr != NULL)
p->state = STATE_DNS_DONE;
constraint_add(p);
h = next;
} while (h != NULL);
free($3->name);
free($3);
}
| CONSTRAINT FROM urllist {
struct constraint *p;
struct ntp_addr *h, *next;
p = new_constraint();
for (h = $3->a; h != NULL; h = next) {
next = h->next;
if (h->ss.ss_family != AF_INET &&
h->ss.ss_family != AF_INET6) {
yyerror("IPv4 or IPv6 address "
"or hostname expected");
free(h);
free(p);
free($3->name);
free($3->path);
free($3);
YYERROR;
}
h->next = p->addr;
p->addr = h;
}
p->addr_head.a = p->addr;
p->addr_head.pool = 0;
p->addr_head.name = strdup($3->name);
p->addr_head.path = strdup($3->path);
if (p->addr_head.name == NULL ||
p->addr_head.path == NULL)
fatal(NULL);
if (p->addr != NULL)
p->state = STATE_DNS_DONE;
constraint_add(p);
free($3->name);
free($3);
}
| SENSOR STRING sensor_opts {
struct ntp_conf_sensor *s;
s = new_sensor($2);
s->weight = $3.weight;
s->correction = $3.correction;
s->refstr = $3.refstr;
s->stratum = $3.stratum;
s->trusted = $3.trusted;
conf->trusted_sensors = conf->trusted_sensors ||
$3.trusted;
free($2);
TAILQ_INSERT_TAIL(&conf->ntp_conf_sensors, s, entry);
}
;
address : STRING {
if (($$ = calloc(1, sizeof(struct ntp_addr_wrap))) ==
NULL)
fatal(NULL);
host($1, &$$->a);
$$->name = $1;
}
;
urllist : urllist address {
struct ntp_addr *p, *q = NULL;
struct in_addr ina;
struct in6_addr in6a;
if (inet_pton(AF_INET, $2->name, &ina) != 1 &&
inet_pton(AF_INET6, $2->name, &in6a) != 1) {
yyerror("url can only be followed by IP "
"addresses");
free($2->name);
free($2);
YYERROR;
}
p = $2->a;
while (p != NULL) {
q = p;
p = p->next;
}
if (q != NULL) {
q->next = $1->a;
$1->a = $2->a;
free($2);
}
$$ = $1;
}
| url {
$$ = $1;
}
;
url : STRING {
char *hname, *path;
if (($$ = calloc(1, sizeof(struct ntp_addr_wrap))) ==
NULL)
fatal("calloc");
if (strncmp("https://", $1,
strlen("https://")) != 0) {
host($1, &$$->a);
$$->name = $1;
} else {
hname = $1 + strlen("https://");
path = hname + strcspn(hname, "/\\");
if (*path != '\0') {
if (($$->path = strdup(path)) == NULL)
fatal("strdup");
*path = '\0';
}
host(hname, &$$->a);
if (($$->name = strdup(hname)) == NULL)
fatal("strdup");
}
if ($$->path == NULL &&
($$->path = strdup("/")) == NULL)
fatal("strdup");
}
;
listen_opts : { opts_default(); }
listen_opts_l
{ $$ = opts; }
| { opts_default(); $$ = opts; }
;
listen_opts_l : listen_opts_l listen_opt
| listen_opt
;
listen_opt : rtable
;
server_opts : { opts_default(); }
server_opts_l
{ $$ = opts; }
| { opts_default(); $$ = opts; }
;
server_opts_l : server_opts_l server_opt
| server_opt
;
server_opt : weight
| trusted
;
sensor_opts : { opts_default(); }
sensor_opts_l
{ $$ = opts; }
| { opts_default(); $$ = opts; }
;
sensor_opts_l : sensor_opts_l sensor_opt
| sensor_opt
;
sensor_opt : correction
| refid
| stratum
| weight
| trusted
;
correction : CORRECTION NUMBER {
if ($2 < -127000000 || $2 > 127000000) {
yyerror("correction must be between "
"-127000000 and 127000000 microseconds");
YYERROR;
}
opts.correction = $2;
}
;
refid : REFID STRING {
size_t l = strlen($2);
if (l < 1 || l > 4) {
yyerror("refid must be 1 to 4 characters");
free($2);
YYERROR;
}
opts.refstr = $2;
}
;
stratum : STRATUM NUMBER {
if ($2 < 1 || $2 > 15) {
yyerror("stratum must be between "
"1 and 15");
YYERROR;
}
opts.stratum = $2;
}
;
weight : WEIGHT NUMBER {
if ($2 < 1 || $2 > 10) {
yyerror("weight must be between 1 and 10");
YYERROR;
}
opts.weight = $2;
}
rtable : RTABLE NUMBER {
if ($2 < 0 || $2 > RT_TABLEID_MAX) {
yyerror("rtable must be between 1"
" and RT_TABLEID_MAX");
YYERROR;
}
opts.rtable = $2;
}
;
trusted : TRUSTED {
opts.trusted = 1;
}
%%
void
opts_default(void)
{
memset(&opts, 0, sizeof opts);
opts.weight = 1;
opts.stratum = 1;
}
struct keywords {
const char *k_name;
int k_val;
};
int
yyerror(const char *fmt, ...)
{
va_list ap;
char *msg;
file->errors++;
va_start(ap, fmt);
if (vasprintf(&msg, fmt, ap) == -1)
fatalx("yyerror vasprintf");
va_end(ap);
log_warnx("%s:%d: %s", file->name, yylval.lineno, msg);
free(msg);
return (0);
}
int
kw_cmp(const void *k, const void *e)
{
return (strcmp(k, ((const struct keywords *)e)->k_name));
}
int
lookup(char *s)
{
/* this has to be sorted always */
static const struct keywords keywords[] = {
{ "constraint", CONSTRAINT},
{ "constraints", CONSTRAINTS},
{ "correction", CORRECTION},
{ "from", FROM},
{ "listen", LISTEN},
{ "on", ON},
{ "query", QUERY},
{ "refid", REFID},
{ "rtable", RTABLE},
{ "sensor", SENSOR},
{ "server", SERVER},
{ "servers", SERVERS},
{ "stratum", STRATUM},
{ "trusted", TRUSTED},
{ "weight", WEIGHT}
};
const struct keywords *p;
p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
sizeof(keywords[0]), kw_cmp);
if (p)
return (p->k_val);
else
return (STRING);
}
#define MAXPUSHBACK 128
char *parsebuf;
int parseindex;
char pushback_buffer[MAXPUSHBACK];
int pushback_index = 0;
int
lgetc(int quotec)
{
int c, next;
if (parsebuf) {
/* Read character from the parsebuffer instead of input. */
if (parseindex >= 0) {
c = (unsigned char)parsebuf[parseindex++];
if (c != '\0')
return (c);
parsebuf = NULL;
} else
parseindex++;
}
if (pushback_index)
return ((unsigned char)pushback_buffer[--pushback_index]);
if (quotec) {
if ((c = getc(file->stream)) == EOF) {
yyerror("reached end of file while parsing "
"quoted string");
if (file == topfile || popfile() == EOF)
return (EOF);
return (quotec);
}
return (c);
}
while ((c = getc(file->stream)) == '\\') {
next = getc(file->stream);
if (next != '\n') {
c = next;
break;
}
yylval.lineno = file->lineno;
file->lineno++;
}
while (c == EOF) {
if (file == topfile || popfile() == EOF)
return (EOF);
c = getc(file->stream);
}
return (c);
}
int
lungetc(int c)
{
if (c == EOF)
return (EOF);
if (parsebuf) {
parseindex--;
if (parseindex >= 0)
return (c);
}
if (pushback_index + 1 >= MAXPUSHBACK)
return (EOF);
pushback_buffer[pushback_index++] = c;
return (c);
}
int
findeol(void)
{
int c;
parsebuf = NULL;
/* skip to either EOF or the first real EOL */
while (1) {
if (pushback_index)
c = (unsigned char)pushback_buffer[--pushback_index];
else
c = lgetc(0);
if (c == '\n') {
file->lineno++;
break;
}
if (c == EOF)
break;
}
return (ERROR);
}
int
yylex(void)
{
char buf[8096];
char *p;
int quotec, next, c;
int token;
p = buf;
while ((c = lgetc(0)) == ' ' || c == '\t')
; /* nothing */
yylval.lineno = file->lineno;
if (c == '#')
while ((c = lgetc(0)) != '\n' && c != EOF)
; /* nothing */
switch (c) {
case '\'':
case '"':
quotec = c;
while (1) {
if ((c = lgetc(quotec)) == EOF)
return (0);
if (c == '\n') {
file->lineno++;
continue;
} else if (c == '\\') {
if ((next = lgetc(quotec)) == EOF)
return (0);
if (next == quotec || next == ' ' ||
next == '\t')
c = next;
else if (next == '\n') {
file->lineno++;
continue;
} else
lungetc(next);
} else if (c == quotec) {
*p = '\0';
break;
} else if (c == '\0') {
yyerror("syntax error");
return (findeol());
}
if (p + 1 >= buf + sizeof(buf) - 1) {
yyerror("string too long");
return (findeol());
}
*p++ = c;
}
yylval.v.string = strdup(buf);
if (yylval.v.string == NULL)
fatal("yylex: strdup");
return (STRING);
}
#define allowed_to_end_number(x) \
(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
if (c == '-' || isdigit(c)) {
do {
*p++ = c;
if ((size_t)(p-buf) >= sizeof(buf)) {
yyerror("string too long");
return (findeol());
}
} while ((c = lgetc(0)) != EOF && isdigit(c));
lungetc(c);
if (p == buf + 1 && buf[0] == '-')
goto nodigits;
if (c == EOF || allowed_to_end_number(c)) {
const char *errstr = NULL;
*p = '\0';
yylval.v.number = strtonum(buf, LLONG_MIN,
LLONG_MAX, &errstr);
if (errstr) {
yyerror("\"%s\" invalid number: %s",
buf, errstr);
return (findeol());
}
return (NUMBER);
} else {
nodigits:
while (p > buf + 1)
lungetc((unsigned char)*--p);
c = (unsigned char)*--p;
if (c == '-')
return (c);
}
}
#define allowed_in_string(x) \
(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
x != '{' && x != '}' && x != '<' && x != '>' && \
x != '!' && x != '=' && x != '/' && x != '#' && \
x != ','))
if (isalnum(c) || c == ':' || c == '_' || c == '*') {
do {
*p++ = c;
if ((size_t)(p-buf) >= sizeof(buf)) {
yyerror("string too long");
return (findeol());
}
} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
lungetc(c);
*p = '\0';
if ((token = lookup(buf)) == STRING)
if ((yylval.v.string = strdup(buf)) == NULL)
fatal("yylex: strdup");
return (token);
}
if (c == '\n') {
yylval.lineno = file->lineno;
file->lineno++;
}
if (c == EOF)
return (0);
return (c);
}
struct file *
pushfile(const char *name)
{
struct file *nfile;
if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
log_warn("%s", __func__);
return (NULL);
}
if ((nfile->name = strdup(name)) == NULL) {
log_warn("%s", __func__);
free(nfile);
return (NULL);
}
if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
log_warn("%s: %s", __func__, nfile->name);
free(nfile->name);
free(nfile);
return (NULL);
}
nfile->lineno = 1;
TAILQ_INSERT_TAIL(&files, nfile, entry);
return (nfile);
}
int
popfile(void)
{
struct file *prev;
if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
prev->errors += file->errors;
TAILQ_REMOVE(&files, file, entry);
fclose(file->stream);
free(file->name);
free(file);
file = prev;
return (file ? 0 : EOF);
}
int
parse_config(const char *filename, struct ntpd_conf *xconf)
{
int errors = 0;
conf = xconf;
TAILQ_INIT(&conf->listen_addrs);
TAILQ_INIT(&conf->ntp_peers);
TAILQ_INIT(&conf->ntp_conf_sensors);
TAILQ_INIT(&conf->constraints);
if ((file = pushfile(filename)) == NULL) {
return (-1);
}
topfile = file;
yyparse();
errors = file->errors;
popfile();
return (errors ? -1 : 0);
}

265
sensors.c Normal file
View File

@ -0,0 +1,265 @@
/* $OpenBSD: sensors.c,v 1.54 2019/11/11 06:32:52 otto Exp $ */
/*
* Copyright (c) 2006 Henning Brauer <henning@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <sys/sensors.h>
#include <sys/sysctl.h>
#include <sys/device.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "ntpd.h"
#define MAXDEVNAMLEN 16
int sensor_probe(int, char *, struct sensor *);
void sensor_add(int, char *);
void sensor_remove(struct ntp_sensor *);
void sensor_update(struct ntp_sensor *);
void
sensor_init(void)
{
TAILQ_INIT(&conf->ntp_sensors);
}
int
sensor_scan(void)
{
int i, n, err;
char d[MAXDEVNAMLEN];
struct sensor s;
n = 0;
for (i = 0; ; i++)
if ((err = sensor_probe(i, d, &s))) {
if (err == 0)
continue;
if (err == -1) /* no further sensors */
break;
sensor_add(i, d);
n++;
}
return n;
}
/*
* 1 = time sensor!
* 0 = sensor exists... but is not a time sensor
* -1: no sensor here, and no further sensors after this
*/
int
sensor_probe(int devid, char *dxname, struct sensor *sensor)
{
int mib[5];
size_t slen, sdlen;
struct sensordev sensordev;
mib[0] = CTL_HW;
mib[1] = HW_SENSORS;
mib[2] = devid;
mib[3] = SENSOR_TIMEDELTA;
mib[4] = 0;
sdlen = sizeof(sensordev);
if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
if (errno == ENXIO)
return (0);
if (errno == ENOENT)
return (-1);
log_warn("sensor_probe sysctl");
}
if (sensordev.maxnumt[SENSOR_TIMEDELTA] == 0)
return (0);
strlcpy(dxname, sensordev.xname, MAXDEVNAMLEN);
slen = sizeof(*sensor);
if (sysctl(mib, 5, sensor, &slen, NULL, 0) == -1) {
if (errno != ENOENT)
log_warn("sensor_probe sysctl");
return (0);
}
return (1);
}
void
sensor_add(int sensordev, char *dxname)
{
struct ntp_sensor *s;
struct ntp_conf_sensor *cs;
/* check whether it is already there */
TAILQ_FOREACH(s, &conf->ntp_sensors, entry)
if (!strcmp(s->device, dxname))
return;
/* check whether it is requested in the config file */
for (cs = TAILQ_FIRST(&conf->ntp_conf_sensors); cs != NULL &&
strcmp(cs->device, dxname) && strcmp(cs->device, "*");
cs = TAILQ_NEXT(cs, entry))
; /* nothing */
if (cs == NULL)
return;
if ((s = calloc(1, sizeof(*s))) == NULL)
fatal("sensor_add calloc");
s->next = getmonotime();
s->weight = cs->weight;
s->correction = cs->correction;
s->stratum = cs->stratum - 1;
s->trusted = cs->trusted;
if ((s->device = strdup(dxname)) == NULL)
fatal("sensor_add strdup");
s->sensordevid = sensordev;
if (cs->refstr == NULL)
memcpy(&s->refid, SENSOR_DEFAULT_REFID, sizeof(s->refid));
else {
s->refid = 0;
strncpy((char *)&s->refid, cs->refstr, sizeof(s->refid));
}
TAILQ_INSERT_TAIL(&conf->ntp_sensors, s, entry);
log_debug("sensor %s added (weight %d, correction %.6f, refstr %.4u, "
"stratum %d)", s->device, s->weight, s->correction / 1e6,
s->refid, s->stratum);
}
void
sensor_remove(struct ntp_sensor *s)
{
TAILQ_REMOVE(&conf->ntp_sensors, s, entry);
free(s->device);
free(s);
}
void
sensor_query(struct ntp_sensor *s)
{
char dxname[MAXDEVNAMLEN];
struct sensor sensor;
double sens_time;
if (conf->settime)
s->next = getmonotime() + SENSOR_QUERY_INTERVAL_SETTIME;
else
s->next = getmonotime() + SENSOR_QUERY_INTERVAL;
/* rcvd is walltime here, monotime in client.c. not used elsewhere */
if (s->update.rcvd < time(NULL) - SENSOR_DATA_MAXAGE)
s->update.good = 0;
if (!sensor_probe(s->sensordevid, dxname, &sensor)) {
sensor_remove(s);
return;
}
if (sensor.flags & SENSOR_FINVALID ||
sensor.status != SENSOR_S_OK)
return;
if (strcmp(dxname, s->device)) {
sensor_remove(s);
return;
}
if (sensor.tv.tv_sec == s->last) /* already seen */
return;
s->last = sensor.tv.tv_sec;
if (!s->trusted && !TAILQ_EMPTY(&conf->constraints)) {
if (conf->constraint_median == 0) {
return;
}
sens_time = gettime() + (sensor.value / -1e9) +
(s->correction / 1e6);
if (constraint_check(sens_time) != 0) {
log_info("sensor %s: constraint check failed", s->device);
return;
}
}
/*
* TD = device time
* TS = system time
* sensor.value = TS - TD in ns
* if value is positive, system time is ahead
*/
s->offsets[s->shift].offset = (sensor.value / -1e9) - getoffset() +
(s->correction / 1e6);
s->offsets[s->shift].rcvd = sensor.tv.tv_sec;
s->offsets[s->shift].good = 1;
s->offsets[s->shift].status.send_refid = s->refid;
/* stratum increased when sent out */
s->offsets[s->shift].status.stratum = s->stratum;
s->offsets[s->shift].status.rootdelay = 0;
s->offsets[s->shift].status.rootdispersion = 0;
s->offsets[s->shift].status.reftime = sensor.tv.tv_sec;
s->offsets[s->shift].status.synced = 1;
log_debug("sensor %s: offset %f", s->device,
s->offsets[s->shift].offset);
if (++s->shift >= SENSOR_OFFSETS) {
s->shift = 0;
sensor_update(s);
}
}
void
sensor_update(struct ntp_sensor *s)
{
struct ntp_offset **offsets;
int i;
if ((offsets = calloc(SENSOR_OFFSETS, sizeof(struct ntp_offset *))) ==
NULL)
fatal("calloc sensor_update");
for (i = 0; i < SENSOR_OFFSETS; i++)
offsets[i] = &s->offsets[i];
qsort(offsets, SENSOR_OFFSETS, sizeof(struct ntp_offset *),
offset_compare);
i = SENSOR_OFFSETS / 2;
memcpy(&s->update, offsets[i], sizeof(s->update));
if (SENSOR_OFFSETS % 2 == 0) {
s->update.offset =
(offsets[i - 1]->offset + offsets[i]->offset) / 2;
}
free(offsets);
log_debug("sensor update %s: offset %f", s->device, s->update.offset);
priv_adjtime();
}

203
server.c Normal file
View File

@ -0,0 +1,203 @@
/* $OpenBSD: server.c,v 1.44 2016/09/03 11:52:06 reyk Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2004 Alexander Guy <alexander@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <errno.h>
#include <ifaddrs.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "ntpd.h"
int
setup_listeners(struct servent *se, struct ntpd_conf *lconf, u_int *cnt)
{
struct listen_addr *la, *nla, *lap;
struct ifaddrs *ifa, *ifap;
struct sockaddr *sa;
struct if_data *ifd;
u_int8_t *a6;
size_t sa6len = sizeof(struct in6_addr);
u_int new_cnt = 0;
int tos = IPTOS_LOWDELAY, rdomain = 0;
TAILQ_FOREACH(lap, &lconf->listen_addrs, entry) {
switch (lap->sa.ss_family) {
case AF_UNSPEC:
if (getifaddrs(&ifa) == -1)
fatal("getifaddrs");
for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
sa = ifap->ifa_addr;
if (sa == NULL || SA_LEN(sa) == 0)
continue;
if (sa->sa_family == AF_LINK) {
ifd = ifap->ifa_data;
rdomain = ifd->ifi_rdomain;
}
if (sa->sa_family != AF_INET &&
sa->sa_family != AF_INET6)
continue;
if (lap->rtable != -1 && rdomain != lap->rtable)
continue;
if (sa->sa_family == AF_INET &&
((struct sockaddr_in *)sa)->sin_addr.s_addr ==
INADDR_ANY)
continue;
if (sa->sa_family == AF_INET6) {
a6 = ((struct sockaddr_in6 *)sa)->
sin6_addr.s6_addr;
if (memcmp(a6, &in6addr_any, sa6len) == 0)
continue;
}
if ((la = calloc(1, sizeof(struct listen_addr))) ==
NULL)
fatal("setup_listeners calloc");
memcpy(&la->sa, sa, SA_LEN(sa));
la->rtable = rdomain;
TAILQ_INSERT_TAIL(&lconf->listen_addrs, la, entry);
}
freeifaddrs(ifa);
default:
continue;
}
}
for (la = TAILQ_FIRST(&lconf->listen_addrs); la; ) {
switch (la->sa.ss_family) {
case AF_INET:
if (((struct sockaddr_in *)&la->sa)->sin_port == 0)
((struct sockaddr_in *)&la->sa)->sin_port =
se->s_port;
break;
case AF_INET6:
if (((struct sockaddr_in6 *)&la->sa)->sin6_port == 0)
((struct sockaddr_in6 *)&la->sa)->sin6_port =
se->s_port;
break;
case AF_UNSPEC:
nla = TAILQ_NEXT(la, entry);
TAILQ_REMOVE(&lconf->listen_addrs, la, entry);
free(la);
la = nla;
continue;
default:
fatalx("king bula sez: af borked");
}
log_info("listening on %s %s",
log_sockaddr((struct sockaddr *)&la->sa),
print_rtable(la->rtable));
if ((la->fd = socket(la->sa.ss_family, SOCK_DGRAM, 0)) == -1)
fatal("socket");
if (la->sa.ss_family == AF_INET && setsockopt(la->fd,
IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1)
log_warn("setsockopt IPTOS_LOWDELAY");
if (la->rtable != -1 &&
setsockopt(la->fd, SOL_SOCKET, SO_RTABLE, &la->rtable,
sizeof(la->rtable)) == -1)
fatal("setup_listeners setsockopt SO_RTABLE");
if (bind(la->fd, (struct sockaddr *)&la->sa,
SA_LEN((struct sockaddr *)&la->sa)) == -1) {
log_warn("bind on %s failed, skipping",
log_sockaddr((struct sockaddr *)&la->sa));
close(la->fd);
nla = TAILQ_NEXT(la, entry);
TAILQ_REMOVE(&lconf->listen_addrs, la, entry);
free(la);
la = nla;
continue;
}
new_cnt++;
la = TAILQ_NEXT(la, entry);
}
*cnt = new_cnt;
return (0);
}
int
server_dispatch(int fd, struct ntpd_conf *lconf)
{
ssize_t size;
double rectime;
struct sockaddr_storage fsa;
socklen_t fsa_len;
struct ntp_msg query, reply;
char buf[NTP_MSGSIZE];
fsa_len = sizeof(fsa);
if ((size = recvfrom(fd, &buf, sizeof(buf), 0,
(struct sockaddr *)&fsa, &fsa_len)) == -1) {
if (errno == EHOSTUNREACH || errno == EHOSTDOWN ||
errno == ENETUNREACH || errno == ENETDOWN) {
log_warn("recvfrom %s",
log_sockaddr((struct sockaddr *)&fsa));
return (0);
} else
fatal("recvfrom");
}
rectime = gettime_corrected();
if (ntp_getmsg((struct sockaddr *)&fsa, buf, size, &query) == -1)
return (0);
memset(&reply, 0, sizeof(reply));
if (lconf->status.synced)
reply.status = lconf->status.leap;
else
reply.status = LI_ALARM;
reply.status |= (query.status & VERSIONMASK);
if ((query.status & MODEMASK) == MODE_CLIENT)
reply.status |= MODE_SERVER;
else if ((query.status & MODEMASK) == MODE_SYM_ACT)
reply.status |= MODE_SYM_PAS;
else /* ignore packets of different type (e.g. bcast) */
return (0);
reply.stratum = lconf->status.stratum;
reply.ppoll = query.ppoll;
reply.precision = lconf->status.precision;
reply.rectime = d_to_lfp(rectime);
reply.reftime = d_to_lfp(lconf->status.reftime);
reply.xmttime = d_to_lfp(gettime_corrected());
reply.orgtime = query.xmttime;
reply.rootdelay = d_to_sfp(lconf->status.rootdelay);
reply.refid = lconf->status.refid;
ntp_sendmsg(fd, (struct sockaddr *)&fsa, &reply);
return (0);
}

253
util.c Normal file
View File

@ -0,0 +1,253 @@
/* $OpenBSD: util.c,v 1.28 2023/12/20 15:36:36 otto Exp $ */
/*
* Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <fcntl.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "ntpd.h"
double
gettime_corrected(void)
{
return (gettime() + getoffset());
}
double
getoffset(void)
{
struct timeval tv;
if (adjtime(NULL, &tv) == -1)
return (0.0);
return (tv.tv_sec + 1.0e-6 * tv.tv_usec);
}
double
gettime(void)
{
struct timeval tv;
if (gettimeofday(&tv, NULL) == -1)
fatal("gettimeofday");
return (gettime_from_timeval(&tv));
}
double
gettime_from_timeval(struct timeval *tv)
{
/*
* Account for overflow on OSes that have a 32-bit time_t.
*/
return ((uint64_t)tv->tv_sec + JAN_1970 + 1.0e-6 * tv->tv_usec);
}
time_t
getmonotime(void)
{
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
fatal("clock_gettime");
return (ts.tv_sec);
}
void
d_to_tv(double d, struct timeval *tv)
{
tv->tv_sec = d;
tv->tv_usec = (d - tv->tv_sec) * 1000000;
while (tv->tv_usec < 0) {
tv->tv_usec += 1000000;
tv->tv_sec -= 1;
}
}
double
lfp_to_d(struct l_fixedpt lfp)
{
double base, ret;
lfp.int_partl = ntohl(lfp.int_partl);
lfp.fractionl = ntohl(lfp.fractionl);
/* see comment in ntp.h */
base = NTP_ERA;
if (lfp.int_partl <= INT32_MAX)
base++;
ret = base * SECS_IN_ERA;
ret += (double)(lfp.int_partl) + ((double)lfp.fractionl / L_DENOMINATOR);
return (ret);
}
struct l_fixedpt
d_to_lfp(double d)
{
struct l_fixedpt lfp;
while (d > SECS_IN_ERA)
d -= SECS_IN_ERA;
lfp.int_partl = htonl((u_int32_t)d);
lfp.fractionl = htonl((u_int32_t)((d - (u_int32_t)d) * L_DENOMINATOR));
return (lfp);
}
double
sfp_to_d(struct s_fixedpt sfp)
{
double ret;
sfp.int_parts = ntohs(sfp.int_parts);
sfp.fractions = ntohs(sfp.fractions);
ret = (double)(sfp.int_parts) + ((double)sfp.fractions / S_DENOMINATOR);
return (ret);
}
struct s_fixedpt
d_to_sfp(double d)
{
struct s_fixedpt sfp;
sfp.int_parts = htons((u_int16_t)d);
sfp.fractions = htons((u_int16_t)((d - (u_int16_t)d) * S_DENOMINATOR));
return (sfp);
}
char *
print_rtable(int r)
{
static char b[11];
b[0] = 0;
if (r > 0)
snprintf(b, sizeof(b), "rtable %d", r);
return (b);
}
const char *
log_sockaddr(struct sockaddr *sa)
{
static char buf[NI_MAXHOST];
if (getnameinfo(sa, SA_LEN(sa), buf, sizeof(buf), NULL, 0,
NI_NUMERICHOST))
return ("(unknown)");
else
return (buf);
}
const char *
log_ntp_addr(struct ntp_addr *addr)
{
if (addr == NULL)
return ("(unknown)");
return log_sockaddr((struct sockaddr *)&addr->ss);
}
pid_t
start_child(char *pname, int cfd, int argc, char **argv)
{
char **nargv;
int nargc, i;
pid_t pid;
/* Prepare the child process new argv. */
nargv = calloc(argc + 3, sizeof(char *));
if (nargv == NULL)
fatal("%s: calloc", __func__);
/* Copy the program name first. */
nargc = 0;
nargv[nargc++] = argv[0];
/* Set the process name and copy the original args. */
nargv[nargc++] = "-P";
nargv[nargc++] = pname;
for (i = 1; i < argc; i++)
nargv[nargc++] = argv[i];
nargv[nargc] = NULL;
switch (pid = fork()) {
case -1:
fatal("%s: fork", __func__);
break;
case 0:
/* Prepare the parent socket and execute. */
if (cfd != PARENT_SOCK_FILENO) {
if (dup2(cfd, PARENT_SOCK_FILENO) == -1)
fatal("dup2");
} else if (fcntl(cfd, F_SETFD, 0) == -1)
fatal("fcntl");
execvp(argv[0], nargv);
fatal("%s: execvp", __func__);
break;
default:
/* Close child's socket end. */
close(cfd);
break;
}
free(nargv);
return (pid);
}
int
sanitize_argv(int *argc, char ***argv)
{
char **nargv;
int nargc;
int i;
/*
* We need at least three arguments:
* Example: '/usr/sbin/ntpd' '-P' 'foobar'.
*/
if (*argc < 3)
return (-1);
*argc -= 2;
/* Allocate new arguments vector and copy pointers. */
nargv = calloc((*argc) + 1, sizeof(char *));
if (nargv == NULL)
return (-1);
nargc = 0;
nargv[nargc++] = (*argv)[0];
for (i = 1; i < *argc; i++)
nargv[nargc++] = (*argv)[i + 2];
nargv[nargc] = NULL;
*argv = nargv;
return (0);
}