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(¬before)) == 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(¬after)) == 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