Browse Source

Move execution of the constraints from the ntp to the parent process.

This helps the ntp process to a) give a better pledge(2) and to b)
keep the promise of "saving the world again... on time" by removing
the delays that have been introduced by expensive constraint forks.
The new design offers better privsep but introduces a few more imsgs
and runs a little bit more code in the privileged parent.  The
privileged code is minimal, carefully checked, and does not attempt to
"parse" any contents; the forked constraints instantly drop all
privileges and pledge to "stdio inet".
OK beck@ deraadt@
OPENBSD_5_9
reyk 9 years ago
parent
commit
707f923fe3
5 changed files with 427 additions and 187 deletions
  1. +2
    -1
      src/usr.sbin/ntpd/config.c
  2. +328
    -123
      src/usr.sbin/ntpd/constraint.c
  3. +15
    -41
      src/usr.sbin/ntpd/ntp.c
  4. +64
    -17
      src/usr.sbin/ntpd/ntpd.c
  5. +18
    -5
      src/usr.sbin/ntpd/ntpd.h

+ 2
- 1
src/usr.sbin/ntpd/config.c View File

@ -1,4 +1,4 @@
/* $OpenBSD: config.c,v 1.27 2015/07/18 00:53:44 bcook Exp $ */
/* $OpenBSD: config.c,v 1.28 2015/10/12 06:50:08 reyk Exp $ */
/* /*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@ -203,6 +203,7 @@ new_constraint(void)
if ((p = calloc(1, sizeof(struct constraint))) == NULL) if ((p = calloc(1, sizeof(struct constraint))) == NULL)
fatal("new_constraint calloc"); fatal("new_constraint calloc");
p->id = ++constraint_maxid; p->id = ++constraint_maxid;
p->fd = -1;
return (p); return (p);
} }


+ 328
- 123
src/usr.sbin/ntpd/constraint.c View File

@ -1,4 +1,4 @@
/* $OpenBSD: constraint.c,v 1.18 2015/10/09 03:50:40 deraadt Exp $ */
/* $OpenBSD: constraint.c,v 1.19 2015/10/12 06:50:08 reyk Exp $ */
/* /*
* Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org> * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@ -21,6 +21,7 @@
#include <sys/time.h> #include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/resource.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <netinet/in.h> #include <netinet/in.h>
@ -28,7 +29,6 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <imsg.h> #include <imsg.h>
#include <netdb.h> #include <netdb.h>
@ -37,8 +37,9 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <time.h> #include <time.h>
#include <ctype.h>
#include <tls.h> #include <tls.h>
#include <err.h>
#include <pwd.h>
#include "log.h" #include "log.h"
#include "ntpd.h" #include "ntpd.h"
@ -50,11 +51,15 @@ struct constraint *
constraint_byfd(int); constraint_byfd(int);
struct constraint * struct constraint *
constraint_bypid(pid_t); constraint_bypid(pid_t);
int constraint_close(int);
int constraint_close(u_int32_t);
void constraint_update(void); void constraint_update(void);
void constraint_reset(void); void constraint_reset(void);
int constraint_cmp(const void *, const void *); int constraint_cmp(const void *, const void *);
void priv_constraint_close(int, int);
void priv_constraint_child(struct constraint *, struct ntp_addr_msg *,
u_int8_t *, int[2]);
struct httpsdate * struct httpsdate *
httpsdate_init(const char *, const char *, const char *, httpsdate_init(const char *, const char *, const char *,
const char *, const u_int8_t *, size_t); const char *, const u_int8_t *, size_t);
@ -66,8 +71,10 @@ void *httpsdate_query(const char *, const char *, const char *,
char *tls_readline(struct tls *, size_t *, size_t *, struct timeval *); char *tls_readline(struct tls *, size_t *, size_t *, struct timeval *);
extern u_int constraint_cnt;
u_int constraint_cnt;
extern u_int peer_cnt; extern u_int peer_cnt;
extern struct imsgbuf *ibuf; /* priv */
extern struct imsgbuf *ibuf_main; /* chld */
struct httpsdate { struct httpsdate {
char *tls_host; char *tls_host;
@ -134,12 +141,10 @@ constraint_addr_init(struct constraint *cstr)
int int
constraint_query(struct constraint *cstr) constraint_query(struct constraint *cstr)
{ {
int pipes[2];
struct timeval rectv, xmttv;
void *ctx;
static char hname[NI_MAXHOST];
time_t now;
struct iovec iov[2];
time_t now;
struct ntp_addr_msg am;
struct iovec iov[3];
int iov_cnt = 0;
now = getmonotime(); now = getmonotime();
@ -168,7 +173,7 @@ constraint_query(struct constraint *cstr)
/* Reset and retry */ /* Reset and retry */
cstr->senderrors = 0; cstr->senderrors = 0;
constraint_close(cstr->fd);
constraint_close(cstr->id);
break; break;
case STATE_REPLY_RECEIVED: case STATE_REPLY_RECEIVED:
default: default:
@ -177,61 +182,82 @@ constraint_query(struct constraint *cstr)
} }
cstr->last = now; cstr->last = now;
if (getnameinfo((struct sockaddr *)&cstr->addr->ss,
SA_LEN((struct sockaddr *)&cstr->addr->ss),
hname, sizeof(hname), NULL, 0,
NI_NUMERICHOST) != 0)
fatalx("%s getnameinfo %s", __func__, cstr->addr_head.name);
cstr->state = STATE_QUERY_SENT;
log_debug("constraint request to %s", hname);
memset(&am, 0, sizeof(am));
memcpy(&am.a, cstr->addr, sizeof(am.a));
iov[iov_cnt].iov_base = &am;
iov[iov_cnt++].iov_len = sizeof(am);
if (cstr->addr_head.name) {
am.namelen = strlen(cstr->addr_head.name) + 1;
iov[iov_cnt].iov_base = cstr->addr_head.name;
iov[iov_cnt++].iov_len = am.namelen;
}
if (cstr->addr_head.path) {
am.pathlen = strlen(cstr->addr_head.path) + 1;
iov[iov_cnt].iov_base = cstr->addr_head.path;
iov[iov_cnt++].iov_len = am.pathlen;
}
imsg_composev(ibuf_main, IMSG_CONSTRAINT_QUERY,
cstr->id, 0, -1, iov, iov_cnt);
return (0);
}
void
priv_constraint_msg(u_int32_t id, u_int8_t *data, size_t len)
{
struct ntp_addr_msg am;
struct ntp_addr *h;
struct constraint *cstr;
int pipes[2];
if ((cstr = constraint_byid(id)) != NULL) {
log_warnx("IMSG_CONSTRAINT_QUERY repeated for id %d", id);
return;
}
if (len < sizeof(am)) {
log_warnx("invalid IMSG_CONSTRAINT_QUERY received");
return;
}
memcpy(&am, data, sizeof(am));
if (len != (sizeof(am) + am.namelen + am.pathlen)) {
log_warnx("invalid IMSG_CONSTRAINT_QUERY received");
return;
}
/* Additional imsg data is obtained in the unpriv child */
if ((h = calloc(1, sizeof(*h))) == NULL)
fatal("calloc ntp_addr");
memcpy(h, &am.a, sizeof(*h));
h->next = NULL;
cstr = new_constraint();
cstr->id = id;
cstr->addr = h;
cstr->addr_head.a = h;
constraint_add(cstr);
constraint_cnt++;
if (socketpair(AF_UNIX, SOCK_DGRAM, AF_UNSPEC, pipes) == -1) if (socketpair(AF_UNIX, SOCK_DGRAM, AF_UNSPEC, pipes) == -1)
fatal("%s pipes", __func__); fatal("%s pipes", __func__);
/* Fork child handlers */
/*
* Fork child handlers and make sure to do any sensitive work in the
* the (unprivileged) child. The parent should not do any parsing,
* certificate loading etc.
*/
switch (cstr->pid = fork()) { switch (cstr->pid = fork()) {
case -1: case -1:
cstr->senderrors++; cstr->senderrors++;
close(pipes[0]); close(pipes[0]);
close(pipes[1]); close(pipes[1]);
return (-1);
return;
case 0: case 0:
setproctitle("constraint from %s", hname);
if (pledge("stdio inet", NULL) == -1)
err(1, "pledge");
/* Child process */
if (dup2(pipes[1], CONSTRAINT_PASSFD) == -1)
fatal("%s dup2 CONSTRAINT_PASSFD", __func__);
if (pipes[0] != CONSTRAINT_PASSFD)
close(pipes[0]);
if (pipes[1] != CONSTRAINT_PASSFD)
close(pipes[1]);
(void)closefrom(CONSTRAINT_PASSFD + 1);
if (fcntl(CONSTRAINT_PASSFD, F_SETFD, FD_CLOEXEC) == -1)
fatal("%s fcntl F_SETFD", __func__);
cstr->fd = CONSTRAINT_PASSFD;
imsg_init(&cstr->ibuf, cstr->fd);
if ((ctx = httpsdate_query(hname,
CONSTRAINT_PORT, cstr->addr_head.name, cstr->addr_head.path,
conf->ca, conf->ca_len, &rectv, &xmttv)) == NULL) {
/* Abort with failure but without warning */
exit(1);
}
iov[0].iov_base = &rectv;
iov[0].iov_len = sizeof(rectv);
iov[1].iov_base = &xmttv;
iov[1].iov_len = sizeof(xmttv);
imsg_composev(&cstr->ibuf, IMSG_CONSTRAINT, 0, 0, -1, iov, 2);
imsg_flush(&cstr->ibuf);
/* Tear down the TLS connection after sending the result */
httpsdate_free(ctx);
priv_constraint_child(cstr, &am, data + sizeof(am), pipes);
_exit(0); _exit(0);
/* NOTREACHED */ /* NOTREACHED */
@ -239,56 +265,140 @@ constraint_query(struct constraint *cstr)
/* Parent */ /* Parent */
close(pipes[1]); close(pipes[1]);
cstr->fd = pipes[0]; cstr->fd = pipes[0];
cstr->state = STATE_QUERY_SENT;
imsg_init(&cstr->ibuf, cstr->fd); imsg_init(&cstr->ibuf, cstr->fd);
break; break;
} }
}
void
priv_constraint_child(struct constraint *cstr, struct ntp_addr_msg *am,
u_int8_t *data, int pipes[2])
{
static char hname[NI_MAXHOST];
struct timeval rectv, xmttv;
struct sigaction sa;
struct passwd *pw;
void *ctx;
struct iovec iov[2];
int i;
if (setpriority(PRIO_PROCESS, 0, 0) == -1)
log_warn("could not set priority");
/* Init TLS and load cert before chroot() */
if (tls_init() == -1)
fatalx("tls_init");
if ((conf->ca = tls_load_file(CONSTRAINT_CA,
&conf->ca_len, NULL)) == NULL)
log_warnx("constraint certificate verification turned off");
/* Drop privileges */
if ((pw = getpwnam(NTPD_USER)) == NULL)
fatalx("unknown user %s", NTPD_USER);
if (chroot(pw->pw_dir) == -1)
fatal("chroot");
if (chdir("/") == -1)
fatal("chdir(\"/\")");
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");
/* Reset all signal handlers */
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_DFL;
for (i = 1; i < _NSIG; i++)
sigaction(i, &sa, NULL);
if (pledge("stdio inet", NULL) == -1)
fatal("pledge");
/* Get name and set process title */
if (getnameinfo((struct sockaddr *)&cstr->addr->ss,
SA_LEN((struct sockaddr *)&cstr->addr->ss),
hname, sizeof(hname), NULL, 0,
NI_NUMERICHOST) != 0)
fatalx("%s getnameinfo", __func__);
log_debug("constraint request to %s", hname);
setproctitle("constraint from %s", hname);
return (0);
/* Set file descriptors */
if (dup2(pipes[1], CONSTRAINT_PASSFD) == -1)
fatal("%s dup2 CONSTRAINT_PASSFD", __func__);
if (pipes[0] != CONSTRAINT_PASSFD)
close(pipes[0]);
if (pipes[1] != CONSTRAINT_PASSFD)
close(pipes[1]);
(void)closefrom(CONSTRAINT_PASSFD + 1);
if (fcntl(CONSTRAINT_PASSFD, F_SETFD, FD_CLOEXEC) == -1)
fatal("%s fcntl F_SETFD", __func__);
cstr->fd = CONSTRAINT_PASSFD;
imsg_init(&cstr->ibuf, cstr->fd);
/* Get remaining data from imsg in the unpriv child */
if (am->namelen) {
if ((cstr->addr_head.name =
get_string(data, am->namelen)) == NULL)
fatalx("invalid IMSG_CONSTRAINT_QUERY name");
data += am->namelen;
}
if (am->pathlen) {
if ((cstr->addr_head.path =
get_string(data, am->pathlen)) == NULL)
fatalx("invalid IMSG_CONSTRAINT_QUERY path");
}
/* Run! */
if ((ctx = httpsdate_query(hname,
CONSTRAINT_PORT, cstr->addr_head.name, cstr->addr_head.path,
conf->ca, conf->ca_len, &rectv, &xmttv)) == NULL) {
/* Abort with failure but without warning */
exit(1);
}
iov[0].iov_base = &rectv;
iov[0].iov_len = sizeof(rectv);
iov[1].iov_base = &xmttv;
iov[1].iov_len = sizeof(xmttv);
imsg_composev(&cstr->ibuf,
IMSG_CONSTRAINT_RESULT, 0, 0, -1, iov, 2);
imsg_flush(&cstr->ibuf);
/* Tear down the TLS connection after sending the result */
httpsdate_free(ctx);
} }
void void
constraint_check_child(void)
priv_constraint_check_child(pid_t pid, int status)
{ {
struct constraint *cstr; struct constraint *cstr;
int status;
int fail, sig; int fail, sig;
pid_t pid;
do {
pid = waitpid(WAIT_ANY, &status, WNOHANG);
if (pid <= 0)
continue;
fail = sig = 0;
if (WIFSIGNALED(status)) {
sig = WTERMSIG(status);
} else if (WIFEXITED(status)) {
if (WEXITSTATUS(status) != 0)
fail = 1;
} else
fatalx("unexpected cause of SIGCHLD");
if ((cstr = constraint_bypid(pid)) != NULL) {
if (sig)
fatalx("constraint %s, signal %d",
log_sockaddr((struct sockaddr *)
&cstr->addr->ss), sig);
if (fail) {
log_debug("no constraint reply from %s"
" received in time, next query %ds",
log_sockaddr((struct sockaddr *)
&cstr->addr->ss), CONSTRAINT_SCAN_INTERVAL);
}
if (fail || cstr->state < STATE_QUERY_SENT) {
cstr->senderrors++;
constraint_close(cstr->fd);
}
}
} while (pid > 0 || (pid == -1 && errno == EINTR));
fail = sig = 0;
if (WIFSIGNALED(status)) {
sig = WTERMSIG(status);
} else if (WIFEXITED(status)) {
if (WEXITSTATUS(status) != 0)
fail = 1;
} else
fatalx("unexpected cause of SIGCHLD");
if ((cstr = constraint_bypid(pid)) != NULL) {
if (sig)
fatalx("constraint %s, signal %d",
log_sockaddr((struct sockaddr *)
&cstr->addr->ss), sig);
priv_constraint_close(cstr->fd, fail);
}
} }
struct constraint * struct constraint *
@ -331,18 +441,15 @@ constraint_bypid(pid_t pid)
} }
int int
constraint_close(int fd)
constraint_close(u_int32_t id)
{ {
struct constraint *cstr; struct constraint *cstr;
if ((cstr = constraint_byfd(fd)) == NULL) {
log_warn("%s: fd %d: not found", __func__, fd);
if ((cstr = constraint_byid(id)) == NULL) {
log_warn("%s: id %d: not found", __func__, id);
return (0); return (0);
} }
msgbuf_clear(&cstr->ibuf.w);
close(cstr->fd);
cstr->fd = -1;
cstr->last = getmonotime(); cstr->last = getmonotime();
if (cstr->addr == NULL || (cstr->addr = cstr->addr->next) == NULL) { if (cstr->addr == NULL || (cstr->addr = cstr->addr->next) == NULL) {
@ -360,6 +467,25 @@ constraint_close(int fd)
return (constraint_init(cstr)); return (constraint_init(cstr));
} }
void
priv_constraint_close(int fd, int fail)
{
struct constraint *cstr;
u_int32_t id;
if ((cstr = constraint_byfd(fd)) == NULL) {
log_warn("%s: fd %d: not found", __func__, fd);
return;
}
id = cstr->id;
constraint_remove(cstr);
constraint_cnt--;
imsg_compose(ibuf, IMSG_CONSTRAINT_CLOSE, id, 0, -1,
&fail, sizeof(fail));
}
void void
constraint_add(struct constraint *cstr) constraint_add(struct constraint *cstr)
{ {
@ -370,19 +496,31 @@ void
constraint_remove(struct constraint *cstr) constraint_remove(struct constraint *cstr)
{ {
TAILQ_REMOVE(&conf->constraints, cstr, entry); TAILQ_REMOVE(&conf->constraints, cstr, entry);
msgbuf_clear(&cstr->ibuf.w);
if (cstr->fd != -1)
close(cstr->fd);
free(cstr->addr_head.name); free(cstr->addr_head.name);
free(cstr->addr_head.path); free(cstr->addr_head.path);
free(cstr); free(cstr);
} }
void
constraint_purge(void)
{
struct constraint *cstr, *ncstr;
TAILQ_FOREACH_SAFE(cstr, &conf->constraints, entry, ncstr)
constraint_remove(cstr);
}
int int
constraint_dispatch_msg(struct pollfd *pfd)
priv_constraint_dispatch(struct pollfd *pfd)
{ {
struct imsg imsg; struct imsg imsg;
struct constraint *cstr; struct constraint *cstr;
ssize_t n; ssize_t n;
struct timeval tv[2]; struct timeval tv[2];
double offset;
if ((cstr = constraint_byfd(pfd->fd)) == NULL) if ((cstr = constraint_byfd(pfd->fd)) == NULL)
return (0); return (0);
@ -391,37 +529,26 @@ constraint_dispatch_msg(struct pollfd *pfd)
return (0); return (0);
if ((n = imsg_read(&cstr->ibuf)) == -1 || n == 0) { if ((n = imsg_read(&cstr->ibuf)) == -1 || n == 0) {
constraint_close(pfd->fd);
priv_constraint_close(pfd->fd, 1);
return (1); return (1);
} }
for (;;) { for (;;) {
if ((n = imsg_get(&cstr->ibuf, &imsg)) == -1) { if ((n = imsg_get(&cstr->ibuf, &imsg)) == -1) {
constraint_close(pfd->fd);
priv_constraint_close(pfd->fd, 1);
return (1); return (1);
} }
if (n == 0) if (n == 0)
break; break;
switch (imsg.hdr.type) { switch (imsg.hdr.type) {
case IMSG_CONSTRAINT:
case IMSG_CONSTRAINT_RESULT:
if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(tv)) if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(tv))
fatalx("invalid IMSG_CONSTRAINT received"); fatalx("invalid IMSG_CONSTRAINT received");
memcpy(tv, imsg.data, sizeof(tv));
offset = gettime_from_timeval(&tv[0]) -
gettime_from_timeval(&tv[1]);
log_info("constraint reply from %s: offset %f",
log_sockaddr((struct sockaddr *)&cstr->addr->ss),
offset);
cstr->state = STATE_REPLY_RECEIVED;
cstr->last = getmonotime();
cstr->constraint = tv[0].tv_sec;
constraint_update();
/* forward imsg to ntp child, don't parse it here */
imsg_compose(ibuf, imsg.hdr.type,
cstr->id, 0, -1, imsg.data, sizeof(tv));
break; break;
default: default:
break; break;
@ -433,7 +560,71 @@ constraint_dispatch_msg(struct pollfd *pfd)
} }
void void
constraint_dns(u_int32_t id, u_int8_t *data, size_t len)
constraint_msg_result(u_int32_t id, u_int8_t *data, size_t len)
{
struct constraint *cstr;
struct timeval tv[2];
double offset;
if ((cstr = constraint_byid(id)) == NULL) {
log_warnx("IMSG_CONSTRAINT_CLOSE with invalid constraint id");
return;
}
if (len != sizeof(tv)) {
log_warnx("invalid IMSG_CONSTRAINT received");
return;
}
memcpy(tv, data, len);
offset = gettime_from_timeval(&tv[0]) -
gettime_from_timeval(&tv[1]);
log_info("constraint reply from %s: offset %f",
log_sockaddr((struct sockaddr *)&cstr->addr->ss),
offset);
cstr->state = STATE_REPLY_RECEIVED;
cstr->last = getmonotime();
cstr->constraint = tv[0].tv_sec;
constraint_update();
}
void
constraint_msg_close(u_int32_t id, u_int8_t *data, size_t len)
{
struct constraint *cstr;
int fail;
if ((cstr = constraint_byid(id)) == NULL) {
log_warnx("IMSG_CONSTRAINT_CLOSE with invalid constraint id");
return;
}
if (len != sizeof(int)) {
log_warnx("invalid IMSG_CONSTRAINT_CLOSE received");
return;
}
memcpy(&fail, data, len);
if (fail) {
log_debug("no constraint reply from %s"
" received in time, next query %ds",
log_sockaddr((struct sockaddr *)
&cstr->addr->ss), CONSTRAINT_SCAN_INTERVAL);
}
if (fail || cstr->state < STATE_QUERY_SENT) {
cstr->senderrors++;
constraint_close(cstr->id);
}
}
void
constraint_msg_dns(u_int32_t id, u_int8_t *data, size_t len)
{ {
struct constraint *cstr, *ncstr = NULL; struct constraint *cstr, *ncstr = NULL;
u_int8_t *p; u_int8_t *p;
@ -542,7 +733,7 @@ constraint_reset(void)
TAILQ_FOREACH(cstr, &conf->constraints, entry) { TAILQ_FOREACH(cstr, &conf->constraints, entry) {
if (cstr->state == STATE_QUERY_SENT) if (cstr->state == STATE_QUERY_SENT)
continue; continue;
constraint_close(cstr->fd);
constraint_close(cstr->id);
} }
conf->constraint_errors = 0; conf->constraint_errors = 0;
} }
@ -583,9 +774,6 @@ httpsdate_init(const char *hname, const char *port, const char *name,
{ {
struct httpsdate *httpsdate = NULL; struct httpsdate *httpsdate = NULL;
if (tls_init() == -1)
return (NULL);
if ((httpsdate = calloc(1, sizeof(*httpsdate))) == NULL) if ((httpsdate = calloc(1, sizeof(*httpsdate))) == NULL)
goto fail; goto fail;
@ -785,3 +973,20 @@ tls_readline(struct tls *tls, size_t *lenp, size_t *maxlength,
fatal("gettimeofday"); fatal("gettimeofday");
return (buf); return (buf);
} }
char *
get_string(u_int8_t *ptr, size_t len)
{
size_t i;
char *str;
for (i = 0; i < len; i++)
if (!(isprint(ptr[i]) || isspace(ptr[i])))
break;
if ((str = calloc(1, i + 1)) == NULL)
return (NULL);
memcpy(str, ptr, i);
return (str);
}

+ 15
- 41
src/usr.sbin/ntpd/ntp.c View File

@ -1,4 +1,4 @@
/* $OpenBSD: ntp.c,v 1.136 2015/10/09 03:54:53 deraadt Exp $ */
/* $OpenBSD: ntp.c,v 1.137 2015/10/12 06:50:08 reyk Exp $ */
/* /*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@ -31,7 +31,6 @@
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include <err.h> #include <err.h>
#include <tls.h>
#include "ntpd.h" #include "ntpd.h"
@ -42,14 +41,13 @@
volatile sig_atomic_t ntp_quit = 0; volatile sig_atomic_t ntp_quit = 0;
volatile sig_atomic_t ntp_report = 0; volatile sig_atomic_t ntp_report = 0;
volatile sig_atomic_t ntp_sigchld = 0;
struct imsgbuf *ibuf_main; struct imsgbuf *ibuf_main;
struct imsgbuf *ibuf_dns; struct imsgbuf *ibuf_dns;
struct ntpd_conf *conf; struct ntpd_conf *conf;
struct ctl_conns ctl_conns; struct ctl_conns ctl_conns;
u_int peer_cnt; u_int peer_cnt;
u_int sensors_cnt; u_int sensors_cnt;
u_int constraint_cnt;
extern u_int constraint_cnt;
time_t lastreport; time_t lastreport;
void ntp_sighdlr(int); void ntp_sighdlr(int);
@ -70,9 +68,6 @@ ntp_sighdlr(int sig)
case SIGINFO: case SIGINFO:
ntp_report = 1; ntp_report = 1;
break; break;
case SIGCHLD:
ntp_sigchld = 1;
break;
} }
} }
@ -110,13 +105,6 @@ ntp_main(int pipe_prnt[2], int fd_ctl, struct ntpd_conf *nconf,
return (pid); return (pid);
} }
tls_init();
/* Verification will be turned off if CA is not found */
if ((conf->ca = tls_load_file(CONSTRAINT_CA,
&conf->ca_len, NULL)) == NULL)
log_warnx("constraint certificate verification turned off");
/* in this case the parent didn't init logging and didn't daemonize */ /* in this case the parent didn't init logging and didn't daemonize */
if (nconf->settime && !nconf->debug) { if (nconf->settime && !nconf->debug) {
log_init(nconf->debug); log_init(nconf->debug);
@ -166,18 +154,8 @@ ntp_main(int pipe_prnt[2], int fd_ctl, struct ntpd_conf *nconf,
endservent(); endservent();
/*
* XXX
* Unfortunately, the "contraint" processes are forked
* below the "ntp engine". Hence the ntp engine needs
* to be able to fork -> "proc", and the "constraint"
* process will want to open sockets -> "inet".
*
* For many reasons, including fork/exec cost, it would
* be better if constraints were forked from the master
* process, which would then tell the ntp engine.
*/
if (pledge("stdio inet proc", NULL) == -1)
/* The ntp process will want to open NTP client sockets -> "inet" */
if (pledge("stdio inet", NULL) == -1)
err(1, "pledge"); err(1, "pledge");
signal(SIGTERM, ntp_sighdlr); signal(SIGTERM, ntp_sighdlr);
@ -185,7 +163,7 @@ ntp_main(int pipe_prnt[2], int fd_ctl, struct ntpd_conf *nconf,
signal(SIGINFO, ntp_sighdlr); signal(SIGINFO, ntp_sighdlr);
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
signal(SIGHUP, SIG_IGN); signal(SIGHUP, SIG_IGN);
signal(SIGCHLD, ntp_sighdlr);
signal(SIGCHLD, SIG_DFL);
if ((ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL) if ((ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL)
fatal(NULL); fatal(NULL);
@ -248,7 +226,7 @@ ntp_main(int pipe_prnt[2], int fd_ctl, struct ntpd_conf *nconf,
} }
new_cnt = PFD_MAX + new_cnt = PFD_MAX +
peer_cnt + listener_cnt + ctl_cnt + constraint_cnt;
peer_cnt + listener_cnt + ctl_cnt;
if (new_cnt > pfd_elms) { if (new_cnt > pfd_elms) {
if ((newp = reallocarray(pfd, new_cnt, if ((newp = reallocarray(pfd, new_cnt,
sizeof(*pfd))) == NULL) { sizeof(*pfd))) == NULL) {
@ -369,9 +347,6 @@ ntp_main(int pipe_prnt[2], int fd_ctl, struct ntpd_conf *nconf,
TAILQ_FOREACH(cstr, &conf->constraints, entry) { TAILQ_FOREACH(cstr, &conf->constraints, entry) {
if (constraint_query(cstr) == -1) if (constraint_query(cstr) == -1)
continue; continue;
pfd[i].fd = cstr->fd;
pfd[i].events = POLLIN;
i++;
} }
now = getmonotime(); now = getmonotime();
@ -439,10 +414,6 @@ ntp_main(int pipe_prnt[2], int fd_ctl, struct ntpd_conf *nconf,
nfds -= control_dispatch_msg(&pfd[j], &ctl_cnt); nfds -= control_dispatch_msg(&pfd[j], &ctl_cnt);
} }
for (; nfds > 0 && j < i; j++) {
nfds -= constraint_dispatch_msg(&pfd[j]);
}
for (s = TAILQ_FIRST(&conf->ntp_sensors); s != NULL; for (s = TAILQ_FIRST(&conf->ntp_sensors); s != NULL;
s = next_s) { s = next_s) {
next_s = TAILQ_NEXT(s, entry); next_s = TAILQ_NEXT(s, entry);
@ -451,11 +422,6 @@ ntp_main(int pipe_prnt[2], int fd_ctl, struct ntpd_conf *nconf,
} }
report_peers(ntp_report); report_peers(ntp_report);
ntp_report = 0; ntp_report = 0;
if (ntp_sigchld) {
constraint_check_child();
ntp_sigchld = 0;
}
} }
msgbuf_write(&ibuf_main->w); msgbuf_write(&ibuf_main->w);
@ -501,6 +467,14 @@ ntp_dispatch_imsg(void)
conf->status.synced = 0; conf->status.synced = 0;
} }
break; 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: default:
break; break;
} }
@ -590,7 +564,7 @@ ntp_dispatch_imsg_dns(void)
client_addr_init(peer); client_addr_init(peer);
break; break;
case IMSG_CONSTRAINT_DNS: case IMSG_CONSTRAINT_DNS:
constraint_dns(imsg.hdr.peerid,
constraint_msg_dns(imsg.hdr.peerid,
imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
break; break;
default: default:


+ 64
- 17
src/usr.sbin/ntpd/ntpd.c View File

@ -1,4 +1,4 @@
/* $OpenBSD: ntpd.c,v 1.96 2015/10/09 01:37:09 deraadt Exp $ */
/* $OpenBSD: ntpd.c,v 1.97 2015/10/12 06:50:08 reyk Exp $ */
/* /*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@ -60,6 +60,8 @@ volatile sig_atomic_t sigchld = 0;
struct imsgbuf *ibuf; struct imsgbuf *ibuf;
int timeout = INFTIM; int timeout = INFTIM;
extern u_int constraint_cnt;
const char *showopt; const char *showopt;
static const char *ctl_showopt_list[] = { static const char *ctl_showopt_list[] = {
@ -99,18 +101,22 @@ usage(void)
#define POLL_MAX 8 #define POLL_MAX 8
#define PFD_PIPE 0 #define PFD_PIPE 0
#define PFD_MAX 1
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
struct ntpd_conf lconf; struct ntpd_conf lconf;
struct pollfd pfd[POLL_MAX];
struct pollfd *pfd = NULL;
pid_t chld_pid = 0, pid; pid_t chld_pid = 0, pid;
const char *conffile; const char *conffile;
int fd_ctl, ch, nfds;
int fd_ctl, ch, nfds, i, j;
int pipe_chld[2]; int pipe_chld[2];
struct passwd *pw; struct passwd *pw;
extern char *__progname; extern char *__progname;
u_int pfd_elms = 0, new_cnt;
struct constraint *cstr;
void *newp;
if (strcmp(__progname, "ntpctl") == 0) { if (strcmp(__progname, "ntpctl") == 0) {
ctl_main(argc, argv); ctl_main(argc, argv);
@ -201,18 +207,42 @@ main(int argc, char *argv[])
signal(SIGHUP, sighdlr); signal(SIGHUP, sighdlr);
close(pipe_chld[1]); close(pipe_chld[1]);
constraint_purge();
if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
fatal(NULL); fatal(NULL);
imsg_init(ibuf, pipe_chld[0]); imsg_init(ibuf, pipe_chld[0]);
constraint_cnt = 0;
while (quit == 0) { 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].fd = ibuf->fd;
pfd[PFD_PIPE].events = POLLIN; pfd[PFD_PIPE].events = POLLIN;
if (ibuf->w.queued) if (ibuf->w.queued)
pfd[PFD_PIPE].events |= POLLOUT; pfd[PFD_PIPE].events |= POLLOUT;
if ((nfds = poll(pfd, 1, timeout)) == -1)
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) { if (errno != EINTR) {
log_warn("poll error"); log_warn("poll error");
quit = 1; quit = 1;
@ -241,6 +271,10 @@ main(int argc, char *argv[])
quit = 1; quit = 1;
} }
for (j = PFD_MAX; nfds > 0 && j < i; j++) {
nfds -= priv_constraint_dispatch(&pfd[j]);
}
if (sigchld) { if (sigchld) {
if (check_child(chld_pid, "child")) { if (check_child(chld_pid, "child")) {
quit = 1; quit = 1;
@ -269,24 +303,33 @@ main(int argc, char *argv[])
} }
int int
check_child(pid_t pid, const char *pname)
check_child(pid_t chld_pid, const char *pname)
{ {
int status, sig; int status, sig;
char *signame; char *signame;
pid_t pid;
if (waitpid(pid, &status, WNOHANG) > 0) {
if (WIFEXITED(status)) {
log_warnx("Lost child: %s exited", pname);
return (1);
}
if (WIFSIGNALED(status)) {
sig = WTERMSIG(status);
signame = strsignal(sig) ? strsignal(sig) : "unknown";
log_warnx("Lost child: %s terminated; signal %d (%s)",
pname, sig, signame);
return (1);
do {
pid = waitpid(WAIT_ANY, &status, WNOHANG);
if (pid <= 0) {
continue;
} else if (pid == chld_pid) {
if (WIFEXITED(status)) {
log_warnx("Lost child: %s exited", pname);
return (1);
}
if (WIFSIGNALED(status)) {
sig = WTERMSIG(status);
signame = strsignal(sig) ?
strsignal(sig) : "unknown";
log_warnx("Lost child: %s terminated; "
"signal %d (%s)", pname, sig, signame);
return (1);
}
} else {
priv_constraint_check_child(pid, status);
} }
}
} while (pid > 0 || (pid == -1 && errno == EINTR));
return (0); return (0);
} }
@ -343,6 +386,10 @@ dispatch_imsg(struct ntpd_conf *lconf)
lconf->settime = 0; lconf->settime = 0;
timeout = INFTIM; timeout = INFTIM;
break; break;
case IMSG_CONSTRAINT_QUERY:
priv_constraint_msg(imsg.hdr.peerid,
imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
break;
default: default:
break; break;
} }


+ 18
- 5
src/usr.sbin/ntpd/ntpd.h View File

@ -1,4 +1,4 @@
/* $OpenBSD: ntpd.h,v 1.121 2015/05/20 13:32:39 reyk Exp $ */
/* $OpenBSD: ntpd.h,v 1.122 2015/10/12 06:50:08 reyk Exp $ */
/* /*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@ -114,6 +114,12 @@ struct ntp_addr_wrap {
u_int8_t pool; u_int8_t pool;
}; };
struct ntp_addr_msg {
struct ntp_addr a;
size_t namelen;
size_t pathlen;
};
struct ntp_status { struct ntp_status {
double rootdelay; double rootdelay;
double rootdispersion; double rootdispersion;
@ -279,8 +285,10 @@ enum imsg_type {
IMSG_ADJFREQ, IMSG_ADJFREQ,
IMSG_SETTIME, IMSG_SETTIME,
IMSG_HOST_DNS, IMSG_HOST_DNS,
IMSG_CONSTRAINT,
IMSG_CONSTRAINT_DNS, IMSG_CONSTRAINT_DNS,
IMSG_CONSTRAINT_QUERY,
IMSG_CONSTRAINT_RESULT,
IMSG_CONSTRAINT_CLOSE,
IMSG_CTL_SHOW_STATUS, IMSG_CTL_SHOW_STATUS,
IMSG_CTL_SHOW_PEERS, IMSG_CTL_SHOW_PEERS,
IMSG_CTL_SHOW_PEERS_END, IMSG_CTL_SHOW_PEERS_END,
@ -343,12 +351,17 @@ void set_next(struct ntp_peer *, time_t);
/* constraint.c */ /* constraint.c */
void constraint_add(struct constraint *); void constraint_add(struct constraint *);
void constraint_remove(struct constraint *); void constraint_remove(struct constraint *);
void constraint_purge(void);
int constraint_init(struct constraint *); int constraint_init(struct constraint *);
int constraint_query(struct constraint *); int constraint_query(struct constraint *);
int constraint_dispatch_msg(struct pollfd *);
void constraint_check_child(void);
int constraint_check(double); int constraint_check(double);
void constraint_dns(u_int32_t, u_int8_t *, size_t);
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 priv_constraint_dispatch(struct pollfd *);
void priv_constraint_check_child(pid_t, int);
char *get_string(u_int8_t *, size_t);
/* util.c */ /* util.c */
double gettime_corrected(void); double gettime_corrected(void);


Loading…
Cancel
Save