xref: /openbsd-src/usr.sbin/ntpd/constraint.c (revision f1b790a5738b7375271fee81f99119b1f82f2cfd)
1*f1b790a5Sclaudio /*	$OpenBSD: constraint.c,v 1.60 2024/11/21 13:38:14 claudio Exp $	*/
2bc58a738Sreyk 
3bc58a738Sreyk /*
4bc58a738Sreyk  * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
5bc58a738Sreyk  *
6bc58a738Sreyk  * Permission to use, copy, modify, and distribute this software for any
7bc58a738Sreyk  * purpose with or without fee is hereby granted, provided that the above
8bc58a738Sreyk  * copyright notice and this permission notice appear in all copies.
9bc58a738Sreyk  *
10bc58a738Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11bc58a738Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12bc58a738Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13bc58a738Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14bc58a738Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15bc58a738Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16bc58a738Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17bc58a738Sreyk  */
18bc58a738Sreyk 
19bc58a738Sreyk #include <sys/queue.h>
20bc58a738Sreyk #include <sys/socket.h>
21bc58a738Sreyk #include <sys/time.h>
22bc58a738Sreyk #include <sys/types.h>
23bc58a738Sreyk #include <sys/wait.h>
24a257dd04Sreyk #include <sys/resource.h>
25bc58a738Sreyk #include <sys/uio.h>
26bc58a738Sreyk 
27bc58a738Sreyk #include <netinet/in.h>
28bc58a738Sreyk #include <arpa/inet.h>
29bc58a738Sreyk 
301a2f0668Sclaudio #include <errno.h>
31bc58a738Sreyk #include <stdio.h>
32bc58a738Sreyk #include <stdlib.h>
33bc58a738Sreyk #include <fcntl.h>
34bc58a738Sreyk #include <imsg.h>
35bc58a738Sreyk #include <netdb.h>
36bc58a738Sreyk #include <poll.h>
37bc58a738Sreyk #include <signal.h>
38bc58a738Sreyk #include <string.h>
39bc58a738Sreyk #include <unistd.h>
40bc58a738Sreyk #include <time.h>
41a257dd04Sreyk #include <ctype.h>
42bc58a738Sreyk #include <tls.h>
43a257dd04Sreyk #include <pwd.h>
44e6588cf8Sotto #include <math.h>
45bc58a738Sreyk 
46bc58a738Sreyk #include "ntpd.h"
47bc58a738Sreyk 
48b7e9bd4fSjsing #define	IMF_FIXDATE	"%a, %d %h %Y %T GMT"
49b7e9bd4fSjsing #define	X509_DATE	"%Y-%m-%d %T UTC"
50b7e9bd4fSjsing 
51bc58a738Sreyk int	 constraint_addr_init(struct constraint *);
52c7e8e3a2Sotto void	 constraint_addr_head_clear(struct constraint *);
53bc58a738Sreyk struct constraint *
54b775b3eeSreyk 	 constraint_byid(u_int32_t);
55b775b3eeSreyk struct constraint *
56bc58a738Sreyk 	 constraint_byfd(int);
57bc58a738Sreyk struct constraint *
58bc58a738Sreyk 	 constraint_bypid(pid_t);
59a257dd04Sreyk int	 constraint_close(u_int32_t);
60bc58a738Sreyk void	 constraint_update(void);
61bc58a738Sreyk int	 constraint_cmp(const void *, const void *);
62bc58a738Sreyk 
63a257dd04Sreyk void	 priv_constraint_close(int, int);
645f14684eSrzalamena void	 priv_constraint_readquery(struct constraint *, struct ntp_addr_msg *,
655f14684eSrzalamena 	    uint8_t **);
66a257dd04Sreyk 
67bc58a738Sreyk struct httpsdate *
68bc58a738Sreyk 	 httpsdate_init(const char *, const char *, const char *,
6905b37b28Sotto 	    const char *, const u_int8_t *, size_t, int);
70bc58a738Sreyk void	 httpsdate_free(void *);
7105b37b28Sotto int	 httpsdate_request(struct httpsdate *, struct timeval *, int);
72bc58a738Sreyk void	*httpsdate_query(const char *, const char *, const char *,
73bc58a738Sreyk 	    const char *, const u_int8_t *, size_t,
7405b37b28Sotto 	    struct timeval *, struct timeval *, int);
75bc58a738Sreyk 
76bc58a738Sreyk char	*tls_readline(struct tls *, size_t *, size_t *, struct timeval *);
77bc58a738Sreyk 
78a257dd04Sreyk u_int constraint_cnt;
79dcbb241cSreyk extern u_int peer_cnt;
80a257dd04Sreyk extern struct imsgbuf *ibuf;		/* priv */
81a257dd04Sreyk extern struct imsgbuf *ibuf_main;	/* chld */
82b775b3eeSreyk 
83bc58a738Sreyk struct httpsdate {
8451d66267Sjsing 	char			*tls_addr;
85bc58a738Sreyk 	char			*tls_port;
8651d66267Sjsing 	char			*tls_hostname;
87bc58a738Sreyk 	char			*tls_path;
88bc58a738Sreyk 	char			*tls_request;
89bc58a738Sreyk 	struct tls_config	*tls_config;
90bc58a738Sreyk 	struct tls		*tls_ctx;
91bc58a738Sreyk 	struct tm		 tls_tm;
92bc58a738Sreyk };
93bc58a738Sreyk 
94bc58a738Sreyk int
95bc58a738Sreyk constraint_init(struct constraint *cstr)
96bc58a738Sreyk {
97bc58a738Sreyk 	cstr->state = STATE_NONE;
98bc58a738Sreyk 	cstr->fd = -1;
99bc58a738Sreyk 	cstr->last = getmonotime();
100bc58a738Sreyk 	cstr->constraint = 0;
1013303745eSreyk 	cstr->senderrors = 0;
102bc58a738Sreyk 
103bc58a738Sreyk 	return (constraint_addr_init(cstr));
104bc58a738Sreyk }
105bc58a738Sreyk 
106bc58a738Sreyk int
107bc58a738Sreyk constraint_addr_init(struct constraint *cstr)
108bc58a738Sreyk {
109bc58a738Sreyk 	struct sockaddr_in	*sa_in;
110bc58a738Sreyk 	struct sockaddr_in6	*sa_in6;
111bc58a738Sreyk 	struct ntp_addr		*h;
112bc58a738Sreyk 
113b775b3eeSreyk 	if (cstr->state == STATE_DNS_INPROGRESS)
114b775b3eeSreyk 		return (0);
115b775b3eeSreyk 
116b775b3eeSreyk 	if (cstr->addr_head.a == NULL) {
117b775b3eeSreyk 		priv_dns(IMSG_CONSTRAINT_DNS, cstr->addr_head.name, cstr->id);
118b775b3eeSreyk 		cstr->state = STATE_DNS_INPROGRESS;
119b775b3eeSreyk 		return (0);
120b775b3eeSreyk 	}
121b775b3eeSreyk 
122b775b3eeSreyk 	h = cstr->addr;
123bc58a738Sreyk 	switch (h->ss.ss_family) {
124bc58a738Sreyk 	case AF_INET:
125bc58a738Sreyk 		sa_in = (struct sockaddr_in *)&h->ss;
126bc58a738Sreyk 		if (ntohs(sa_in->sin_port) == 0)
127bc58a738Sreyk 			sa_in->sin_port = htons(443);
128bc58a738Sreyk 		cstr->state = STATE_DNS_DONE;
129bc58a738Sreyk 		break;
130bc58a738Sreyk 	case AF_INET6:
131bc58a738Sreyk 		sa_in6 = (struct sockaddr_in6 *)&h->ss;
132bc58a738Sreyk 		if (ntohs(sa_in6->sin6_port) == 0)
133bc58a738Sreyk 			sa_in6->sin6_port = htons(443);
134bc58a738Sreyk 		cstr->state = STATE_DNS_DONE;
135bc58a738Sreyk 		break;
136bc58a738Sreyk 	default:
137bc58a738Sreyk 		/* XXX king bula sez it? */
138bc58a738Sreyk 		fatalx("wrong AF in constraint_addr_init");
139bc58a738Sreyk 		/* NOTREACHED */
140bc58a738Sreyk 	}
141bc58a738Sreyk 
142b775b3eeSreyk 	return (1);
143bc58a738Sreyk }
144bc58a738Sreyk 
145c7e8e3a2Sotto void
146c7e8e3a2Sotto constraint_addr_head_clear(struct constraint *cstr)
147c7e8e3a2Sotto {
148c7e8e3a2Sotto 	host_dns_free(cstr->addr_head.a);
149c7e8e3a2Sotto 	cstr->addr_head.a = NULL;
150c7e8e3a2Sotto 	cstr->addr = NULL;
151c7e8e3a2Sotto }
152c7e8e3a2Sotto 
153bc58a738Sreyk int
15405b37b28Sotto constraint_query(struct constraint *cstr, int synced)
155bc58a738Sreyk {
156bc58a738Sreyk 	time_t			 now;
157a257dd04Sreyk 	struct ntp_addr_msg	 am;
158a257dd04Sreyk 	struct iovec		 iov[3];
159a257dd04Sreyk 	int			 iov_cnt = 0;
160bc58a738Sreyk 
161bc58a738Sreyk 	now = getmonotime();
162bc58a738Sreyk 
1631338eb9bSreyk 	switch (cstr->state) {
1641338eb9bSreyk 	case STATE_DNS_DONE:
1651338eb9bSreyk 		/* Proceed and query the time */
1661338eb9bSreyk 		break;
167b775b3eeSreyk 	case STATE_DNS_TEMPFAIL:
16809f868acSotto 		if (now > cstr->last + (cstr->dnstries >= TRIES_AUTO_DNSFAIL ?
16909f868acSotto 		    CONSTRAINT_RETRY_INTERVAL : INTERVAL_AUIO_DNSFAIL)) {
17009f868acSotto 			cstr->dnstries++;
171b775b3eeSreyk 			/* Retry resolving the address */
172b775b3eeSreyk 			constraint_init(cstr);
173841516aaSotto 			return 0;
174841516aaSotto 		}
175b775b3eeSreyk 		return (-1);
1761338eb9bSreyk 	case STATE_QUERY_SENT:
177bc58a738Sreyk 		if (cstr->last + CONSTRAINT_SCAN_TIMEOUT > now) {
178bc58a738Sreyk 			/* The caller should expect a reply */
179bc58a738Sreyk 			return (0);
180bc58a738Sreyk 		}
181bc58a738Sreyk 
1821338eb9bSreyk 		/* Timeout, just kill the process to reset it. */
1830a1ac5ecSreyk 		imsg_compose(ibuf_main, IMSG_CONSTRAINT_KILL,
1840a1ac5ecSreyk 		    cstr->id, 0, -1, NULL, 0);
1850a1ac5ecSreyk 
1860a1ac5ecSreyk 		cstr->state = STATE_TIMEOUT;
187bc58a738Sreyk 		return (-1);
1881338eb9bSreyk 	case STATE_INVALID:
1891338eb9bSreyk 		if (cstr->last + CONSTRAINT_SCAN_INTERVAL > now) {
1901338eb9bSreyk 			/* Nothing to do */
1911338eb9bSreyk 			return (-1);
1921338eb9bSreyk 		}
1931338eb9bSreyk 
1941338eb9bSreyk 		/* Reset and retry */
1951338eb9bSreyk 		cstr->senderrors = 0;
196a257dd04Sreyk 		constraint_close(cstr->id);
1971338eb9bSreyk 		break;
1981338eb9bSreyk 	case STATE_REPLY_RECEIVED:
1991338eb9bSreyk 	default:
2001338eb9bSreyk 		/* Nothing to do */
2011338eb9bSreyk 		return (-1);
202bc58a738Sreyk 	}
203bc58a738Sreyk 
204bc58a738Sreyk 	cstr->last = now;
205a257dd04Sreyk 	cstr->state = STATE_QUERY_SENT;
206bc58a738Sreyk 
207a257dd04Sreyk 	memset(&am, 0, sizeof(am));
208a257dd04Sreyk 	memcpy(&am.a, cstr->addr, sizeof(am.a));
20905b37b28Sotto 	am.synced = synced;
210a257dd04Sreyk 
211a257dd04Sreyk 	iov[iov_cnt].iov_base = &am;
212a257dd04Sreyk 	iov[iov_cnt++].iov_len = sizeof(am);
213a257dd04Sreyk 	if (cstr->addr_head.name) {
214a257dd04Sreyk 		am.namelen = strlen(cstr->addr_head.name) + 1;
215a257dd04Sreyk 		iov[iov_cnt].iov_base = cstr->addr_head.name;
216a257dd04Sreyk 		iov[iov_cnt++].iov_len = am.namelen;
217a257dd04Sreyk 	}
218a257dd04Sreyk 	if (cstr->addr_head.path) {
219a257dd04Sreyk 		am.pathlen = strlen(cstr->addr_head.path) + 1;
220a257dd04Sreyk 		iov[iov_cnt].iov_base = cstr->addr_head.path;
221a257dd04Sreyk 		iov[iov_cnt++].iov_len = am.pathlen;
222a257dd04Sreyk 	}
223a257dd04Sreyk 
224a257dd04Sreyk 	imsg_composev(ibuf_main, IMSG_CONSTRAINT_QUERY,
225a257dd04Sreyk 	    cstr->id, 0, -1, iov, iov_cnt);
226a257dd04Sreyk 
227a257dd04Sreyk 	return (0);
228a257dd04Sreyk }
229a257dd04Sreyk 
230a257dd04Sreyk void
2315f14684eSrzalamena priv_constraint_msg(u_int32_t id, u_int8_t *data, size_t len, int argc,
2325f14684eSrzalamena     char **argv)
233a257dd04Sreyk {
234a257dd04Sreyk 	struct ntp_addr_msg	 am;
235a257dd04Sreyk 	struct ntp_addr		*h;
236a257dd04Sreyk 	struct constraint	*cstr;
237a257dd04Sreyk 	int			 pipes[2];
238a257dd04Sreyk 
239a257dd04Sreyk 	if ((cstr = constraint_byid(id)) != NULL) {
240a257dd04Sreyk 		log_warnx("IMSG_CONSTRAINT_QUERY repeated for id %d", id);
241a257dd04Sreyk 		return;
242a257dd04Sreyk 	}
243a257dd04Sreyk 
244a257dd04Sreyk 	if (len < sizeof(am)) {
245a257dd04Sreyk 		log_warnx("invalid IMSG_CONSTRAINT_QUERY received");
246a257dd04Sreyk 		return;
247a257dd04Sreyk 	}
248a257dd04Sreyk 	memcpy(&am, data, sizeof(am));
249a257dd04Sreyk 	if (len != (sizeof(am) + am.namelen + am.pathlen)) {
250a257dd04Sreyk 		log_warnx("invalid IMSG_CONSTRAINT_QUERY received");
251a257dd04Sreyk 		return;
252a257dd04Sreyk 	}
253a257dd04Sreyk 	/* Additional imsg data is obtained in the unpriv child */
254a257dd04Sreyk 
255a257dd04Sreyk 	if ((h = calloc(1, sizeof(*h))) == NULL)
256a257dd04Sreyk 		fatal("calloc ntp_addr");
257a257dd04Sreyk 	memcpy(h, &am.a, sizeof(*h));
258a257dd04Sreyk 	h->next = NULL;
259a257dd04Sreyk 
260a257dd04Sreyk 	cstr = new_constraint();
261a257dd04Sreyk 	cstr->id = id;
262a257dd04Sreyk 	cstr->addr = h;
263a257dd04Sreyk 	cstr->addr_head.a = h;
264a257dd04Sreyk 	constraint_add(cstr);
265a257dd04Sreyk 	constraint_cnt++;
266bc58a738Sreyk 
2675f14684eSrzalamena 	if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, AF_UNSPEC,
2685f14684eSrzalamena 	    pipes) == -1)
269bc58a738Sreyk 		fatal("%s pipes", __func__);
270bc58a738Sreyk 
2715f14684eSrzalamena 	/* Prepare and send constraint data to child. */
2725f14684eSrzalamena 	cstr->fd = pipes[0];
273*f1b790a5Sclaudio 	if (imsgbuf_init(&cstr->ibuf, cstr->fd) == -1)
274*f1b790a5Sclaudio 		fatal("imsgbuf_init");
2755f14684eSrzalamena 	if (imsg_compose(&cstr->ibuf, IMSG_CONSTRAINT_QUERY, id, 0, -1,
2765f14684eSrzalamena 	    data, len) == -1)
2775f14684eSrzalamena 		fatal("%s: imsg_compose", __func__);
278a257dd04Sreyk 	/*
279a257dd04Sreyk 	 * Fork child handlers and make sure to do any sensitive work in the
280a257dd04Sreyk 	 * the (unprivileged) child.  The parent should not do any parsing,
281a257dd04Sreyk 	 * certificate loading etc.
282a257dd04Sreyk 	 */
2838c5c2965Srzalamena 	cstr->pid = start_child(CONSTRAINT_PROC_NAME, pipes[1], argc, argv);
284f3f62f4dSclaudio 
285f3f62f4dSclaudio 	if (imsgbuf_flush(&cstr->ibuf) == -1)
286f3f62f4dSclaudio 		fatal("imsgbuf_flush");
287f3f62f4dSclaudio }
288f3f62f4dSclaudio 
289f3f62f4dSclaudio static int
290f3f62f4dSclaudio imsgbuf_read_one(struct imsgbuf *imsgbuf, struct imsg *imsg)
291f3f62f4dSclaudio {
292f3f62f4dSclaudio 	while (1) {
293f3f62f4dSclaudio 		switch (imsg_get(imsgbuf, imsg)) {
294f3f62f4dSclaudio 		case -1:
295f3f62f4dSclaudio 			return (-1);
296f3f62f4dSclaudio 		case 0:
297f3f62f4dSclaudio 			break;
298f3f62f4dSclaudio 		default:
299f3f62f4dSclaudio 			return (1);
300f3f62f4dSclaudio 		}
301f3f62f4dSclaudio 
302f3f62f4dSclaudio 		switch (imsgbuf_read(imsgbuf)) {
303f3f62f4dSclaudio 		case -1:
304f3f62f4dSclaudio 			return (-1);
305f3f62f4dSclaudio 		case 0:
306f3f62f4dSclaudio 			return (0);
307f3f62f4dSclaudio 		}
308f3f62f4dSclaudio 	}
309a257dd04Sreyk }
310a257dd04Sreyk 
311a257dd04Sreyk void
3125f14684eSrzalamena priv_constraint_readquery(struct constraint *cstr, struct ntp_addr_msg *am,
3135f14684eSrzalamena     uint8_t **data)
314a257dd04Sreyk {
3155f14684eSrzalamena 	struct ntp_addr		*h;
3165f14684eSrzalamena 	uint8_t			*dptr;
3175f14684eSrzalamena 	struct imsg		 imsg;
3185f14684eSrzalamena 	size_t			 mlen;
3195f14684eSrzalamena 
3205f14684eSrzalamena 	/* Read the message our parent left us. */
321f3f62f4dSclaudio 	switch (imsgbuf_read_one(&cstr->ibuf, &imsg)) {
322f3f62f4dSclaudio 	case -1:
323f3f62f4dSclaudio 		fatal("%s: imsgbuf_read_one", __func__);
324f3f62f4dSclaudio 	case 0:
325f3f62f4dSclaudio 		fatalx("%s: imsgbuf_read_one: connection closed", __func__);
326f3f62f4dSclaudio 	}
3275f14684eSrzalamena 	if (imsg.hdr.type != IMSG_CONSTRAINT_QUERY)
3285f14684eSrzalamena 		fatalx("%s: invalid message type", __func__);
3295f14684eSrzalamena 
3305f14684eSrzalamena 	/*
3315f14684eSrzalamena 	 * Copy the message contents just like our father:
3325f14684eSrzalamena 	 * priv_constraint_msg().
3335f14684eSrzalamena 	 */
3345f14684eSrzalamena 	mlen = imsg.hdr.len - IMSG_HEADER_SIZE;
3355f14684eSrzalamena 	if (mlen < sizeof(*am))
3365f14684eSrzalamena 		fatalx("%s: mlen < sizeof(*am)", __func__);
3375f14684eSrzalamena 
3385f14684eSrzalamena 	memcpy(am, imsg.data, sizeof(*am));
3395f14684eSrzalamena 	if (mlen != (sizeof(*am) + am->namelen + am->pathlen))
3405f14684eSrzalamena 		fatalx("%s: mlen < sizeof(*am) + am->namelen + am->pathlen",
3415f14684eSrzalamena 		    __func__);
3425f14684eSrzalamena 
3435f14684eSrzalamena 	if ((h = calloc(1, sizeof(*h))) == NULL ||
3445f14684eSrzalamena 	    (*data = calloc(1, mlen)) == NULL)
3455f14684eSrzalamena 		fatal("%s: calloc", __func__);
3465f14684eSrzalamena 
3475f14684eSrzalamena 	memcpy(h, &am->a, sizeof(*h));
3485f14684eSrzalamena 	h->next = NULL;
3495f14684eSrzalamena 
3505f14684eSrzalamena 	cstr->id = imsg.hdr.peerid;
3515f14684eSrzalamena 	cstr->addr = h;
3525f14684eSrzalamena 	cstr->addr_head.a = h;
3535f14684eSrzalamena 
3545f14684eSrzalamena 	dptr = imsg.data;
3555f14684eSrzalamena 	memcpy(*data, dptr + sizeof(*am), mlen - sizeof(*am));
3565f14684eSrzalamena 	imsg_free(&imsg);
3575f14684eSrzalamena }
3585f14684eSrzalamena 
3595f14684eSrzalamena void
3605f14684eSrzalamena priv_constraint_child(const char *pw_dir, uid_t pw_uid, gid_t pw_gid)
3615f14684eSrzalamena {
3629efcf0f3Srzalamena 	struct constraint	 cstr;
3639efcf0f3Srzalamena 	struct ntp_addr_msg	 am;
3645f14684eSrzalamena 	uint8_t			*data;
36551d66267Sjsing 	static char		 addr[NI_MAXHOST];
366a257dd04Sreyk 	struct timeval		 rectv, xmttv;
367a257dd04Sreyk 	struct sigaction	 sa;
368a257dd04Sreyk 	void			*ctx;
369a257dd04Sreyk 	struct iovec		 iov[2];
370dd7efffeSclaudio 	int			 i;
371a257dd04Sreyk 
372579813e4Sreyk 	log_procinit("constraint");
373579813e4Sreyk 
374a257dd04Sreyk 	if (setpriority(PRIO_PROCESS, 0, 0) == -1)
375a257dd04Sreyk 		log_warn("could not set priority");
376a257dd04Sreyk 
3775df30466Skn 	/* load CA certs before chroot() */
378fb0a89eeStedu 	if ((conf->ca = tls_load_file(tls_default_ca_cert_file(),
379a257dd04Sreyk 	    &conf->ca_len, NULL)) == NULL)
3809083f23cSjsing 		fatalx("failed to load constraint ca");
381a257dd04Sreyk 
382b1da763cSderaadt 	if (chroot(pw_dir) == -1)
383a257dd04Sreyk 		fatal("chroot");
384a257dd04Sreyk 	if (chdir("/") == -1)
385a257dd04Sreyk 		fatal("chdir(\"/\")");
386a257dd04Sreyk 
387b1da763cSderaadt 	if (setgroups(1, &pw_gid) ||
388b1da763cSderaadt 	    setresgid(pw_gid, pw_gid, pw_gid) ||
389b1da763cSderaadt 	    setresuid(pw_uid, pw_uid, pw_uid))
390a257dd04Sreyk 		fatal("can't drop privileges");
391a257dd04Sreyk 
392a257dd04Sreyk 	/* Reset all signal handlers */
393a257dd04Sreyk 	memset(&sa, 0, sizeof(sa));
394a257dd04Sreyk 	sigemptyset(&sa.sa_mask);
395a257dd04Sreyk 	sa.sa_flags = SA_RESTART;
396a257dd04Sreyk 	sa.sa_handler = SIG_DFL;
397a257dd04Sreyk 	for (i = 1; i < _NSIG; i++)
398a257dd04Sreyk 		sigaction(i, &sa, NULL);
399bc58a738Sreyk 
400d2f9ff44Sderaadt 	if (pledge("stdio inet", NULL) == -1)
401a257dd04Sreyk 		fatal("pledge");
402d2f9ff44Sderaadt 
4039efcf0f3Srzalamena 	cstr.fd = CONSTRAINT_PASSFD;
404*f1b790a5Sclaudio 	if (imsgbuf_init(&cstr.ibuf, cstr.fd) == -1)
405*f1b790a5Sclaudio 		fatal("imsgbuf_init");
4069efcf0f3Srzalamena 	priv_constraint_readquery(&cstr, &am, &data);
4075f14684eSrzalamena 
408b7f2e836Sreyk 	/*
409b7f2e836Sreyk 	 * Get the IP address as name and set the process title accordingly.
410b7f2e836Sreyk 	 * This only converts an address into a string and does not trigger
411b7f2e836Sreyk 	 * any DNS operation, so it is safe to be called without the dns
412b7f2e836Sreyk 	 * pledge.
413b7f2e836Sreyk 	 */
4149efcf0f3Srzalamena 	if (getnameinfo((struct sockaddr *)&cstr.addr->ss,
4159efcf0f3Srzalamena 	    SA_LEN((struct sockaddr *)&cstr.addr->ss),
41651d66267Sjsing 	    addr, sizeof(addr), NULL, 0,
417a257dd04Sreyk 	    NI_NUMERICHOST) != 0)
418a257dd04Sreyk 		fatalx("%s getnameinfo", __func__);
419a257dd04Sreyk 
42051d66267Sjsing 	log_debug("constraint request to %s", addr);
42151d66267Sjsing 	setproctitle("constraint from %s", addr);
422bc58a738Sreyk 	(void)closefrom(CONSTRAINT_PASSFD + 1);
423bc58a738Sreyk 
424b7f2e836Sreyk 	/*
425b7f2e836Sreyk 	 * Set the close-on-exec flag to prevent leaking the communication
426b7f2e836Sreyk 	 * channel to any exec'ed child.  In theory this could never happen,
427b7f2e836Sreyk 	 * constraints don't exec children and pledge() prevents it,
428b7f2e836Sreyk 	 * but we keep it as a safety belt; especially for portability.
429b7f2e836Sreyk 	 */
430bc58a738Sreyk 	if (fcntl(CONSTRAINT_PASSFD, F_SETFD, FD_CLOEXEC) == -1)
431bc58a738Sreyk 		fatal("%s fcntl F_SETFD", __func__);
432bc58a738Sreyk 
433a257dd04Sreyk 	/* Get remaining data from imsg in the unpriv child */
4349efcf0f3Srzalamena 	if (am.namelen) {
4359efcf0f3Srzalamena 		if ((cstr.addr_head.name =
4369efcf0f3Srzalamena 		    get_string(data, am.namelen)) == NULL)
437a257dd04Sreyk 			fatalx("invalid IMSG_CONSTRAINT_QUERY name");
4389efcf0f3Srzalamena 		data += am.namelen;
439a257dd04Sreyk 	}
4409efcf0f3Srzalamena 	if (am.pathlen) {
4419efcf0f3Srzalamena 		if ((cstr.addr_head.path =
4429efcf0f3Srzalamena 		    get_string(data, am.pathlen)) == NULL)
443a257dd04Sreyk 			fatalx("invalid IMSG_CONSTRAINT_QUERY path");
444a257dd04Sreyk 	}
445a257dd04Sreyk 
446a257dd04Sreyk 	/* Run! */
44751d66267Sjsing 	if ((ctx = httpsdate_query(addr,
4489efcf0f3Srzalamena 	    CONSTRAINT_PORT, cstr.addr_head.name, cstr.addr_head.path,
44905b37b28Sotto 	    conf->ca, conf->ca_len, &rectv, &xmttv, am.synced)) == NULL) {
450bc58a738Sreyk 		/* Abort with failure but without warning */
451bc58a738Sreyk 		exit(1);
452bc58a738Sreyk 	}
453bc58a738Sreyk 
454bc58a738Sreyk 	iov[0].iov_base = &rectv;
455bc58a738Sreyk 	iov[0].iov_len = sizeof(rectv);
456bc58a738Sreyk 	iov[1].iov_base = &xmttv;
457bc58a738Sreyk 	iov[1].iov_len = sizeof(xmttv);
4589efcf0f3Srzalamena 	imsg_composev(&cstr.ibuf,
459a257dd04Sreyk 	    IMSG_CONSTRAINT_RESULT, 0, 0, -1, iov, 2);
460dd7efffeSclaudio 	imsgbuf_flush(&cstr.ibuf);
461bc58a738Sreyk 
462bc58a738Sreyk 	/* Tear down the TLS connection after sending the result */
463bc58a738Sreyk 	httpsdate_free(ctx);
4645f14684eSrzalamena 
4655f14684eSrzalamena 	exit(0);
466bc58a738Sreyk }
467bc58a738Sreyk 
468bc58a738Sreyk void
469a257dd04Sreyk priv_constraint_check_child(pid_t pid, int status)
470bc58a738Sreyk {
471bc58a738Sreyk 	struct constraint	*cstr;
47231f765bfSderaadt 	int			 fail, sig;
4730a1ac5ecSreyk 	char			*signame;
474bc58a738Sreyk 
47531f765bfSderaadt 	fail = sig = 0;
476bc58a738Sreyk 	if (WIFSIGNALED(status)) {
47731f765bfSderaadt 		sig = WTERMSIG(status);
478bc58a738Sreyk 	} else if (WIFEXITED(status)) {
47991e60ad5Sbcook 		if (WEXITSTATUS(status) != 0)
480bc58a738Sreyk 			fail = 1;
481bc58a738Sreyk 	} else
482bc58a738Sreyk 		fatalx("unexpected cause of SIGCHLD");
483bc58a738Sreyk 
484bc58a738Sreyk 	if ((cstr = constraint_bypid(pid)) != NULL) {
4850a1ac5ecSreyk 		if (sig) {
4860a1ac5ecSreyk 			if (sig != SIGTERM) {
4870a1ac5ecSreyk 				signame = strsignal(sig) ?
4880a1ac5ecSreyk 				    strsignal(sig) : "unknown";
4890a1ac5ecSreyk 				log_warnx("constraint %s; "
4900a1ac5ecSreyk 				    "terminated with signal %d (%s)",
4918745f5cfSotto 				    log_ntp_addr(cstr->addr), sig, signame);
4920a1ac5ecSreyk 			}
4930a1ac5ecSreyk 			fail = 1;
4940a1ac5ecSreyk 		}
495bc58a738Sreyk 
496a257dd04Sreyk 		priv_constraint_close(cstr->fd, fail);
497bc58a738Sreyk 	}
498bc58a738Sreyk }
499bc58a738Sreyk 
5000a1ac5ecSreyk void
5010a1ac5ecSreyk priv_constraint_kill(u_int32_t id)
5020a1ac5ecSreyk {
5030a1ac5ecSreyk 	struct constraint	*cstr;
5040a1ac5ecSreyk 
5050a1ac5ecSreyk 	if ((cstr = constraint_byid(id)) == NULL) {
5060a1ac5ecSreyk 		log_warnx("IMSG_CONSTRAINT_KILL for invalid id %d", id);
5070a1ac5ecSreyk 		return;
5080a1ac5ecSreyk 	}
5090a1ac5ecSreyk 
5100a1ac5ecSreyk 	kill(cstr->pid, SIGTERM);
5110a1ac5ecSreyk }
5120a1ac5ecSreyk 
513bc58a738Sreyk struct constraint *
514b775b3eeSreyk constraint_byid(u_int32_t id)
515b775b3eeSreyk {
516b775b3eeSreyk 	struct constraint	*cstr;
517b775b3eeSreyk 
518b775b3eeSreyk 	TAILQ_FOREACH(cstr, &conf->constraints, entry) {
519b775b3eeSreyk 		if (cstr->id == id)
520b775b3eeSreyk 			return (cstr);
521b775b3eeSreyk 	}
522b775b3eeSreyk 
523b775b3eeSreyk 	return (NULL);
524b775b3eeSreyk }
525b775b3eeSreyk 
526b775b3eeSreyk struct constraint *
527bc58a738Sreyk constraint_byfd(int fd)
528bc58a738Sreyk {
529bc58a738Sreyk 	struct constraint	*cstr;
530bc58a738Sreyk 
531bc58a738Sreyk 	TAILQ_FOREACH(cstr, &conf->constraints, entry) {
532bc58a738Sreyk 		if (cstr->fd == fd)
533bc58a738Sreyk 			return (cstr);
534bc58a738Sreyk 	}
535bc58a738Sreyk 
536bc58a738Sreyk 	return (NULL);
537bc58a738Sreyk }
538bc58a738Sreyk 
539bc58a738Sreyk struct constraint *
540bc58a738Sreyk constraint_bypid(pid_t pid)
541bc58a738Sreyk {
542bc58a738Sreyk 	struct constraint	*cstr;
543bc58a738Sreyk 
544bc58a738Sreyk 	TAILQ_FOREACH(cstr, &conf->constraints, entry) {
545bc58a738Sreyk 		if (cstr->pid == pid)
546bc58a738Sreyk 			return (cstr);
547bc58a738Sreyk 	}
548bc58a738Sreyk 
549bc58a738Sreyk 	return (NULL);
550bc58a738Sreyk }
551bc58a738Sreyk 
552bc58a738Sreyk int
553a257dd04Sreyk constraint_close(u_int32_t id)
554bc58a738Sreyk {
555bc58a738Sreyk 	struct constraint	*cstr;
556bc58a738Sreyk 
557a257dd04Sreyk 	if ((cstr = constraint_byid(id)) == NULL) {
558a257dd04Sreyk 		log_warn("%s: id %d: not found", __func__, id);
559bc58a738Sreyk 		return (0);
560bc58a738Sreyk 	}
561bc58a738Sreyk 
5623303745eSreyk 	cstr->last = getmonotime();
5633303745eSreyk 
5643303745eSreyk 	if (cstr->addr == NULL || (cstr->addr = cstr->addr->next) == NULL) {
5653303745eSreyk 		/* Either a pool or all addresses have been tried */
5663303745eSreyk 		cstr->addr = cstr->addr_head.a;
567bc58a738Sreyk 		if (cstr->senderrors)
568bc58a738Sreyk 			cstr->state = STATE_INVALID;
569bc58a738Sreyk 		else if (cstr->state >= STATE_QUERY_SENT)
570bc58a738Sreyk 			cstr->state = STATE_DNS_DONE;
571bc58a738Sreyk 
572bc58a738Sreyk 		return (1);
573bc58a738Sreyk 	}
574bc58a738Sreyk 
5753303745eSreyk 	return (constraint_init(cstr));
5763303745eSreyk }
5773303745eSreyk 
578b775b3eeSreyk void
579a257dd04Sreyk priv_constraint_close(int fd, int fail)
580a257dd04Sreyk {
581a257dd04Sreyk 	struct constraint	*cstr;
582a257dd04Sreyk 	u_int32_t		 id;
583a257dd04Sreyk 
584a257dd04Sreyk 	if ((cstr = constraint_byfd(fd)) == NULL) {
585a257dd04Sreyk 		log_warn("%s: fd %d: not found", __func__, fd);
586a257dd04Sreyk 		return;
587a257dd04Sreyk 	}
588a257dd04Sreyk 
589a257dd04Sreyk 	id = cstr->id;
590a257dd04Sreyk 	constraint_remove(cstr);
591a257dd04Sreyk 	constraint_cnt--;
592a257dd04Sreyk 
593a257dd04Sreyk 	imsg_compose(ibuf, IMSG_CONSTRAINT_CLOSE, id, 0, -1,
594a257dd04Sreyk 	    &fail, sizeof(fail));
595a257dd04Sreyk }
596a257dd04Sreyk 
597a257dd04Sreyk void
598b775b3eeSreyk constraint_add(struct constraint *cstr)
599b775b3eeSreyk {
600b775b3eeSreyk 	TAILQ_INSERT_TAIL(&conf->constraints, cstr, entry);
601b775b3eeSreyk }
602b775b3eeSreyk 
603b775b3eeSreyk void
604b775b3eeSreyk constraint_remove(struct constraint *cstr)
605b775b3eeSreyk {
606b775b3eeSreyk 	TAILQ_REMOVE(&conf->constraints, cstr, entry);
607a257dd04Sreyk 
6089cbf9e90Sclaudio 	imsgbuf_clear(&cstr->ibuf);
609a257dd04Sreyk 	if (cstr->fd != -1)
610a257dd04Sreyk 		close(cstr->fd);
611b775b3eeSreyk 	free(cstr->addr_head.name);
612b775b3eeSreyk 	free(cstr->addr_head.path);
613d746074eSderaadt 	free(cstr->addr);
614b775b3eeSreyk 	free(cstr);
615b775b3eeSreyk }
616b775b3eeSreyk 
617a257dd04Sreyk void
618a257dd04Sreyk constraint_purge(void)
619a257dd04Sreyk {
620a257dd04Sreyk 	struct constraint	*cstr, *ncstr;
621a257dd04Sreyk 
622a257dd04Sreyk 	TAILQ_FOREACH_SAFE(cstr, &conf->constraints, entry, ncstr)
623a257dd04Sreyk 		constraint_remove(cstr);
624a257dd04Sreyk }
625a257dd04Sreyk 
626bc58a738Sreyk int
627a257dd04Sreyk priv_constraint_dispatch(struct pollfd *pfd)
628bc58a738Sreyk {
629bc58a738Sreyk 	struct imsg		 imsg;
630bc58a738Sreyk 	struct constraint	*cstr;
631bc58a738Sreyk 	ssize_t			 n;
632bc58a738Sreyk 	struct timeval		 tv[2];
633bc58a738Sreyk 
634bc58a738Sreyk 	if ((cstr = constraint_byfd(pfd->fd)) == NULL)
635bc58a738Sreyk 		return (0);
636bc58a738Sreyk 
637bc58a738Sreyk 	if (!(pfd->revents & POLLIN))
638bc58a738Sreyk 		return (0);
639bc58a738Sreyk 
640f3f62f4dSclaudio 	if (imsgbuf_read(&cstr->ibuf) != 1) {
641c7e8e3a2Sotto 		/* there's a race between SIGCHLD delivery and reading imsg
642c7e8e3a2Sotto 		   but if we've seen the reply, we're good */
643c7e8e3a2Sotto 		priv_constraint_close(pfd->fd, cstr->state !=
644c7e8e3a2Sotto 		    STATE_REPLY_RECEIVED);
645bc58a738Sreyk 		return (1);
646bc58a738Sreyk 	}
647bc58a738Sreyk 
648bc58a738Sreyk 	for (;;) {
649bc58a738Sreyk 		if ((n = imsg_get(&cstr->ibuf, &imsg)) == -1) {
650a257dd04Sreyk 			priv_constraint_close(pfd->fd, 1);
651bc58a738Sreyk 			return (1);
652bc58a738Sreyk 		}
653bc58a738Sreyk 		if (n == 0)
654bc58a738Sreyk 			break;
655bc58a738Sreyk 
656bc58a738Sreyk 		switch (imsg.hdr.type) {
657a257dd04Sreyk 		case IMSG_CONSTRAINT_RESULT:
658bc58a738Sreyk 			 if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(tv))
659bc58a738Sreyk 				fatalx("invalid IMSG_CONSTRAINT received");
660bc58a738Sreyk 
661c7e8e3a2Sotto 			/* state is maintained by child, but we want to
662c7e8e3a2Sotto 			   remember we've seen the result */
663c7e8e3a2Sotto 			cstr->state = STATE_REPLY_RECEIVED;
664a257dd04Sreyk 			/* forward imsg to ntp child, don't parse it here */
665a257dd04Sreyk 			imsg_compose(ibuf, imsg.hdr.type,
666a257dd04Sreyk 			    cstr->id, 0, -1, imsg.data, sizeof(tv));
667a257dd04Sreyk 			break;
668a257dd04Sreyk 		default:
669a257dd04Sreyk 			break;
670a257dd04Sreyk 		}
671a257dd04Sreyk 		imsg_free(&imsg);
672a257dd04Sreyk 	}
673a257dd04Sreyk 
674a257dd04Sreyk 	return (0);
675a257dd04Sreyk }
676a257dd04Sreyk 
677a257dd04Sreyk void
678a257dd04Sreyk constraint_msg_result(u_int32_t id, u_int8_t *data, size_t len)
679a257dd04Sreyk {
680a257dd04Sreyk 	struct constraint	*cstr;
681a257dd04Sreyk 	struct timeval		 tv[2];
682a257dd04Sreyk 	double			 offset;
683a257dd04Sreyk 
684a257dd04Sreyk 	if ((cstr = constraint_byid(id)) == NULL) {
685a257dd04Sreyk 		log_warnx("IMSG_CONSTRAINT_CLOSE with invalid constraint id");
686a257dd04Sreyk 		return;
687a257dd04Sreyk 	}
688a257dd04Sreyk 
689a257dd04Sreyk 	if (len != sizeof(tv)) {
690a257dd04Sreyk 		log_warnx("invalid IMSG_CONSTRAINT received");
691a257dd04Sreyk 		return;
692a257dd04Sreyk 	}
693a257dd04Sreyk 
694a257dd04Sreyk 	memcpy(tv, data, len);
695bc58a738Sreyk 
696bc58a738Sreyk 	offset = gettime_from_timeval(&tv[0]) -
697bc58a738Sreyk 	    gettime_from_timeval(&tv[1]);
698bc58a738Sreyk 
699bc58a738Sreyk 	log_info("constraint reply from %s: offset %f",
7008745f5cfSotto 	    log_ntp_addr(cstr->addr),
701bc58a738Sreyk 	    offset);
702bc58a738Sreyk 
703bc58a738Sreyk 	cstr->state = STATE_REPLY_RECEIVED;
704bc58a738Sreyk 	cstr->last = getmonotime();
705bc58a738Sreyk 	cstr->constraint = tv[0].tv_sec;
706bc58a738Sreyk 
707bc58a738Sreyk 	constraint_update();
708bc58a738Sreyk }
709bc58a738Sreyk 
710b775b3eeSreyk void
711a257dd04Sreyk constraint_msg_close(u_int32_t id, u_int8_t *data, size_t len)
712a257dd04Sreyk {
7131908d877Sotto 	struct constraint	*cstr, *tmp;
7141908d877Sotto 	int			 fail, cnt;
7151908d877Sotto 	static int		 total_fails;
716a257dd04Sreyk 
717a257dd04Sreyk 	if ((cstr = constraint_byid(id)) == NULL) {
718a257dd04Sreyk 		log_warnx("IMSG_CONSTRAINT_CLOSE with invalid constraint id");
719a257dd04Sreyk 		return;
720a257dd04Sreyk 	}
721a257dd04Sreyk 
722a257dd04Sreyk 	if (len != sizeof(int)) {
723a257dd04Sreyk 		log_warnx("invalid IMSG_CONSTRAINT_CLOSE received");
724a257dd04Sreyk 		return;
725a257dd04Sreyk 	}
726a257dd04Sreyk 
727a257dd04Sreyk 	memcpy(&fail, data, len);
728a257dd04Sreyk 
729a257dd04Sreyk 	if (fail) {
730a257dd04Sreyk 		log_debug("no constraint reply from %s"
731a257dd04Sreyk 		    " received in time, next query %ds",
7328745f5cfSotto 		    log_ntp_addr(cstr->addr),
7338745f5cfSotto 		    CONSTRAINT_SCAN_INTERVAL);
7341908d877Sotto 
7351908d877Sotto 		cnt = 0;
7361908d877Sotto 		TAILQ_FOREACH(tmp, &conf->constraints, entry)
7371908d877Sotto 			cnt++;
7381908d877Sotto 		if (cnt > 0 && ++total_fails >= cnt &&
7391908d877Sotto 		    conf->constraint_median == 0) {
740d2460e3aSotto 			log_warnx("constraints configured but none available");
7411908d877Sotto 			total_fails = 0;
7421908d877Sotto 		}
743a257dd04Sreyk 	}
744a257dd04Sreyk 
745a257dd04Sreyk 	if (fail || cstr->state < STATE_QUERY_SENT) {
746a257dd04Sreyk 		cstr->senderrors++;
747a257dd04Sreyk 		constraint_close(cstr->id);
748a257dd04Sreyk 	}
749a257dd04Sreyk }
750a257dd04Sreyk 
751a257dd04Sreyk void
752a257dd04Sreyk constraint_msg_dns(u_int32_t id, u_int8_t *data, size_t len)
753b775b3eeSreyk {
7543303745eSreyk 	struct constraint	*cstr, *ncstr = NULL;
755b775b3eeSreyk 	u_int8_t		*p;
756b775b3eeSreyk 	struct ntp_addr		*h;
757b775b3eeSreyk 
758b775b3eeSreyk 	if ((cstr = constraint_byid(id)) == NULL) {
759c7e8e3a2Sotto 		log_debug("IMSG_CONSTRAINT_DNS with invalid constraint id");
760b775b3eeSreyk 		return;
761b775b3eeSreyk 	}
762b775b3eeSreyk 	if (cstr->addr != NULL) {
763b775b3eeSreyk 		log_warnx("IMSG_CONSTRAINT_DNS but addr != NULL!");
764b775b3eeSreyk 		return;
765b775b3eeSreyk 	}
766b775b3eeSreyk 	if (len == 0) {
767b775b3eeSreyk 		log_debug("%s FAILED", __func__);
768b775b3eeSreyk 		cstr->state = STATE_DNS_TEMPFAIL;
769b775b3eeSreyk 		return;
770b775b3eeSreyk 	}
771b775b3eeSreyk 
772b98b0a5cSotto 	if (len % (sizeof(struct sockaddr_storage) + sizeof(int)) != 0)
773b775b3eeSreyk 		fatalx("IMSG_CONSTRAINT_DNS len");
774b775b3eeSreyk 
775c7e8e3a2Sotto 	if (cstr->addr_head.pool) {
776c7e8e3a2Sotto 		struct constraint *n, *tmp;
777c7e8e3a2Sotto 		TAILQ_FOREACH_SAFE(n, &conf->constraints, entry, tmp) {
778c7e8e3a2Sotto 			if (cstr->id == n->id)
779c7e8e3a2Sotto 				continue;
780c7e8e3a2Sotto 			if (cstr->addr_head.pool == n->addr_head.pool)
781c7e8e3a2Sotto 				constraint_remove(n);
782c7e8e3a2Sotto 		}
783c7e8e3a2Sotto 	}
784c7e8e3a2Sotto 
785b775b3eeSreyk 	p = data;
786b775b3eeSreyk 	do {
787b775b3eeSreyk 		if ((h = calloc(1, sizeof(*h))) == NULL)
788b775b3eeSreyk 			fatal("calloc ntp_addr");
789b775b3eeSreyk 		memcpy(&h->ss, p, sizeof(h->ss));
790b775b3eeSreyk 		p += sizeof(h->ss);
791b775b3eeSreyk 		len -= sizeof(h->ss);
792b98b0a5cSotto 		memcpy(&h->notauth, p, sizeof(int));
793b98b0a5cSotto 		p += sizeof(int);
794b98b0a5cSotto 		len -= sizeof(int);
795b775b3eeSreyk 
7963303745eSreyk 		if (ncstr == NULL || cstr->addr_head.pool) {
797b775b3eeSreyk 			ncstr = new_constraint();
798b775b3eeSreyk 			ncstr->addr = h;
799b775b3eeSreyk 			ncstr->addr_head.a = h;
800b775b3eeSreyk 			ncstr->addr_head.name = strdup(cstr->addr_head.name);
801b775b3eeSreyk 			ncstr->addr_head.path = strdup(cstr->addr_head.path);
802b775b3eeSreyk 			if (ncstr->addr_head.name == NULL ||
803b775b3eeSreyk 			    ncstr->addr_head.path == NULL)
804b775b3eeSreyk 				fatal("calloc name");
805b775b3eeSreyk 			ncstr->addr_head.pool = cstr->addr_head.pool;
8063303745eSreyk 			ncstr->state = STATE_DNS_DONE;
807b775b3eeSreyk 			constraint_add(ncstr);
8083303745eSreyk 			constraint_cnt += constraint_init(ncstr);
8093303745eSreyk 		} else {
8103303745eSreyk 			h->next = ncstr->addr;
8113303745eSreyk 			ncstr->addr = h;
8123303745eSreyk 			ncstr->addr_head.a = h;
8133303745eSreyk 		}
8143303745eSreyk 	} while (len);
815b775b3eeSreyk 
816b775b3eeSreyk 	constraint_remove(cstr);
817b775b3eeSreyk }
818b775b3eeSreyk 
819bc58a738Sreyk int
820bc58a738Sreyk constraint_cmp(const void *a, const void *b)
821bc58a738Sreyk {
822cf493856Sotto 	time_t at = *(const time_t *)a;
823cf493856Sotto 	time_t bt = *(const time_t *)b;
824cf493856Sotto 	return at < bt ? -1 : (at > bt ? 1 : 0);
825bc58a738Sreyk }
826bc58a738Sreyk 
827bc58a738Sreyk void
828bc58a738Sreyk constraint_update(void)
829bc58a738Sreyk {
830bc58a738Sreyk 	struct constraint *cstr;
831bc58a738Sreyk 	int	 cnt, i;
832e6588cf8Sotto 	time_t	*values;
833bc58a738Sreyk 	time_t	 now;
834bc58a738Sreyk 
835bc58a738Sreyk 	now = getmonotime();
836bc58a738Sreyk 
837bc58a738Sreyk 	cnt = 0;
838bc58a738Sreyk 	TAILQ_FOREACH(cstr, &conf->constraints, entry) {
839bc58a738Sreyk 		if (cstr->state != STATE_REPLY_RECEIVED)
840bc58a738Sreyk 			continue;
841bc58a738Sreyk 		cnt++;
842bc58a738Sreyk 	}
843e6588cf8Sotto 	if (cnt == 0)
844e6588cf8Sotto 		return;
845bc58a738Sreyk 
846e6588cf8Sotto 	if ((values = calloc(cnt, sizeof(time_t))) == NULL)
847bc58a738Sreyk 		fatal("calloc");
848bc58a738Sreyk 
849bc58a738Sreyk 	i = 0;
850bc58a738Sreyk 	TAILQ_FOREACH(cstr, &conf->constraints, entry) {
851bc58a738Sreyk 		if (cstr->state != STATE_REPLY_RECEIVED)
852bc58a738Sreyk 			continue;
853e6588cf8Sotto 		values[i++] = cstr->constraint + (now - cstr->last);
854bc58a738Sreyk 	}
855bc58a738Sreyk 
856e6588cf8Sotto 	qsort(values, cnt, sizeof(time_t), constraint_cmp);
857bc58a738Sreyk 
858bc58a738Sreyk 	/* calculate median */
859bc58a738Sreyk 	i = cnt / 2;
860bc58a738Sreyk 	if (cnt % 2 == 0)
861e6588cf8Sotto 		conf->constraint_median = (values[i - 1] + values[i]) / 2;
862e6588cf8Sotto 	else
863e6588cf8Sotto 		conf->constraint_median = values[i];
864bc58a738Sreyk 
865bc58a738Sreyk 	conf->constraint_last = now;
866bc58a738Sreyk 
867e6588cf8Sotto 	free(values);
868bc58a738Sreyk }
869bc58a738Sreyk 
870bc58a738Sreyk void
871bc58a738Sreyk constraint_reset(void)
872bc58a738Sreyk {
873bc58a738Sreyk 	struct constraint *cstr;
874bc58a738Sreyk 
875bc58a738Sreyk 	TAILQ_FOREACH(cstr, &conf->constraints, entry) {
876bc58a738Sreyk 		if (cstr->state == STATE_QUERY_SENT)
877bc58a738Sreyk 			continue;
878a257dd04Sreyk 		constraint_close(cstr->id);
879c7e8e3a2Sotto 		constraint_addr_head_clear(cstr);
880c7e8e3a2Sotto 		constraint_init(cstr);
881bc58a738Sreyk 	}
882bc58a738Sreyk 	conf->constraint_errors = 0;
883bc58a738Sreyk }
884bc58a738Sreyk 
885bc58a738Sreyk int
886bc58a738Sreyk constraint_check(double val)
887bc58a738Sreyk {
888bc58a738Sreyk 	struct timeval	tv;
889e6588cf8Sotto 	double		diff;
890bc58a738Sreyk 	time_t		now;
891bc58a738Sreyk 
892bc58a738Sreyk 	if (conf->constraint_median == 0)
893bc58a738Sreyk 		return (0);
894bc58a738Sreyk 
895bc58a738Sreyk 	/* Calculate the constraint with the current offset */
896bc58a738Sreyk 	now = getmonotime();
897bc58a738Sreyk 	tv.tv_sec = conf->constraint_median + (now - conf->constraint_last);
898bc58a738Sreyk 	tv.tv_usec = 0;
899e6588cf8Sotto 	diff = fabs(val - gettime_from_timeval(&tv));
900bc58a738Sreyk 
901e6588cf8Sotto 	if (diff > CONSTRAINT_MARGIN) {
902dcbb241cSreyk 		if (conf->constraint_errors++ >
903dcbb241cSreyk 		    (CONSTRAINT_ERROR_MARGIN * peer_cnt)) {
904bc58a738Sreyk 			constraint_reset();
905bc58a738Sreyk 		}
906bc58a738Sreyk 
907bc58a738Sreyk 		return (-1);
908bc58a738Sreyk 	}
909bc58a738Sreyk 
910bc58a738Sreyk 	return (0);
911bc58a738Sreyk }
912bc58a738Sreyk 
913bc58a738Sreyk struct httpsdate *
91451d66267Sjsing httpsdate_init(const char *addr, const char *port, const char *hostname,
91505b37b28Sotto     const char *path, const u_int8_t *ca, size_t ca_len, int synced)
916bc58a738Sreyk {
917bc58a738Sreyk 	struct httpsdate	*httpsdate = NULL;
918bc58a738Sreyk 
919bc58a738Sreyk 	if ((httpsdate = calloc(1, sizeof(*httpsdate))) == NULL)
920bc58a738Sreyk 		goto fail;
921bc58a738Sreyk 
92251d66267Sjsing 	if (hostname == NULL)
92351d66267Sjsing 		hostname = addr;
924bc58a738Sreyk 
92551d66267Sjsing 	if ((httpsdate->tls_addr = strdup(addr)) == NULL ||
926bc58a738Sreyk 	    (httpsdate->tls_port = strdup(port)) == NULL ||
92751d66267Sjsing 	    (httpsdate->tls_hostname = strdup(hostname)) == NULL ||
928bc58a738Sreyk 	    (httpsdate->tls_path = strdup(path)) == NULL)
929bc58a738Sreyk 		goto fail;
930bc58a738Sreyk 
931bc58a738Sreyk 	if (asprintf(&httpsdate->tls_request,
932bc58a738Sreyk 	    "HEAD %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n",
93351d66267Sjsing 	    httpsdate->tls_path, httpsdate->tls_hostname) == -1)
934bc58a738Sreyk 		goto fail;
935bc58a738Sreyk 
936bc58a738Sreyk 	if ((httpsdate->tls_config = tls_config_new()) == NULL)
937bc58a738Sreyk 		goto fail;
9389b2f2e5bSjsing 	if (tls_config_set_ca_mem(httpsdate->tls_config, ca, ca_len) == -1)
939c7dffc0bSjsing 		goto fail;
940c7dffc0bSjsing 
941205dc5ecSjsing 	/*
942205dc5ecSjsing 	 * Due to the fact that we're trying to determine a constraint for time
943205dc5ecSjsing 	 * we do our own certificate validity checking, since the automatic
944205dc5ecSjsing 	 * version is based on our wallclock, which may well be inaccurate...
945205dc5ecSjsing 	 */
94605b37b28Sotto 	if (!synced) {
94714c44c9eSotto 		log_debug("constraints: using received time in certificate validation");
948205dc5ecSjsing 		tls_config_insecure_noverifytime(httpsdate->tls_config);
94905b37b28Sotto 	}
950205dc5ecSjsing 
951bc58a738Sreyk 	return (httpsdate);
952bc58a738Sreyk 
953bc58a738Sreyk  fail:
954bc58a738Sreyk 	httpsdate_free(httpsdate);
955bc58a738Sreyk 	return (NULL);
956bc58a738Sreyk }
957bc58a738Sreyk 
958bc58a738Sreyk void
959bc58a738Sreyk httpsdate_free(void *arg)
960bc58a738Sreyk {
961bc58a738Sreyk 	struct httpsdate *httpsdate = arg;
962bc58a738Sreyk 	if (httpsdate == NULL)
963bc58a738Sreyk 		return;
964bc58a738Sreyk 	if (httpsdate->tls_ctx)
965bc58a738Sreyk 		tls_close(httpsdate->tls_ctx);
966bc58a738Sreyk 	tls_free(httpsdate->tls_ctx);
967bc58a738Sreyk 	tls_config_free(httpsdate->tls_config);
96851d66267Sjsing 	free(httpsdate->tls_addr);
969bc58a738Sreyk 	free(httpsdate->tls_port);
97051d66267Sjsing 	free(httpsdate->tls_hostname);
971bc58a738Sreyk 	free(httpsdate->tls_path);
972bc58a738Sreyk 	free(httpsdate->tls_request);
973bc58a738Sreyk 	free(httpsdate);
974bc58a738Sreyk }
975bc58a738Sreyk 
976bc58a738Sreyk int
97705b37b28Sotto httpsdate_request(struct httpsdate *httpsdate, struct timeval *when, int synced)
978bc58a738Sreyk {
979b7e9bd4fSjsing 	char	 timebuf1[32], timebuf2[32];
980a9fc612dSbluhm 	size_t	 outlen = 0, maxlength = CONSTRAINT_MAXHEADERLENGTH, len;
981a9fc612dSbluhm 	char	*line, *p, *buf;
982b7e9bd4fSjsing 	time_t	 httptime, notbefore, notafter;
983b7e9bd4fSjsing 	struct tm *tm;
984cd548fa4Sbeck 	ssize_t	 ret;
985bc58a738Sreyk 
986bc58a738Sreyk 	if ((httpsdate->tls_ctx = tls_client()) == NULL)
987bc58a738Sreyk 		goto fail;
988bc58a738Sreyk 
989bc58a738Sreyk 	if (tls_configure(httpsdate->tls_ctx, httpsdate->tls_config) == -1)
990bc58a738Sreyk 		goto fail;
991bc58a738Sreyk 
992b7f2e836Sreyk 	/*
993b7f2e836Sreyk 	 * libtls expects an address string, which can also be a DNS name,
994b7f2e836Sreyk 	 * but we pass a pre-resolved IP address string in tls_addr so it
995b7f2e836Sreyk 	 * does not trigger any DNS operation and is safe to be called
996b7f2e836Sreyk 	 * without the dns pledge.
997b7f2e836Sreyk 	 */
9989083f23cSjsing 	if (tls_connect_servername(httpsdate->tls_ctx, httpsdate->tls_addr,
9999083f23cSjsing 	    httpsdate->tls_port, httpsdate->tls_hostname) == -1) {
1000afaa0890Sderaadt 		log_debug("tls connect failed: %s (%s): %s",
10019083f23cSjsing 		    httpsdate->tls_addr, httpsdate->tls_hostname,
1002bc58a738Sreyk 		    tls_error(httpsdate->tls_ctx));
1003bc58a738Sreyk 		goto fail;
1004bc58a738Sreyk 	}
1005bc58a738Sreyk 
1006a9fc612dSbluhm 	buf = httpsdate->tls_request;
1007a9fc612dSbluhm 	len = strlen(httpsdate->tls_request);
1008a9fc612dSbluhm 	while (len > 0) {
1009caf85d1dSbeck 		ret = tls_write(httpsdate->tls_ctx, buf, len);
1010caf85d1dSbeck 		if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT)
1011a9fc612dSbluhm 			continue;
1012df69c215Sderaadt 		if (ret == -1) {
10139083f23cSjsing 			log_warnx("tls write failed: %s (%s): %s",
10149083f23cSjsing 			    httpsdate->tls_addr, httpsdate->tls_hostname,
10159083f23cSjsing 			    tls_error(httpsdate->tls_ctx));
1016bc58a738Sreyk 			goto fail;
10179083f23cSjsing 		}
1018caf85d1dSbeck 		buf += ret;
1019caf85d1dSbeck 		len -= ret;
1020a9fc612dSbluhm 	}
1021bc58a738Sreyk 
1022bc58a738Sreyk 	while ((line = tls_readline(httpsdate->tls_ctx, &outlen,
1023bc58a738Sreyk 	    &maxlength, when)) != NULL) {
1024bc58a738Sreyk 		line[strcspn(line, "\r\n")] = '\0';
1025bc58a738Sreyk 
1026bc58a738Sreyk 		if ((p = strchr(line, ' ')) == NULL || *p == '\0')
1027bc58a738Sreyk 			goto next;
1028bc58a738Sreyk 		*p++ = '\0';
1029bc58a738Sreyk 		if (strcasecmp("Date:", line) != 0)
1030bc58a738Sreyk 			goto next;
1031bc58a738Sreyk 
1032bc58a738Sreyk 		/*
1033bc58a738Sreyk 		 * Expect the date/time format as IMF-fixdate which is
1034bc58a738Sreyk 		 * mandated by HTTP/1.1 in the new RFC 7231 and was
1035bc58a738Sreyk 		 * preferred by RFC 2616.  Other formats would be RFC 850
1036bc58a738Sreyk 		 * or ANSI C's asctime() - the latter doesn't include
1037bc58a738Sreyk 		 * the timezone which is required here.
1038bc58a738Sreyk 		 */
1039b7e9bd4fSjsing 		if (strptime(p, IMF_FIXDATE,
1040bc58a738Sreyk 		    &httpsdate->tls_tm) == NULL) {
1041bc58a738Sreyk 			log_warnx("unsupported date format");
1042bc58a738Sreyk 			free(line);
1043b0ee91feSotto 			goto fail;
1044bc58a738Sreyk 		}
1045bc58a738Sreyk 
1046bc58a738Sreyk 		free(line);
1047bc58a738Sreyk 		break;
1048bc58a738Sreyk  next:
1049bc58a738Sreyk 		free(line);
1050bc58a738Sreyk 	}
1051b0ee91feSotto 	if (httpsdate->tls_tm.tm_year == 0)
1052b0ee91feSotto 		goto fail;
1053bc58a738Sreyk 
105405b37b28Sotto 	/* If we are synced, we already checked the certificate validity */
105505b37b28Sotto 	if (synced)
105605b37b28Sotto 		return 0;
105705b37b28Sotto 
1058205dc5ecSjsing 	/*
1059205dc5ecSjsing 	 * Now manually check the validity of the certificate presented in the
1060205dc5ecSjsing 	 * TLS handshake, based on the time specified by the server's HTTP Date:
1061205dc5ecSjsing 	 * header.
1062205dc5ecSjsing 	 */
1063b7e9bd4fSjsing 	notbefore = tls_peer_cert_notbefore(httpsdate->tls_ctx);
1064b7e9bd4fSjsing 	notafter = tls_peer_cert_notafter(httpsdate->tls_ctx);
10654dd4abe4Sjsing 	if ((httptime = timegm(&httpsdate->tls_tm)) == -1)
10664dd4abe4Sjsing 		goto fail;
1067b7e9bd4fSjsing 	if (httptime <= notbefore) {
1068b7e9bd4fSjsing 		if ((tm = gmtime(&notbefore)) == NULL)
1069b7e9bd4fSjsing 			goto fail;
1070b7e9bd4fSjsing 		if (strftime(timebuf1, sizeof(timebuf1), X509_DATE, tm) == 0)
1071b7e9bd4fSjsing 			goto fail;
1072b7e9bd4fSjsing 		if (strftime(timebuf2, sizeof(timebuf2), X509_DATE,
1073b7e9bd4fSjsing 		    &httpsdate->tls_tm) == 0)
1074b7e9bd4fSjsing 			goto fail;
1075b7e9bd4fSjsing 		log_warnx("tls certificate not yet valid: %s (%s): "
1076b7e9bd4fSjsing 		    "not before %s, now %s", httpsdate->tls_addr,
1077b7e9bd4fSjsing 		    httpsdate->tls_hostname, timebuf1, timebuf2);
1078b7e9bd4fSjsing 		goto fail;
1079b7e9bd4fSjsing 	}
1080b7e9bd4fSjsing 	if (httptime >= notafter) {
1081b7e9bd4fSjsing 		if ((tm = gmtime(&notafter)) == NULL)
1082b7e9bd4fSjsing 			goto fail;
1083b7e9bd4fSjsing 		if (strftime(timebuf1, sizeof(timebuf1), X509_DATE, tm) == 0)
1084b7e9bd4fSjsing 			goto fail;
1085b7e9bd4fSjsing 		if (strftime(timebuf2, sizeof(timebuf2), X509_DATE,
1086b7e9bd4fSjsing 		    &httpsdate->tls_tm) == 0)
1087b7e9bd4fSjsing 			goto fail;
1088b7e9bd4fSjsing 		log_warnx("tls certificate expired: %s (%s): "
1089b7e9bd4fSjsing 		    "not after %s, now %s", httpsdate->tls_addr,
1090b7e9bd4fSjsing 		    httpsdate->tls_hostname, timebuf1, timebuf2);
1091205dc5ecSjsing 		goto fail;
1092205dc5ecSjsing 	}
1093205dc5ecSjsing 
1094bc58a738Sreyk 	return (0);
10959083f23cSjsing 
1096bc58a738Sreyk  fail:
1097bc58a738Sreyk 	httpsdate_free(httpsdate);
1098bc58a738Sreyk 	return (-1);
1099bc58a738Sreyk }
1100bc58a738Sreyk 
1101bc58a738Sreyk void *
110251d66267Sjsing httpsdate_query(const char *addr, const char *port, const char *hostname,
1103bc58a738Sreyk     const char *path, const u_int8_t *ca, size_t ca_len,
110405b37b28Sotto     struct timeval *rectv, struct timeval *xmttv, int synced)
1105bc58a738Sreyk {
1106bc58a738Sreyk 	struct httpsdate	*httpsdate;
1107bc58a738Sreyk 	struct timeval		 when;
1108bc58a738Sreyk 	time_t			 t;
1109bc58a738Sreyk 
111051d66267Sjsing 	if ((httpsdate = httpsdate_init(addr, port, hostname, path,
111105b37b28Sotto 	    ca, ca_len, synced)) == NULL)
1112bc58a738Sreyk 		return (NULL);
1113bc58a738Sreyk 
111405b37b28Sotto 	if (httpsdate_request(httpsdate, &when, synced) == -1)
1115bc58a738Sreyk 		return (NULL);
1116bc58a738Sreyk 
1117bc58a738Sreyk 	/* Return parsed date as local time */
1118bc58a738Sreyk 	t = timegm(&httpsdate->tls_tm);
1119bc58a738Sreyk 
1120bc58a738Sreyk 	/* Report parsed Date: as "received time" */
1121bc58a738Sreyk 	rectv->tv_sec = t;
1122bc58a738Sreyk 	rectv->tv_usec = 0;
1123bc58a738Sreyk 
1124bc58a738Sreyk 	/* And add delay as "transmit time" */
1125bc58a738Sreyk 	xmttv->tv_sec = when.tv_sec;
1126bc58a738Sreyk 	xmttv->tv_usec = when.tv_usec;
1127bc58a738Sreyk 
1128bc58a738Sreyk 	return (httpsdate);
1129bc58a738Sreyk }
1130bc58a738Sreyk 
1131bc58a738Sreyk /* Based on SSL_readline in ftp/fetch.c */
1132bc58a738Sreyk char *
1133bc58a738Sreyk tls_readline(struct tls *tls, size_t *lenp, size_t *maxlength,
1134bc58a738Sreyk     struct timeval *when)
1135bc58a738Sreyk {
1136caf85d1dSbeck 	size_t i, len;
1137bc58a738Sreyk 	char *buf, *q, c;
1138cd548fa4Sbeck 	ssize_t ret;
1139bc58a738Sreyk 
1140bc58a738Sreyk 	len = 128;
1141bc58a738Sreyk 	if ((buf = malloc(len)) == NULL)
1142bc58a738Sreyk 		fatal("Can't allocate memory for transfer buffer");
1143bc58a738Sreyk 	for (i = 0; ; i++) {
1144bc58a738Sreyk 		if (i >= len - 1) {
1145bc58a738Sreyk 			if ((q = reallocarray(buf, len, 2)) == NULL)
1146bc58a738Sreyk 				fatal("Can't expand transfer buffer");
1147bc58a738Sreyk 			buf = q;
1148bc58a738Sreyk 			len *= 2;
1149bc58a738Sreyk 		}
1150bc58a738Sreyk  again:
1151caf85d1dSbeck 		ret = tls_read(tls, &c, 1);
1152caf85d1dSbeck 		if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT)
1153bc58a738Sreyk 			goto again;
1154df69c215Sderaadt 		if (ret == -1) {
1155bc58a738Sreyk 			/* SSL read error, ignore */
115687e97f23Sjsg 			free(buf);
1157bc58a738Sreyk 			return (NULL);
1158bc58a738Sreyk 		}
1159bc58a738Sreyk 
1160bc58a738Sreyk 		if (maxlength != NULL && (*maxlength)-- == 0) {
1161bc58a738Sreyk 			log_warnx("maximum length exceeded");
11629d863a9fSmillert 			free(buf);
1163bc58a738Sreyk 			return (NULL);
1164bc58a738Sreyk 		}
1165bc58a738Sreyk 
1166bc58a738Sreyk 		buf[i] = c;
1167bc58a738Sreyk 		if (c == '\n')
1168bc58a738Sreyk 			break;
1169bc58a738Sreyk 	}
1170bc58a738Sreyk 	*lenp = i;
1171bc58a738Sreyk 	if (gettimeofday(when, NULL) == -1)
1172bc58a738Sreyk 		fatal("gettimeofday");
1173bc58a738Sreyk 	return (buf);
1174bc58a738Sreyk }
1175a257dd04Sreyk 
1176a257dd04Sreyk char *
1177a257dd04Sreyk get_string(u_int8_t *ptr, size_t len)
1178a257dd04Sreyk {
1179a257dd04Sreyk 	size_t	 i;
1180a257dd04Sreyk 
1181a257dd04Sreyk 	for (i = 0; i < len; i++)
1182a257dd04Sreyk 		if (!(isprint(ptr[i]) || isspace(ptr[i])))
1183a257dd04Sreyk 			break;
1184a257dd04Sreyk 
1185b52b3078Smmcc 	return strndup(ptr, i);
1186a257dd04Sreyk }
1187