xref: /openbsd/usr.sbin/ntpd/constraint.c (revision 8745f5cf)
1*8745f5cfSotto /*	$OpenBSD: constraint.c,v 1.56 2023/12/20 15:36:36 otto 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
constraint_init(struct constraint * cstr)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
constraint_addr_init(struct constraint * cstr)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
constraint_addr_head_clear(struct constraint * cstr)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
constraint_query(struct constraint * cstr,int synced)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
priv_constraint_msg(u_int32_t id,u_int8_t * data,size_t len,int argc,char ** argv)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];
2383b54e53fSrzalamena 	int			 rv;
239a257dd04Sreyk 
240a257dd04Sreyk 	if ((cstr = constraint_byid(id)) != NULL) {
241a257dd04Sreyk 		log_warnx("IMSG_CONSTRAINT_QUERY repeated for id %d", id);
242a257dd04Sreyk 		return;
243a257dd04Sreyk 	}
244a257dd04Sreyk 
245a257dd04Sreyk 	if (len < sizeof(am)) {
246a257dd04Sreyk 		log_warnx("invalid IMSG_CONSTRAINT_QUERY received");
247a257dd04Sreyk 		return;
248a257dd04Sreyk 	}
249a257dd04Sreyk 	memcpy(&am, data, sizeof(am));
250a257dd04Sreyk 	if (len != (sizeof(am) + am.namelen + am.pathlen)) {
251a257dd04Sreyk 		log_warnx("invalid IMSG_CONSTRAINT_QUERY received");
252a257dd04Sreyk 		return;
253a257dd04Sreyk 	}
254a257dd04Sreyk 	/* Additional imsg data is obtained in the unpriv child */
255a257dd04Sreyk 
256a257dd04Sreyk 	if ((h = calloc(1, sizeof(*h))) == NULL)
257a257dd04Sreyk 		fatal("calloc ntp_addr");
258a257dd04Sreyk 	memcpy(h, &am.a, sizeof(*h));
259a257dd04Sreyk 	h->next = NULL;
260a257dd04Sreyk 
261a257dd04Sreyk 	cstr = new_constraint();
262a257dd04Sreyk 	cstr->id = id;
263a257dd04Sreyk 	cstr->addr = h;
264a257dd04Sreyk 	cstr->addr_head.a = h;
265a257dd04Sreyk 	constraint_add(cstr);
266a257dd04Sreyk 	constraint_cnt++;
267bc58a738Sreyk 
2685f14684eSrzalamena 	if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, AF_UNSPEC,
2695f14684eSrzalamena 	    pipes) == -1)
270bc58a738Sreyk 		fatal("%s pipes", __func__);
271bc58a738Sreyk 
2725f14684eSrzalamena 	/* Prepare and send constraint data to child. */
2735f14684eSrzalamena 	cstr->fd = pipes[0];
2745f14684eSrzalamena 	imsg_init(&cstr->ibuf, cstr->fd);
2755f14684eSrzalamena 	if (imsg_compose(&cstr->ibuf, IMSG_CONSTRAINT_QUERY, id, 0, -1,
2765f14684eSrzalamena 	    data, len) == -1)
2775f14684eSrzalamena 		fatal("%s: imsg_compose", __func__);
2783b54e53fSrzalamena 	do {
2793b54e53fSrzalamena 		rv = imsg_flush(&cstr->ibuf);
2803b54e53fSrzalamena 	} while (rv == -1 && errno == EAGAIN);
2813b54e53fSrzalamena 	if (rv == -1)
2823b54e53fSrzalamena 		fatal("imsg_flush");
2835f14684eSrzalamena 
284a257dd04Sreyk 	/*
285a257dd04Sreyk 	 * Fork child handlers and make sure to do any sensitive work in the
286a257dd04Sreyk 	 * the (unprivileged) child.  The parent should not do any parsing,
287a257dd04Sreyk 	 * certificate loading etc.
288a257dd04Sreyk 	 */
2898c5c2965Srzalamena 	cstr->pid = start_child(CONSTRAINT_PROC_NAME, pipes[1], argc, argv);
290a257dd04Sreyk }
291a257dd04Sreyk 
292a257dd04Sreyk void
priv_constraint_readquery(struct constraint * cstr,struct ntp_addr_msg * am,uint8_t ** data)2935f14684eSrzalamena priv_constraint_readquery(struct constraint *cstr, struct ntp_addr_msg *am,
2945f14684eSrzalamena     uint8_t **data)
295a257dd04Sreyk {
2965f14684eSrzalamena 	struct ntp_addr		*h;
2975f14684eSrzalamena 	uint8_t			*dptr;
2985f14684eSrzalamena 	int			 n;
2995f14684eSrzalamena 	struct imsg		 imsg;
3005f14684eSrzalamena 	size_t			 mlen;
3015f14684eSrzalamena 
3025f14684eSrzalamena 	/* Read the message our parent left us. */
3035f14684eSrzalamena 	if (((n = imsg_read(&cstr->ibuf)) == -1 && errno != EAGAIN) || n == 0)
3045f14684eSrzalamena 		fatal("%s: imsg_read", __func__);
3055f14684eSrzalamena 	if (((n = imsg_get(&cstr->ibuf, &imsg)) == -1) || n == 0)
3065f14684eSrzalamena 		fatal("%s: imsg_get", __func__);
3075f14684eSrzalamena 	if (imsg.hdr.type != IMSG_CONSTRAINT_QUERY)
3085f14684eSrzalamena 		fatalx("%s: invalid message type", __func__);
3095f14684eSrzalamena 
3105f14684eSrzalamena 	/*
3115f14684eSrzalamena 	 * Copy the message contents just like our father:
3125f14684eSrzalamena 	 * priv_constraint_msg().
3135f14684eSrzalamena 	 */
3145f14684eSrzalamena 	mlen = imsg.hdr.len - IMSG_HEADER_SIZE;
3155f14684eSrzalamena 	if (mlen < sizeof(*am))
3165f14684eSrzalamena 		fatalx("%s: mlen < sizeof(*am)", __func__);
3175f14684eSrzalamena 
3185f14684eSrzalamena 	memcpy(am, imsg.data, sizeof(*am));
3195f14684eSrzalamena 	if (mlen != (sizeof(*am) + am->namelen + am->pathlen))
3205f14684eSrzalamena 		fatalx("%s: mlen < sizeof(*am) + am->namelen + am->pathlen",
3215f14684eSrzalamena 		    __func__);
3225f14684eSrzalamena 
3235f14684eSrzalamena 	if ((h = calloc(1, sizeof(*h))) == NULL ||
3245f14684eSrzalamena 	    (*data = calloc(1, mlen)) == NULL)
3255f14684eSrzalamena 		fatal("%s: calloc", __func__);
3265f14684eSrzalamena 
3275f14684eSrzalamena 	memcpy(h, &am->a, sizeof(*h));
3285f14684eSrzalamena 	h->next = NULL;
3295f14684eSrzalamena 
3305f14684eSrzalamena 	cstr->id = imsg.hdr.peerid;
3315f14684eSrzalamena 	cstr->addr = h;
3325f14684eSrzalamena 	cstr->addr_head.a = h;
3335f14684eSrzalamena 
3345f14684eSrzalamena 	dptr = imsg.data;
3355f14684eSrzalamena 	memcpy(*data, dptr + sizeof(*am), mlen - sizeof(*am));
3365f14684eSrzalamena 	imsg_free(&imsg);
3375f14684eSrzalamena }
3385f14684eSrzalamena 
3395f14684eSrzalamena void
priv_constraint_child(const char * pw_dir,uid_t pw_uid,gid_t pw_gid)3405f14684eSrzalamena priv_constraint_child(const char *pw_dir, uid_t pw_uid, gid_t pw_gid)
3415f14684eSrzalamena {
3429efcf0f3Srzalamena 	struct constraint	 cstr;
3439efcf0f3Srzalamena 	struct ntp_addr_msg	 am;
3445f14684eSrzalamena 	uint8_t			*data;
34551d66267Sjsing 	static char		 addr[NI_MAXHOST];
346a257dd04Sreyk 	struct timeval		 rectv, xmttv;
347a257dd04Sreyk 	struct sigaction	 sa;
348a257dd04Sreyk 	void			*ctx;
349a257dd04Sreyk 	struct iovec		 iov[2];
3503b54e53fSrzalamena 	int			 i, rv;
351a257dd04Sreyk 
352579813e4Sreyk 	log_procinit("constraint");
353579813e4Sreyk 
354a257dd04Sreyk 	if (setpriority(PRIO_PROCESS, 0, 0) == -1)
355a257dd04Sreyk 		log_warn("could not set priority");
356a257dd04Sreyk 
3575df30466Skn 	/* load CA certs before chroot() */
358fb0a89eeStedu 	if ((conf->ca = tls_load_file(tls_default_ca_cert_file(),
359a257dd04Sreyk 	    &conf->ca_len, NULL)) == NULL)
3609083f23cSjsing 		fatalx("failed to load constraint ca");
361a257dd04Sreyk 
362b1da763cSderaadt 	if (chroot(pw_dir) == -1)
363a257dd04Sreyk 		fatal("chroot");
364a257dd04Sreyk 	if (chdir("/") == -1)
365a257dd04Sreyk 		fatal("chdir(\"/\")");
366a257dd04Sreyk 
367b1da763cSderaadt 	if (setgroups(1, &pw_gid) ||
368b1da763cSderaadt 	    setresgid(pw_gid, pw_gid, pw_gid) ||
369b1da763cSderaadt 	    setresuid(pw_uid, pw_uid, pw_uid))
370a257dd04Sreyk 		fatal("can't drop privileges");
371a257dd04Sreyk 
372a257dd04Sreyk 	/* Reset all signal handlers */
373a257dd04Sreyk 	memset(&sa, 0, sizeof(sa));
374a257dd04Sreyk 	sigemptyset(&sa.sa_mask);
375a257dd04Sreyk 	sa.sa_flags = SA_RESTART;
376a257dd04Sreyk 	sa.sa_handler = SIG_DFL;
377a257dd04Sreyk 	for (i = 1; i < _NSIG; i++)
378a257dd04Sreyk 		sigaction(i, &sa, NULL);
379bc58a738Sreyk 
380d2f9ff44Sderaadt 	if (pledge("stdio inet", NULL) == -1)
381a257dd04Sreyk 		fatal("pledge");
382d2f9ff44Sderaadt 
3839efcf0f3Srzalamena 	cstr.fd = CONSTRAINT_PASSFD;
3849efcf0f3Srzalamena 	imsg_init(&cstr.ibuf, cstr.fd);
3859efcf0f3Srzalamena 	priv_constraint_readquery(&cstr, &am, &data);
3865f14684eSrzalamena 
387b7f2e836Sreyk 	/*
388b7f2e836Sreyk 	 * Get the IP address as name and set the process title accordingly.
389b7f2e836Sreyk 	 * This only converts an address into a string and does not trigger
390b7f2e836Sreyk 	 * any DNS operation, so it is safe to be called without the dns
391b7f2e836Sreyk 	 * pledge.
392b7f2e836Sreyk 	 */
3939efcf0f3Srzalamena 	if (getnameinfo((struct sockaddr *)&cstr.addr->ss,
3949efcf0f3Srzalamena 	    SA_LEN((struct sockaddr *)&cstr.addr->ss),
39551d66267Sjsing 	    addr, sizeof(addr), NULL, 0,
396a257dd04Sreyk 	    NI_NUMERICHOST) != 0)
397a257dd04Sreyk 		fatalx("%s getnameinfo", __func__);
398a257dd04Sreyk 
39951d66267Sjsing 	log_debug("constraint request to %s", addr);
40051d66267Sjsing 	setproctitle("constraint from %s", addr);
401bc58a738Sreyk 	(void)closefrom(CONSTRAINT_PASSFD + 1);
402bc58a738Sreyk 
403b7f2e836Sreyk 	/*
404b7f2e836Sreyk 	 * Set the close-on-exec flag to prevent leaking the communication
405b7f2e836Sreyk 	 * channel to any exec'ed child.  In theory this could never happen,
406b7f2e836Sreyk 	 * constraints don't exec children and pledge() prevents it,
407b7f2e836Sreyk 	 * but we keep it as a safety belt; especially for portability.
408b7f2e836Sreyk 	 */
409bc58a738Sreyk 	if (fcntl(CONSTRAINT_PASSFD, F_SETFD, FD_CLOEXEC) == -1)
410bc58a738Sreyk 		fatal("%s fcntl F_SETFD", __func__);
411bc58a738Sreyk 
412a257dd04Sreyk 	/* Get remaining data from imsg in the unpriv child */
4139efcf0f3Srzalamena 	if (am.namelen) {
4149efcf0f3Srzalamena 		if ((cstr.addr_head.name =
4159efcf0f3Srzalamena 		    get_string(data, am.namelen)) == NULL)
416a257dd04Sreyk 			fatalx("invalid IMSG_CONSTRAINT_QUERY name");
4179efcf0f3Srzalamena 		data += am.namelen;
418a257dd04Sreyk 	}
4199efcf0f3Srzalamena 	if (am.pathlen) {
4209efcf0f3Srzalamena 		if ((cstr.addr_head.path =
4219efcf0f3Srzalamena 		    get_string(data, am.pathlen)) == NULL)
422a257dd04Sreyk 			fatalx("invalid IMSG_CONSTRAINT_QUERY path");
423a257dd04Sreyk 	}
424a257dd04Sreyk 
425a257dd04Sreyk 	/* Run! */
42651d66267Sjsing 	if ((ctx = httpsdate_query(addr,
4279efcf0f3Srzalamena 	    CONSTRAINT_PORT, cstr.addr_head.name, cstr.addr_head.path,
42805b37b28Sotto 	    conf->ca, conf->ca_len, &rectv, &xmttv, am.synced)) == NULL) {
429bc58a738Sreyk 		/* Abort with failure but without warning */
430bc58a738Sreyk 		exit(1);
431bc58a738Sreyk 	}
432bc58a738Sreyk 
433bc58a738Sreyk 	iov[0].iov_base = &rectv;
434bc58a738Sreyk 	iov[0].iov_len = sizeof(rectv);
435bc58a738Sreyk 	iov[1].iov_base = &xmttv;
436bc58a738Sreyk 	iov[1].iov_len = sizeof(xmttv);
4379efcf0f3Srzalamena 	imsg_composev(&cstr.ibuf,
438a257dd04Sreyk 	    IMSG_CONSTRAINT_RESULT, 0, 0, -1, iov, 2);
4393b54e53fSrzalamena 	do {
4409efcf0f3Srzalamena 		rv = imsg_flush(&cstr.ibuf);
4413b54e53fSrzalamena 	} while (rv == -1 && errno == EAGAIN);
442bc58a738Sreyk 
443bc58a738Sreyk 	/* Tear down the TLS connection after sending the result */
444bc58a738Sreyk 	httpsdate_free(ctx);
4455f14684eSrzalamena 
4465f14684eSrzalamena 	exit(0);
447bc58a738Sreyk }
448bc58a738Sreyk 
449bc58a738Sreyk void
priv_constraint_check_child(pid_t pid,int status)450a257dd04Sreyk priv_constraint_check_child(pid_t pid, int status)
451bc58a738Sreyk {
452bc58a738Sreyk 	struct constraint	*cstr;
45331f765bfSderaadt 	int			 fail, sig;
4540a1ac5ecSreyk 	char			*signame;
455bc58a738Sreyk 
45631f765bfSderaadt 	fail = sig = 0;
457bc58a738Sreyk 	if (WIFSIGNALED(status)) {
45831f765bfSderaadt 		sig = WTERMSIG(status);
459bc58a738Sreyk 	} else if (WIFEXITED(status)) {
46091e60ad5Sbcook 		if (WEXITSTATUS(status) != 0)
461bc58a738Sreyk 			fail = 1;
462bc58a738Sreyk 	} else
463bc58a738Sreyk 		fatalx("unexpected cause of SIGCHLD");
464bc58a738Sreyk 
465bc58a738Sreyk 	if ((cstr = constraint_bypid(pid)) != NULL) {
4660a1ac5ecSreyk 		if (sig) {
4670a1ac5ecSreyk 			if (sig != SIGTERM) {
4680a1ac5ecSreyk 				signame = strsignal(sig) ?
4690a1ac5ecSreyk 				    strsignal(sig) : "unknown";
4700a1ac5ecSreyk 				log_warnx("constraint %s; "
4710a1ac5ecSreyk 				    "terminated with signal %d (%s)",
472*8745f5cfSotto 				    log_ntp_addr(cstr->addr), sig, signame);
4730a1ac5ecSreyk 			}
4740a1ac5ecSreyk 			fail = 1;
4750a1ac5ecSreyk 		}
476bc58a738Sreyk 
477a257dd04Sreyk 		priv_constraint_close(cstr->fd, fail);
478bc58a738Sreyk 	}
479bc58a738Sreyk }
480bc58a738Sreyk 
4810a1ac5ecSreyk void
priv_constraint_kill(u_int32_t id)4820a1ac5ecSreyk priv_constraint_kill(u_int32_t id)
4830a1ac5ecSreyk {
4840a1ac5ecSreyk 	struct constraint	*cstr;
4850a1ac5ecSreyk 
4860a1ac5ecSreyk 	if ((cstr = constraint_byid(id)) == NULL) {
4870a1ac5ecSreyk 		log_warnx("IMSG_CONSTRAINT_KILL for invalid id %d", id);
4880a1ac5ecSreyk 		return;
4890a1ac5ecSreyk 	}
4900a1ac5ecSreyk 
4910a1ac5ecSreyk 	kill(cstr->pid, SIGTERM);
4920a1ac5ecSreyk }
4930a1ac5ecSreyk 
494bc58a738Sreyk struct constraint *
constraint_byid(u_int32_t id)495b775b3eeSreyk constraint_byid(u_int32_t id)
496b775b3eeSreyk {
497b775b3eeSreyk 	struct constraint	*cstr;
498b775b3eeSreyk 
499b775b3eeSreyk 	TAILQ_FOREACH(cstr, &conf->constraints, entry) {
500b775b3eeSreyk 		if (cstr->id == id)
501b775b3eeSreyk 			return (cstr);
502b775b3eeSreyk 	}
503b775b3eeSreyk 
504b775b3eeSreyk 	return (NULL);
505b775b3eeSreyk }
506b775b3eeSreyk 
507b775b3eeSreyk struct constraint *
constraint_byfd(int fd)508bc58a738Sreyk constraint_byfd(int fd)
509bc58a738Sreyk {
510bc58a738Sreyk 	struct constraint	*cstr;
511bc58a738Sreyk 
512bc58a738Sreyk 	TAILQ_FOREACH(cstr, &conf->constraints, entry) {
513bc58a738Sreyk 		if (cstr->fd == fd)
514bc58a738Sreyk 			return (cstr);
515bc58a738Sreyk 	}
516bc58a738Sreyk 
517bc58a738Sreyk 	return (NULL);
518bc58a738Sreyk }
519bc58a738Sreyk 
520bc58a738Sreyk struct constraint *
constraint_bypid(pid_t pid)521bc58a738Sreyk constraint_bypid(pid_t pid)
522bc58a738Sreyk {
523bc58a738Sreyk 	struct constraint	*cstr;
524bc58a738Sreyk 
525bc58a738Sreyk 	TAILQ_FOREACH(cstr, &conf->constraints, entry) {
526bc58a738Sreyk 		if (cstr->pid == pid)
527bc58a738Sreyk 			return (cstr);
528bc58a738Sreyk 	}
529bc58a738Sreyk 
530bc58a738Sreyk 	return (NULL);
531bc58a738Sreyk }
532bc58a738Sreyk 
533bc58a738Sreyk int
constraint_close(u_int32_t id)534a257dd04Sreyk constraint_close(u_int32_t id)
535bc58a738Sreyk {
536bc58a738Sreyk 	struct constraint	*cstr;
537bc58a738Sreyk 
538a257dd04Sreyk 	if ((cstr = constraint_byid(id)) == NULL) {
539a257dd04Sreyk 		log_warn("%s: id %d: not found", __func__, id);
540bc58a738Sreyk 		return (0);
541bc58a738Sreyk 	}
542bc58a738Sreyk 
5433303745eSreyk 	cstr->last = getmonotime();
5443303745eSreyk 
5453303745eSreyk 	if (cstr->addr == NULL || (cstr->addr = cstr->addr->next) == NULL) {
5463303745eSreyk 		/* Either a pool or all addresses have been tried */
5473303745eSreyk 		cstr->addr = cstr->addr_head.a;
548bc58a738Sreyk 		if (cstr->senderrors)
549bc58a738Sreyk 			cstr->state = STATE_INVALID;
550bc58a738Sreyk 		else if (cstr->state >= STATE_QUERY_SENT)
551bc58a738Sreyk 			cstr->state = STATE_DNS_DONE;
552bc58a738Sreyk 
553bc58a738Sreyk 		return (1);
554bc58a738Sreyk 	}
555bc58a738Sreyk 
5563303745eSreyk 	return (constraint_init(cstr));
5573303745eSreyk }
5583303745eSreyk 
559b775b3eeSreyk void
priv_constraint_close(int fd,int fail)560a257dd04Sreyk priv_constraint_close(int fd, int fail)
561a257dd04Sreyk {
562a257dd04Sreyk 	struct constraint	*cstr;
563a257dd04Sreyk 	u_int32_t		 id;
564a257dd04Sreyk 
565a257dd04Sreyk 	if ((cstr = constraint_byfd(fd)) == NULL) {
566a257dd04Sreyk 		log_warn("%s: fd %d: not found", __func__, fd);
567a257dd04Sreyk 		return;
568a257dd04Sreyk 	}
569a257dd04Sreyk 
570a257dd04Sreyk 	id = cstr->id;
571a257dd04Sreyk 	constraint_remove(cstr);
572a257dd04Sreyk 	constraint_cnt--;
573a257dd04Sreyk 
574a257dd04Sreyk 	imsg_compose(ibuf, IMSG_CONSTRAINT_CLOSE, id, 0, -1,
575a257dd04Sreyk 	    &fail, sizeof(fail));
576a257dd04Sreyk }
577a257dd04Sreyk 
578a257dd04Sreyk void
constraint_add(struct constraint * cstr)579b775b3eeSreyk constraint_add(struct constraint *cstr)
580b775b3eeSreyk {
581b775b3eeSreyk 	TAILQ_INSERT_TAIL(&conf->constraints, cstr, entry);
582b775b3eeSreyk }
583b775b3eeSreyk 
584b775b3eeSreyk void
constraint_remove(struct constraint * cstr)585b775b3eeSreyk constraint_remove(struct constraint *cstr)
586b775b3eeSreyk {
587b775b3eeSreyk 	TAILQ_REMOVE(&conf->constraints, cstr, entry);
588a257dd04Sreyk 
589a257dd04Sreyk 	msgbuf_clear(&cstr->ibuf.w);
590a257dd04Sreyk 	if (cstr->fd != -1)
591a257dd04Sreyk 		close(cstr->fd);
592b775b3eeSreyk 	free(cstr->addr_head.name);
593b775b3eeSreyk 	free(cstr->addr_head.path);
594d746074eSderaadt 	free(cstr->addr);
595b775b3eeSreyk 	free(cstr);
596b775b3eeSreyk }
597b775b3eeSreyk 
598a257dd04Sreyk void
constraint_purge(void)599a257dd04Sreyk constraint_purge(void)
600a257dd04Sreyk {
601a257dd04Sreyk 	struct constraint	*cstr, *ncstr;
602a257dd04Sreyk 
603a257dd04Sreyk 	TAILQ_FOREACH_SAFE(cstr, &conf->constraints, entry, ncstr)
604a257dd04Sreyk 		constraint_remove(cstr);
605a257dd04Sreyk }
606a257dd04Sreyk 
607bc58a738Sreyk int
priv_constraint_dispatch(struct pollfd * pfd)608a257dd04Sreyk priv_constraint_dispatch(struct pollfd *pfd)
609bc58a738Sreyk {
610bc58a738Sreyk 	struct imsg		 imsg;
611bc58a738Sreyk 	struct constraint	*cstr;
612bc58a738Sreyk 	ssize_t			 n;
613bc58a738Sreyk 	struct timeval		 tv[2];
614bc58a738Sreyk 
615bc58a738Sreyk 	if ((cstr = constraint_byfd(pfd->fd)) == NULL)
616bc58a738Sreyk 		return (0);
617bc58a738Sreyk 
618bc58a738Sreyk 	if (!(pfd->revents & POLLIN))
619bc58a738Sreyk 		return (0);
620bc58a738Sreyk 
6211a2f0668Sclaudio 	if (((n = imsg_read(&cstr->ibuf)) == -1 && errno != EAGAIN) || n == 0) {
622c7e8e3a2Sotto 		/* there's a race between SIGCHLD delivery and reading imsg
623c7e8e3a2Sotto 		   but if we've seen the reply, we're good */
624c7e8e3a2Sotto 		priv_constraint_close(pfd->fd, cstr->state !=
625c7e8e3a2Sotto 		    STATE_REPLY_RECEIVED);
626bc58a738Sreyk 		return (1);
627bc58a738Sreyk 	}
628bc58a738Sreyk 
629bc58a738Sreyk 	for (;;) {
630bc58a738Sreyk 		if ((n = imsg_get(&cstr->ibuf, &imsg)) == -1) {
631a257dd04Sreyk 			priv_constraint_close(pfd->fd, 1);
632bc58a738Sreyk 			return (1);
633bc58a738Sreyk 		}
634bc58a738Sreyk 		if (n == 0)
635bc58a738Sreyk 			break;
636bc58a738Sreyk 
637bc58a738Sreyk 		switch (imsg.hdr.type) {
638a257dd04Sreyk 		case IMSG_CONSTRAINT_RESULT:
639bc58a738Sreyk 			 if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(tv))
640bc58a738Sreyk 				fatalx("invalid IMSG_CONSTRAINT received");
641bc58a738Sreyk 
642c7e8e3a2Sotto 			/* state is maintained by child, but we want to
643c7e8e3a2Sotto 			   remember we've seen the result */
644c7e8e3a2Sotto 			cstr->state = STATE_REPLY_RECEIVED;
645a257dd04Sreyk 			/* forward imsg to ntp child, don't parse it here */
646a257dd04Sreyk 			imsg_compose(ibuf, imsg.hdr.type,
647a257dd04Sreyk 			    cstr->id, 0, -1, imsg.data, sizeof(tv));
648a257dd04Sreyk 			break;
649a257dd04Sreyk 		default:
650a257dd04Sreyk 			break;
651a257dd04Sreyk 		}
652a257dd04Sreyk 		imsg_free(&imsg);
653a257dd04Sreyk 	}
654a257dd04Sreyk 
655a257dd04Sreyk 	return (0);
656a257dd04Sreyk }
657a257dd04Sreyk 
658a257dd04Sreyk void
constraint_msg_result(u_int32_t id,u_int8_t * data,size_t len)659a257dd04Sreyk constraint_msg_result(u_int32_t id, u_int8_t *data, size_t len)
660a257dd04Sreyk {
661a257dd04Sreyk 	struct constraint	*cstr;
662a257dd04Sreyk 	struct timeval		 tv[2];
663a257dd04Sreyk 	double			 offset;
664a257dd04Sreyk 
665a257dd04Sreyk 	if ((cstr = constraint_byid(id)) == NULL) {
666a257dd04Sreyk 		log_warnx("IMSG_CONSTRAINT_CLOSE with invalid constraint id");
667a257dd04Sreyk 		return;
668a257dd04Sreyk 	}
669a257dd04Sreyk 
670a257dd04Sreyk 	if (len != sizeof(tv)) {
671a257dd04Sreyk 		log_warnx("invalid IMSG_CONSTRAINT received");
672a257dd04Sreyk 		return;
673a257dd04Sreyk 	}
674a257dd04Sreyk 
675a257dd04Sreyk 	memcpy(tv, data, len);
676bc58a738Sreyk 
677bc58a738Sreyk 	offset = gettime_from_timeval(&tv[0]) -
678bc58a738Sreyk 	    gettime_from_timeval(&tv[1]);
679bc58a738Sreyk 
680bc58a738Sreyk 	log_info("constraint reply from %s: offset %f",
681*8745f5cfSotto 	    log_ntp_addr(cstr->addr),
682bc58a738Sreyk 	    offset);
683bc58a738Sreyk 
684bc58a738Sreyk 	cstr->state = STATE_REPLY_RECEIVED;
685bc58a738Sreyk 	cstr->last = getmonotime();
686bc58a738Sreyk 	cstr->constraint = tv[0].tv_sec;
687bc58a738Sreyk 
688bc58a738Sreyk 	constraint_update();
689bc58a738Sreyk }
690bc58a738Sreyk 
691b775b3eeSreyk void
constraint_msg_close(u_int32_t id,u_int8_t * data,size_t len)692a257dd04Sreyk constraint_msg_close(u_int32_t id, u_int8_t *data, size_t len)
693a257dd04Sreyk {
6941908d877Sotto 	struct constraint	*cstr, *tmp;
6951908d877Sotto 	int			 fail, cnt;
6961908d877Sotto 	static int		 total_fails;
697a257dd04Sreyk 
698a257dd04Sreyk 	if ((cstr = constraint_byid(id)) == NULL) {
699a257dd04Sreyk 		log_warnx("IMSG_CONSTRAINT_CLOSE with invalid constraint id");
700a257dd04Sreyk 		return;
701a257dd04Sreyk 	}
702a257dd04Sreyk 
703a257dd04Sreyk 	if (len != sizeof(int)) {
704a257dd04Sreyk 		log_warnx("invalid IMSG_CONSTRAINT_CLOSE received");
705a257dd04Sreyk 		return;
706a257dd04Sreyk 	}
707a257dd04Sreyk 
708a257dd04Sreyk 	memcpy(&fail, data, len);
709a257dd04Sreyk 
710a257dd04Sreyk 	if (fail) {
711a257dd04Sreyk 		log_debug("no constraint reply from %s"
712a257dd04Sreyk 		    " received in time, next query %ds",
713*8745f5cfSotto 		    log_ntp_addr(cstr->addr),
714*8745f5cfSotto 		    CONSTRAINT_SCAN_INTERVAL);
7151908d877Sotto 
7161908d877Sotto 		cnt = 0;
7171908d877Sotto 		TAILQ_FOREACH(tmp, &conf->constraints, entry)
7181908d877Sotto 			cnt++;
7191908d877Sotto 		if (cnt > 0 && ++total_fails >= cnt &&
7201908d877Sotto 		    conf->constraint_median == 0) {
721d2460e3aSotto 			log_warnx("constraints configured but none available");
7221908d877Sotto 			total_fails = 0;
7231908d877Sotto 		}
724a257dd04Sreyk 	}
725a257dd04Sreyk 
726a257dd04Sreyk 	if (fail || cstr->state < STATE_QUERY_SENT) {
727a257dd04Sreyk 		cstr->senderrors++;
728a257dd04Sreyk 		constraint_close(cstr->id);
729a257dd04Sreyk 	}
730a257dd04Sreyk }
731a257dd04Sreyk 
732a257dd04Sreyk void
constraint_msg_dns(u_int32_t id,u_int8_t * data,size_t len)733a257dd04Sreyk constraint_msg_dns(u_int32_t id, u_int8_t *data, size_t len)
734b775b3eeSreyk {
7353303745eSreyk 	struct constraint	*cstr, *ncstr = NULL;
736b775b3eeSreyk 	u_int8_t		*p;
737b775b3eeSreyk 	struct ntp_addr		*h;
738b775b3eeSreyk 
739b775b3eeSreyk 	if ((cstr = constraint_byid(id)) == NULL) {
740c7e8e3a2Sotto 		log_debug("IMSG_CONSTRAINT_DNS with invalid constraint id");
741b775b3eeSreyk 		return;
742b775b3eeSreyk 	}
743b775b3eeSreyk 	if (cstr->addr != NULL) {
744b775b3eeSreyk 		log_warnx("IMSG_CONSTRAINT_DNS but addr != NULL!");
745b775b3eeSreyk 		return;
746b775b3eeSreyk 	}
747b775b3eeSreyk 	if (len == 0) {
748b775b3eeSreyk 		log_debug("%s FAILED", __func__);
749b775b3eeSreyk 		cstr->state = STATE_DNS_TEMPFAIL;
750b775b3eeSreyk 		return;
751b775b3eeSreyk 	}
752b775b3eeSreyk 
753b98b0a5cSotto 	if (len % (sizeof(struct sockaddr_storage) + sizeof(int)) != 0)
754b775b3eeSreyk 		fatalx("IMSG_CONSTRAINT_DNS len");
755b775b3eeSreyk 
756c7e8e3a2Sotto 	if (cstr->addr_head.pool) {
757c7e8e3a2Sotto 		struct constraint *n, *tmp;
758c7e8e3a2Sotto 		TAILQ_FOREACH_SAFE(n, &conf->constraints, entry, tmp) {
759c7e8e3a2Sotto 			if (cstr->id == n->id)
760c7e8e3a2Sotto 				continue;
761c7e8e3a2Sotto 			if (cstr->addr_head.pool == n->addr_head.pool)
762c7e8e3a2Sotto 				constraint_remove(n);
763c7e8e3a2Sotto 		}
764c7e8e3a2Sotto 	}
765c7e8e3a2Sotto 
766b775b3eeSreyk 	p = data;
767b775b3eeSreyk 	do {
768b775b3eeSreyk 		if ((h = calloc(1, sizeof(*h))) == NULL)
769b775b3eeSreyk 			fatal("calloc ntp_addr");
770b775b3eeSreyk 		memcpy(&h->ss, p, sizeof(h->ss));
771b775b3eeSreyk 		p += sizeof(h->ss);
772b775b3eeSreyk 		len -= sizeof(h->ss);
773b98b0a5cSotto 		memcpy(&h->notauth, p, sizeof(int));
774b98b0a5cSotto 		p += sizeof(int);
775b98b0a5cSotto 		len -= sizeof(int);
776b775b3eeSreyk 
7773303745eSreyk 		if (ncstr == NULL || cstr->addr_head.pool) {
778b775b3eeSreyk 			ncstr = new_constraint();
779b775b3eeSreyk 			ncstr->addr = h;
780b775b3eeSreyk 			ncstr->addr_head.a = h;
781b775b3eeSreyk 			ncstr->addr_head.name = strdup(cstr->addr_head.name);
782b775b3eeSreyk 			ncstr->addr_head.path = strdup(cstr->addr_head.path);
783b775b3eeSreyk 			if (ncstr->addr_head.name == NULL ||
784b775b3eeSreyk 			    ncstr->addr_head.path == NULL)
785b775b3eeSreyk 				fatal("calloc name");
786b775b3eeSreyk 			ncstr->addr_head.pool = cstr->addr_head.pool;
7873303745eSreyk 			ncstr->state = STATE_DNS_DONE;
788b775b3eeSreyk 			constraint_add(ncstr);
7893303745eSreyk 			constraint_cnt += constraint_init(ncstr);
7903303745eSreyk 		} else {
7913303745eSreyk 			h->next = ncstr->addr;
7923303745eSreyk 			ncstr->addr = h;
7933303745eSreyk 			ncstr->addr_head.a = h;
7943303745eSreyk 		}
7953303745eSreyk 	} while (len);
796b775b3eeSreyk 
797b775b3eeSreyk 	constraint_remove(cstr);
798b775b3eeSreyk }
799b775b3eeSreyk 
800bc58a738Sreyk int
constraint_cmp(const void * a,const void * b)801bc58a738Sreyk constraint_cmp(const void *a, const void *b)
802bc58a738Sreyk {
803cf493856Sotto 	time_t at = *(const time_t *)a;
804cf493856Sotto 	time_t bt = *(const time_t *)b;
805cf493856Sotto 	return at < bt ? -1 : (at > bt ? 1 : 0);
806bc58a738Sreyk }
807bc58a738Sreyk 
808bc58a738Sreyk void
constraint_update(void)809bc58a738Sreyk constraint_update(void)
810bc58a738Sreyk {
811bc58a738Sreyk 	struct constraint *cstr;
812bc58a738Sreyk 	int	 cnt, i;
813e6588cf8Sotto 	time_t	*values;
814bc58a738Sreyk 	time_t	 now;
815bc58a738Sreyk 
816bc58a738Sreyk 	now = getmonotime();
817bc58a738Sreyk 
818bc58a738Sreyk 	cnt = 0;
819bc58a738Sreyk 	TAILQ_FOREACH(cstr, &conf->constraints, entry) {
820bc58a738Sreyk 		if (cstr->state != STATE_REPLY_RECEIVED)
821bc58a738Sreyk 			continue;
822bc58a738Sreyk 		cnt++;
823bc58a738Sreyk 	}
824e6588cf8Sotto 	if (cnt == 0)
825e6588cf8Sotto 		return;
826bc58a738Sreyk 
827e6588cf8Sotto 	if ((values = calloc(cnt, sizeof(time_t))) == NULL)
828bc58a738Sreyk 		fatal("calloc");
829bc58a738Sreyk 
830bc58a738Sreyk 	i = 0;
831bc58a738Sreyk 	TAILQ_FOREACH(cstr, &conf->constraints, entry) {
832bc58a738Sreyk 		if (cstr->state != STATE_REPLY_RECEIVED)
833bc58a738Sreyk 			continue;
834e6588cf8Sotto 		values[i++] = cstr->constraint + (now - cstr->last);
835bc58a738Sreyk 	}
836bc58a738Sreyk 
837e6588cf8Sotto 	qsort(values, cnt, sizeof(time_t), constraint_cmp);
838bc58a738Sreyk 
839bc58a738Sreyk 	/* calculate median */
840bc58a738Sreyk 	i = cnt / 2;
841bc58a738Sreyk 	if (cnt % 2 == 0)
842e6588cf8Sotto 		conf->constraint_median = (values[i - 1] + values[i]) / 2;
843e6588cf8Sotto 	else
844e6588cf8Sotto 		conf->constraint_median = values[i];
845bc58a738Sreyk 
846bc58a738Sreyk 	conf->constraint_last = now;
847bc58a738Sreyk 
848e6588cf8Sotto 	free(values);
849bc58a738Sreyk }
850bc58a738Sreyk 
851bc58a738Sreyk void
constraint_reset(void)852bc58a738Sreyk constraint_reset(void)
853bc58a738Sreyk {
854bc58a738Sreyk 	struct constraint *cstr;
855bc58a738Sreyk 
856bc58a738Sreyk 	TAILQ_FOREACH(cstr, &conf->constraints, entry) {
857bc58a738Sreyk 		if (cstr->state == STATE_QUERY_SENT)
858bc58a738Sreyk 			continue;
859a257dd04Sreyk 		constraint_close(cstr->id);
860c7e8e3a2Sotto 		constraint_addr_head_clear(cstr);
861c7e8e3a2Sotto 		constraint_init(cstr);
862bc58a738Sreyk 	}
863bc58a738Sreyk 	conf->constraint_errors = 0;
864bc58a738Sreyk }
865bc58a738Sreyk 
866bc58a738Sreyk int
constraint_check(double val)867bc58a738Sreyk constraint_check(double val)
868bc58a738Sreyk {
869bc58a738Sreyk 	struct timeval	tv;
870e6588cf8Sotto 	double		diff;
871bc58a738Sreyk 	time_t		now;
872bc58a738Sreyk 
873bc58a738Sreyk 	if (conf->constraint_median == 0)
874bc58a738Sreyk 		return (0);
875bc58a738Sreyk 
876bc58a738Sreyk 	/* Calculate the constraint with the current offset */
877bc58a738Sreyk 	now = getmonotime();
878bc58a738Sreyk 	tv.tv_sec = conf->constraint_median + (now - conf->constraint_last);
879bc58a738Sreyk 	tv.tv_usec = 0;
880e6588cf8Sotto 	diff = fabs(val - gettime_from_timeval(&tv));
881bc58a738Sreyk 
882e6588cf8Sotto 	if (diff > CONSTRAINT_MARGIN) {
883dcbb241cSreyk 		if (conf->constraint_errors++ >
884dcbb241cSreyk 		    (CONSTRAINT_ERROR_MARGIN * peer_cnt)) {
885bc58a738Sreyk 			constraint_reset();
886bc58a738Sreyk 		}
887bc58a738Sreyk 
888bc58a738Sreyk 		return (-1);
889bc58a738Sreyk 	}
890bc58a738Sreyk 
891bc58a738Sreyk 	return (0);
892bc58a738Sreyk }
893bc58a738Sreyk 
894bc58a738Sreyk struct httpsdate *
httpsdate_init(const char * addr,const char * port,const char * hostname,const char * path,const u_int8_t * ca,size_t ca_len,int synced)89551d66267Sjsing httpsdate_init(const char *addr, const char *port, const char *hostname,
89605b37b28Sotto     const char *path, const u_int8_t *ca, size_t ca_len, int synced)
897bc58a738Sreyk {
898bc58a738Sreyk 	struct httpsdate	*httpsdate = NULL;
899bc58a738Sreyk 
900bc58a738Sreyk 	if ((httpsdate = calloc(1, sizeof(*httpsdate))) == NULL)
901bc58a738Sreyk 		goto fail;
902bc58a738Sreyk 
90351d66267Sjsing 	if (hostname == NULL)
90451d66267Sjsing 		hostname = addr;
905bc58a738Sreyk 
90651d66267Sjsing 	if ((httpsdate->tls_addr = strdup(addr)) == NULL ||
907bc58a738Sreyk 	    (httpsdate->tls_port = strdup(port)) == NULL ||
90851d66267Sjsing 	    (httpsdate->tls_hostname = strdup(hostname)) == NULL ||
909bc58a738Sreyk 	    (httpsdate->tls_path = strdup(path)) == NULL)
910bc58a738Sreyk 		goto fail;
911bc58a738Sreyk 
912bc58a738Sreyk 	if (asprintf(&httpsdate->tls_request,
913bc58a738Sreyk 	    "HEAD %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n",
91451d66267Sjsing 	    httpsdate->tls_path, httpsdate->tls_hostname) == -1)
915bc58a738Sreyk 		goto fail;
916bc58a738Sreyk 
917bc58a738Sreyk 	if ((httpsdate->tls_config = tls_config_new()) == NULL)
918bc58a738Sreyk 		goto fail;
9199b2f2e5bSjsing 	if (tls_config_set_ca_mem(httpsdate->tls_config, ca, ca_len) == -1)
920c7dffc0bSjsing 		goto fail;
921c7dffc0bSjsing 
922205dc5ecSjsing 	/*
923205dc5ecSjsing 	 * Due to the fact that we're trying to determine a constraint for time
924205dc5ecSjsing 	 * we do our own certificate validity checking, since the automatic
925205dc5ecSjsing 	 * version is based on our wallclock, which may well be inaccurate...
926205dc5ecSjsing 	 */
92705b37b28Sotto 	if (!synced) {
92814c44c9eSotto 		log_debug("constraints: using received time in certificate validation");
929205dc5ecSjsing 		tls_config_insecure_noverifytime(httpsdate->tls_config);
93005b37b28Sotto 	}
931205dc5ecSjsing 
932bc58a738Sreyk 	return (httpsdate);
933bc58a738Sreyk 
934bc58a738Sreyk  fail:
935bc58a738Sreyk 	httpsdate_free(httpsdate);
936bc58a738Sreyk 	return (NULL);
937bc58a738Sreyk }
938bc58a738Sreyk 
939bc58a738Sreyk void
httpsdate_free(void * arg)940bc58a738Sreyk httpsdate_free(void *arg)
941bc58a738Sreyk {
942bc58a738Sreyk 	struct httpsdate *httpsdate = arg;
943bc58a738Sreyk 	if (httpsdate == NULL)
944bc58a738Sreyk 		return;
945bc58a738Sreyk 	if (httpsdate->tls_ctx)
946bc58a738Sreyk 		tls_close(httpsdate->tls_ctx);
947bc58a738Sreyk 	tls_free(httpsdate->tls_ctx);
948bc58a738Sreyk 	tls_config_free(httpsdate->tls_config);
94951d66267Sjsing 	free(httpsdate->tls_addr);
950bc58a738Sreyk 	free(httpsdate->tls_port);
95151d66267Sjsing 	free(httpsdate->tls_hostname);
952bc58a738Sreyk 	free(httpsdate->tls_path);
953bc58a738Sreyk 	free(httpsdate->tls_request);
954bc58a738Sreyk 	free(httpsdate);
955bc58a738Sreyk }
956bc58a738Sreyk 
957bc58a738Sreyk int
httpsdate_request(struct httpsdate * httpsdate,struct timeval * when,int synced)95805b37b28Sotto httpsdate_request(struct httpsdate *httpsdate, struct timeval *when, int synced)
959bc58a738Sreyk {
960b7e9bd4fSjsing 	char	 timebuf1[32], timebuf2[32];
961a9fc612dSbluhm 	size_t	 outlen = 0, maxlength = CONSTRAINT_MAXHEADERLENGTH, len;
962a9fc612dSbluhm 	char	*line, *p, *buf;
963b7e9bd4fSjsing 	time_t	 httptime, notbefore, notafter;
964b7e9bd4fSjsing 	struct tm *tm;
965cd548fa4Sbeck 	ssize_t	 ret;
966bc58a738Sreyk 
967bc58a738Sreyk 	if ((httpsdate->tls_ctx = tls_client()) == NULL)
968bc58a738Sreyk 		goto fail;
969bc58a738Sreyk 
970bc58a738Sreyk 	if (tls_configure(httpsdate->tls_ctx, httpsdate->tls_config) == -1)
971bc58a738Sreyk 		goto fail;
972bc58a738Sreyk 
973b7f2e836Sreyk 	/*
974b7f2e836Sreyk 	 * libtls expects an address string, which can also be a DNS name,
975b7f2e836Sreyk 	 * but we pass a pre-resolved IP address string in tls_addr so it
976b7f2e836Sreyk 	 * does not trigger any DNS operation and is safe to be called
977b7f2e836Sreyk 	 * without the dns pledge.
978b7f2e836Sreyk 	 */
9799083f23cSjsing 	if (tls_connect_servername(httpsdate->tls_ctx, httpsdate->tls_addr,
9809083f23cSjsing 	    httpsdate->tls_port, httpsdate->tls_hostname) == -1) {
981afaa0890Sderaadt 		log_debug("tls connect failed: %s (%s): %s",
9829083f23cSjsing 		    httpsdate->tls_addr, httpsdate->tls_hostname,
983bc58a738Sreyk 		    tls_error(httpsdate->tls_ctx));
984bc58a738Sreyk 		goto fail;
985bc58a738Sreyk 	}
986bc58a738Sreyk 
987a9fc612dSbluhm 	buf = httpsdate->tls_request;
988a9fc612dSbluhm 	len = strlen(httpsdate->tls_request);
989a9fc612dSbluhm 	while (len > 0) {
990caf85d1dSbeck 		ret = tls_write(httpsdate->tls_ctx, buf, len);
991caf85d1dSbeck 		if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT)
992a9fc612dSbluhm 			continue;
993df69c215Sderaadt 		if (ret == -1) {
9949083f23cSjsing 			log_warnx("tls write failed: %s (%s): %s",
9959083f23cSjsing 			    httpsdate->tls_addr, httpsdate->tls_hostname,
9969083f23cSjsing 			    tls_error(httpsdate->tls_ctx));
997bc58a738Sreyk 			goto fail;
9989083f23cSjsing 		}
999caf85d1dSbeck 		buf += ret;
1000caf85d1dSbeck 		len -= ret;
1001a9fc612dSbluhm 	}
1002bc58a738Sreyk 
1003bc58a738Sreyk 	while ((line = tls_readline(httpsdate->tls_ctx, &outlen,
1004bc58a738Sreyk 	    &maxlength, when)) != NULL) {
1005bc58a738Sreyk 		line[strcspn(line, "\r\n")] = '\0';
1006bc58a738Sreyk 
1007bc58a738Sreyk 		if ((p = strchr(line, ' ')) == NULL || *p == '\0')
1008bc58a738Sreyk 			goto next;
1009bc58a738Sreyk 		*p++ = '\0';
1010bc58a738Sreyk 		if (strcasecmp("Date:", line) != 0)
1011bc58a738Sreyk 			goto next;
1012bc58a738Sreyk 
1013bc58a738Sreyk 		/*
1014bc58a738Sreyk 		 * Expect the date/time format as IMF-fixdate which is
1015bc58a738Sreyk 		 * mandated by HTTP/1.1 in the new RFC 7231 and was
1016bc58a738Sreyk 		 * preferred by RFC 2616.  Other formats would be RFC 850
1017bc58a738Sreyk 		 * or ANSI C's asctime() - the latter doesn't include
1018bc58a738Sreyk 		 * the timezone which is required here.
1019bc58a738Sreyk 		 */
1020b7e9bd4fSjsing 		if (strptime(p, IMF_FIXDATE,
1021bc58a738Sreyk 		    &httpsdate->tls_tm) == NULL) {
1022bc58a738Sreyk 			log_warnx("unsupported date format");
1023bc58a738Sreyk 			free(line);
1024b0ee91feSotto 			goto fail;
1025bc58a738Sreyk 		}
1026bc58a738Sreyk 
1027bc58a738Sreyk 		free(line);
1028bc58a738Sreyk 		break;
1029bc58a738Sreyk  next:
1030bc58a738Sreyk 		free(line);
1031bc58a738Sreyk 	}
1032b0ee91feSotto 	if (httpsdate->tls_tm.tm_year == 0)
1033b0ee91feSotto 		goto fail;
1034bc58a738Sreyk 
103505b37b28Sotto 	/* If we are synced, we already checked the certificate validity */
103605b37b28Sotto 	if (synced)
103705b37b28Sotto 		return 0;
103805b37b28Sotto 
1039205dc5ecSjsing 	/*
1040205dc5ecSjsing 	 * Now manually check the validity of the certificate presented in the
1041205dc5ecSjsing 	 * TLS handshake, based on the time specified by the server's HTTP Date:
1042205dc5ecSjsing 	 * header.
1043205dc5ecSjsing 	 */
1044b7e9bd4fSjsing 	notbefore = tls_peer_cert_notbefore(httpsdate->tls_ctx);
1045b7e9bd4fSjsing 	notafter = tls_peer_cert_notafter(httpsdate->tls_ctx);
10464dd4abe4Sjsing 	if ((httptime = timegm(&httpsdate->tls_tm)) == -1)
10474dd4abe4Sjsing 		goto fail;
1048b7e9bd4fSjsing 	if (httptime <= notbefore) {
1049b7e9bd4fSjsing 		if ((tm = gmtime(&notbefore)) == NULL)
1050b7e9bd4fSjsing 			goto fail;
1051b7e9bd4fSjsing 		if (strftime(timebuf1, sizeof(timebuf1), X509_DATE, tm) == 0)
1052b7e9bd4fSjsing 			goto fail;
1053b7e9bd4fSjsing 		if (strftime(timebuf2, sizeof(timebuf2), X509_DATE,
1054b7e9bd4fSjsing 		    &httpsdate->tls_tm) == 0)
1055b7e9bd4fSjsing 			goto fail;
1056b7e9bd4fSjsing 		log_warnx("tls certificate not yet valid: %s (%s): "
1057b7e9bd4fSjsing 		    "not before %s, now %s", httpsdate->tls_addr,
1058b7e9bd4fSjsing 		    httpsdate->tls_hostname, timebuf1, timebuf2);
1059b7e9bd4fSjsing 		goto fail;
1060b7e9bd4fSjsing 	}
1061b7e9bd4fSjsing 	if (httptime >= notafter) {
1062b7e9bd4fSjsing 		if ((tm = gmtime(&notafter)) == NULL)
1063b7e9bd4fSjsing 			goto fail;
1064b7e9bd4fSjsing 		if (strftime(timebuf1, sizeof(timebuf1), X509_DATE, tm) == 0)
1065b7e9bd4fSjsing 			goto fail;
1066b7e9bd4fSjsing 		if (strftime(timebuf2, sizeof(timebuf2), X509_DATE,
1067b7e9bd4fSjsing 		    &httpsdate->tls_tm) == 0)
1068b7e9bd4fSjsing 			goto fail;
1069b7e9bd4fSjsing 		log_warnx("tls certificate expired: %s (%s): "
1070b7e9bd4fSjsing 		    "not after %s, now %s", httpsdate->tls_addr,
1071b7e9bd4fSjsing 		    httpsdate->tls_hostname, timebuf1, timebuf2);
1072205dc5ecSjsing 		goto fail;
1073205dc5ecSjsing 	}
1074205dc5ecSjsing 
1075bc58a738Sreyk 	return (0);
10769083f23cSjsing 
1077bc58a738Sreyk  fail:
1078bc58a738Sreyk 	httpsdate_free(httpsdate);
1079bc58a738Sreyk 	return (-1);
1080bc58a738Sreyk }
1081bc58a738Sreyk 
1082bc58a738Sreyk void *
httpsdate_query(const char * addr,const char * port,const char * hostname,const char * path,const u_int8_t * ca,size_t ca_len,struct timeval * rectv,struct timeval * xmttv,int synced)108351d66267Sjsing httpsdate_query(const char *addr, const char *port, const char *hostname,
1084bc58a738Sreyk     const char *path, const u_int8_t *ca, size_t ca_len,
108505b37b28Sotto     struct timeval *rectv, struct timeval *xmttv, int synced)
1086bc58a738Sreyk {
1087bc58a738Sreyk 	struct httpsdate	*httpsdate;
1088bc58a738Sreyk 	struct timeval		 when;
1089bc58a738Sreyk 	time_t			 t;
1090bc58a738Sreyk 
109151d66267Sjsing 	if ((httpsdate = httpsdate_init(addr, port, hostname, path,
109205b37b28Sotto 	    ca, ca_len, synced)) == NULL)
1093bc58a738Sreyk 		return (NULL);
1094bc58a738Sreyk 
109505b37b28Sotto 	if (httpsdate_request(httpsdate, &when, synced) == -1)
1096bc58a738Sreyk 		return (NULL);
1097bc58a738Sreyk 
1098bc58a738Sreyk 	/* Return parsed date as local time */
1099bc58a738Sreyk 	t = timegm(&httpsdate->tls_tm);
1100bc58a738Sreyk 
1101bc58a738Sreyk 	/* Report parsed Date: as "received time" */
1102bc58a738Sreyk 	rectv->tv_sec = t;
1103bc58a738Sreyk 	rectv->tv_usec = 0;
1104bc58a738Sreyk 
1105bc58a738Sreyk 	/* And add delay as "transmit time" */
1106bc58a738Sreyk 	xmttv->tv_sec = when.tv_sec;
1107bc58a738Sreyk 	xmttv->tv_usec = when.tv_usec;
1108bc58a738Sreyk 
1109bc58a738Sreyk 	return (httpsdate);
1110bc58a738Sreyk }
1111bc58a738Sreyk 
1112bc58a738Sreyk /* Based on SSL_readline in ftp/fetch.c */
1113bc58a738Sreyk char *
tls_readline(struct tls * tls,size_t * lenp,size_t * maxlength,struct timeval * when)1114bc58a738Sreyk tls_readline(struct tls *tls, size_t *lenp, size_t *maxlength,
1115bc58a738Sreyk     struct timeval *when)
1116bc58a738Sreyk {
1117caf85d1dSbeck 	size_t i, len;
1118bc58a738Sreyk 	char *buf, *q, c;
1119cd548fa4Sbeck 	ssize_t ret;
1120bc58a738Sreyk 
1121bc58a738Sreyk 	len = 128;
1122bc58a738Sreyk 	if ((buf = malloc(len)) == NULL)
1123bc58a738Sreyk 		fatal("Can't allocate memory for transfer buffer");
1124bc58a738Sreyk 	for (i = 0; ; i++) {
1125bc58a738Sreyk 		if (i >= len - 1) {
1126bc58a738Sreyk 			if ((q = reallocarray(buf, len, 2)) == NULL)
1127bc58a738Sreyk 				fatal("Can't expand transfer buffer");
1128bc58a738Sreyk 			buf = q;
1129bc58a738Sreyk 			len *= 2;
1130bc58a738Sreyk 		}
1131bc58a738Sreyk  again:
1132caf85d1dSbeck 		ret = tls_read(tls, &c, 1);
1133caf85d1dSbeck 		if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT)
1134bc58a738Sreyk 			goto again;
1135df69c215Sderaadt 		if (ret == -1) {
1136bc58a738Sreyk 			/* SSL read error, ignore */
113787e97f23Sjsg 			free(buf);
1138bc58a738Sreyk 			return (NULL);
1139bc58a738Sreyk 		}
1140bc58a738Sreyk 
1141bc58a738Sreyk 		if (maxlength != NULL && (*maxlength)-- == 0) {
1142bc58a738Sreyk 			log_warnx("maximum length exceeded");
11439d863a9fSmillert 			free(buf);
1144bc58a738Sreyk 			return (NULL);
1145bc58a738Sreyk 		}
1146bc58a738Sreyk 
1147bc58a738Sreyk 		buf[i] = c;
1148bc58a738Sreyk 		if (c == '\n')
1149bc58a738Sreyk 			break;
1150bc58a738Sreyk 	}
1151bc58a738Sreyk 	*lenp = i;
1152bc58a738Sreyk 	if (gettimeofday(when, NULL) == -1)
1153bc58a738Sreyk 		fatal("gettimeofday");
1154bc58a738Sreyk 	return (buf);
1155bc58a738Sreyk }
1156a257dd04Sreyk 
1157a257dd04Sreyk char *
get_string(u_int8_t * ptr,size_t len)1158a257dd04Sreyk get_string(u_int8_t *ptr, size_t len)
1159a257dd04Sreyk {
1160a257dd04Sreyk 	size_t	 i;
1161a257dd04Sreyk 
1162a257dd04Sreyk 	for (i = 0; i < len; i++)
1163a257dd04Sreyk 		if (!(isprint(ptr[i]) || isspace(ptr[i])))
1164a257dd04Sreyk 			break;
1165a257dd04Sreyk 
1166b52b3078Smmcc 	return strndup(ptr, i);
1167a257dd04Sreyk }
1168