diff options
author | Florian Obser <florian@cvs.openbsd.org> | 2019-02-07 17:20:36 +0000 |
---|---|---|
committer | Florian Obser <florian@cvs.openbsd.org> | 2019-02-07 17:20:36 +0000 |
commit | 5d9974cd143e70d683c9ca6ba60503c6fd4fcf55 (patch) | |
tree | 35d27d2c840b4f8804e2f13edb766ac16405bbb2 /sbin/unwind | |
parent | eb2da26d8a159b4d500eb10e771f34b6758b5065 (diff) |
Rewrite trust anchor handling.
Do not use the libunbound's auto trust anchor file feature since it
then the resolver process needs rpath, wpath, and cpath pledges and
permission on the trust anchor file.
Instead configure the trust anchor as resource record strings. The
parent process opens the file, passes a filedescriptor to the frontend
process to parse the file and then passes trust anchors to the
resolver process to (re-) configure the resolver contexts.
The resolver process periodically probes for new trust anchors (DNSKEY
records of the root zone) and passes those to the frontend process.
This in turn requests a file descripter for writing from the parent
process. Once the trust anchors have been written the parent process
renames the tmp file to the final location.
Also provide a built in trust anchor for boot strapping purposes if no
file is found on disk. That way we can get rid of unbound-anchor in
unwind's rc.d script.
Diffstat (limited to 'sbin/unwind')
-rw-r--r-- | sbin/unwind/captiveportal.c | 6 | ||||
-rw-r--r-- | sbin/unwind/frontend.c | 210 | ||||
-rw-r--r-- | sbin/unwind/frontend.h | 14 | ||||
-rw-r--r-- | sbin/unwind/resolver.c | 247 | ||||
-rw-r--r-- | sbin/unwind/unwind.c | 102 | ||||
-rw-r--r-- | sbin/unwind/unwind.h | 15 |
6 files changed, 549 insertions, 45 deletions
diff --git a/sbin/unwind/captiveportal.c b/sbin/unwind/captiveportal.c index c9a9f8f1d34..ac856d54a9c 100644 --- a/sbin/unwind/captiveportal.c +++ b/sbin/unwind/captiveportal.c @@ -1,4 +1,4 @@ -/* $OpenBSD: captiveportal.c,v 1.1 2019/02/03 12:02:30 florian Exp $ */ +/* $OpenBSD: captiveportal.c,v 1.2 2019/02/07 17:20:35 florian Exp $ */ /* * Copyright (c) 2018 Florian Obser <florian@openbsd.org> @@ -302,6 +302,10 @@ captiveportal_dispatch_main(int fd, short event, void *bula) iev_frontend->events, iev_frontend->handler, iev_frontend); event_add(&iev_frontend->ev, NULL); break; + case IMSG_STARTUP: + if (pledge("stdio", NULL) == -1) + fatal("pledge"); + break; case IMSG_RECONF_CONF: if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct unwind_conf)) diff --git a/sbin/unwind/frontend.c b/sbin/unwind/frontend.c index b7e4381c99e..6fb169e7717 100644 --- a/sbin/unwind/frontend.c +++ b/sbin/unwind/frontend.c @@ -1,4 +1,4 @@ -/* $OpenBSD: frontend.c,v 1.10 2019/02/03 12:02:30 florian Exp $ */ +/* $OpenBSD: frontend.c,v 1.11 2019/02/07 17:20:35 florian Exp $ */ /* * Copyright (c) 2018 Florian Obser <florian@openbsd.org> @@ -88,6 +88,9 @@ void get_rtaddrs(int, struct sockaddr *, void rtmget_default(void); struct pending_query *find_pending_query(uint64_t); void parse_dhcp_lease(int); +void parse_trust_anchor(struct trust_anchor_head *, int); +void send_trust_anchors(struct trust_anchor_head *); +void write_trust_anchors(struct trust_anchor_head *, int); struct unwind_conf *frontend_conf; struct imsgev *iev_main; @@ -96,6 +99,9 @@ struct imsgev *iev_captiveportal; struct event ev_route; int udp4sock = -1, udp6sock = -1, routesock = -1; +static struct trust_anchor_head built_in_trust_anchors; +static struct trust_anchor_head trust_anchors, new_trust_anchors; + void frontend_sig_handler(int sig, short event, void *bula) { @@ -194,6 +200,12 @@ frontend(int debug, int verbose) TAILQ_INIT(&pending_queries); + TAILQ_INIT(&built_in_trust_anchors); + TAILQ_INIT(&trust_anchors); + TAILQ_INIT(&new_trust_anchors); + + add_new_ta(&built_in_trust_anchors, KSK2017); + event_dispatch(); frontend_shutdown(); @@ -460,6 +472,21 @@ frontend_dispatch_main(int fd, short event, void *bula) __func__); parse_dhcp_lease(fd); break; + case IMSG_TAFD: + if ((fd = imsg.fd) != -1) + parse_trust_anchor(&trust_anchors, fd); + if (!TAILQ_EMPTY(&trust_anchors)) + send_trust_anchors(&trust_anchors); + else + send_trust_anchors(&built_in_trust_anchors); + break; + case IMSG_TAFD_W: + if ((fd = imsg.fd) == -1) + fatalx("%s: expected to receive imsg trust " + "anchor fd but didn't receive any", + __func__); + write_trust_anchors(&trust_anchors, fd); + break; default: log_debug("%s: error handling imsg %d", __func__, imsg.hdr.type); @@ -484,7 +511,8 @@ frontend_dispatch_resolver(int fd, short event, void *bula) struct imsgbuf *ibuf = &iev->ibuf; struct imsg imsg; struct query_imsg *query_imsg; - int n, shut = 0; + int n, shut = 0, chg; + char *ta; if (event & EV_READ) { if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) @@ -555,6 +583,31 @@ frontend_dispatch_resolver(int fd, short event, void *bula) case IMSG_CTL_END: control_imsg_relay(&imsg); break; + case IMSG_NEW_TA: + /* make sure this is a string */ + ((char *)imsg.data)[imsg.hdr.len - IMSG_HEADER_SIZE - 1] + = '\0'; + ta = imsg.data; + add_new_ta(&new_trust_anchors, ta); + break; + case IMSG_NEW_TAS_ABORT: + log_debug("%s: IMSG_NEW_TAS_ABORT", __func__); + free_tas(&new_trust_anchors); + break; + case IMSG_NEW_TAS_DONE: + chg = merge_tas(&new_trust_anchors, &trust_anchors); + log_debug("%s: IMSG_NEW_TAS_DONE: change: %d", + __func__, chg); + if (chg) { + send_trust_anchors(&trust_anchors); + } + /* + * always write trust anchors, the modify date on + * the file is an indication when we made progress + */ + frontend_imsg_compose_main(IMSG_OPEN_TA_W, 0, NULL, + 0); + break; default: log_debug("%s: error handling imsg %d", __func__, imsg.hdr.type); @@ -624,6 +677,8 @@ frontend_startup(void) event_add(&ev_route, NULL); + frontend_imsg_compose_main(IMSG_OPEN_TA_RO, 0, NULL, 0); + frontend_imsg_compose_main(IMSG_STARTUP_DONE, 0, NULL, 0); rtmget_default(); } @@ -1028,3 +1083,154 @@ parse_dhcp_lease(int fd) strlen(ns) + 1); } } + + +void +add_new_ta(struct trust_anchor_head *tah, char *val) +{ + + struct trust_anchor *ta, *i; + int cmp; + + if ((ta = malloc(sizeof(*ta))) == NULL) + fatal("%s", __func__); + if ((ta->ta = strdup(val)) == NULL) + fatal("%s", __func__); + + /* keep the list sorted to prevent churn if the order changes in DNS */ + TAILQ_FOREACH(i, tah, entry) { + cmp = strcmp(i->ta, ta->ta); + if ( cmp == 0) { + /* duplicate */ + free(ta->ta); + free(ta); + return; + } else if (cmp > 0) { + TAILQ_INSERT_BEFORE(i, ta, entry); + return; + } + } + TAILQ_INSERT_TAIL(tah, ta, entry); +} + +void +free_tas(struct trust_anchor_head *tah) +{ + struct trust_anchor *ta; + + while ((ta = TAILQ_FIRST(tah))) { + TAILQ_REMOVE(tah, ta, entry); + free(ta->ta); + free(ta); + } +} + +int +merge_tas(struct trust_anchor_head *newh, struct trust_anchor_head *oldh) +{ + struct trust_anchor *i, *j; + int chg = 0; + + j = TAILQ_FIRST(oldh); + + TAILQ_FOREACH(i, newh, entry) { + if (j == NULL || strcmp(i->ta, j->ta) != 0) { + chg = 1; + break; + } + j = TAILQ_NEXT(j, entry); + } + if (j!= NULL) + chg = 1; + + if (chg) { + free_tas(oldh); + while((i = TAILQ_FIRST(newh)) != NULL) { + TAILQ_REMOVE(newh, i, entry); + TAILQ_INSERT_TAIL(oldh, i, entry); + } + } else { + free_tas(newh); + } + return (chg); +} + +void +parse_trust_anchor(struct trust_anchor_head *tah, int fd) +{ + FILE *f; + char *line = NULL, *p; + size_t linesize = 0; + ssize_t linelen; + + if((f = fdopen(fd, "r")) == NULL) { + log_warn("cannot read trust anchor file"); + close(fd); + return; + } + + while ((linelen = getline(&line, &linesize, f)) != -1) { + if (*line == ';') + continue; + p = strchr(line, ';'); + if (p == NULL) + p = strchr(line, '\n'); + if (p != NULL) { + do { + p--; + } while(p != line && *p == ' '); + *(p + 1) = '\0'; + } + log_debug("%s: %s", __func__, line); + add_new_ta(tah, line); + } + free(line); + if (ferror(f)) + log_warn("getline"); + fclose(f); +} + +void +send_trust_anchors(struct trust_anchor_head *tah) +{ + struct trust_anchor *ta; + + TAILQ_FOREACH(ta, tah, entry) + frontend_imsg_compose_resolver(IMSG_NEW_TA, 0, ta->ta, + strlen(ta->ta) + 1); + frontend_imsg_compose_resolver(IMSG_NEW_TAS_DONE, 0, NULL, 0); +} + +void +write_trust_anchors(struct trust_anchor_head *tah, int fd) +{ + FILE *f; + struct trust_anchor *ta; + + if((f = fdopen(fd, "w+")) == NULL) { + log_warn("cannot open trust anchor file for writing"); + goto err; + } + + TAILQ_FOREACH(ta, tah, entry) + if (fprintf(f, "%s\n", ta->ta) < 0) + goto err; + if (ferror(f)) { + log_warn("%s", __func__); + goto err; + } + if (fclose(f) != 0) { + f = NULL; + log_warn("%s", __func__); + goto err; + } + frontend_imsg_compose_main(IMSG_TA_W_DONE, 0, NULL, 0); + return; +err: + if (f == NULL) + close(fd); + else + fclose(f); + + frontend_imsg_compose_main(IMSG_TA_W_FAILED, 0, NULL, 0); +} diff --git a/sbin/unwind/frontend.h b/sbin/unwind/frontend.h index 102df500cf3..6057b8fac17 100644 --- a/sbin/unwind/frontend.h +++ b/sbin/unwind/frontend.h @@ -1,4 +1,4 @@ -/* $OpenBSD: frontend.h,v 1.2 2019/02/03 12:02:30 florian Exp $ */ +/* $OpenBSD: frontend.h,v 1.3 2019/02/07 17:20:35 florian Exp $ */ /* * Copyright (c) 2018 Florian Obser <florian@openbsd.org> @@ -19,6 +19,14 @@ TAILQ_HEAD(ctl_conns, ctl_conn) ctl_conns; +struct trust_anchor { + TAILQ_ENTRY(trust_anchor) entry; + char *ta; +}; + +TAILQ_HEAD(trust_anchor_head, trust_anchor); + + void frontend(int, int); void frontend_dispatch_main(int, short, void *); void frontend_dispatch_resolver(int, short, void *); @@ -27,3 +35,7 @@ int frontend_imsg_compose_main(int, pid_t, void *, uint16_t); int frontend_imsg_compose_resolver(int, pid_t, void *, uint16_t); int frontend_imsg_compose_captiveportal(int, pid_t, void *, uint16_t); char *ip_port(struct sockaddr *); +void add_new_ta(struct trust_anchor_head *, char *); +void free_tas(struct trust_anchor_head *); +int merge_tas(struct trust_anchor_head *, + struct trust_anchor_head *); diff --git a/sbin/unwind/resolver.c b/sbin/unwind/resolver.c index ab1c2e571da..c12f43561d0 100644 --- a/sbin/unwind/resolver.c +++ b/sbin/unwind/resolver.c @@ -1,4 +1,4 @@ -/* $OpenBSD: resolver.c,v 1.15 2019/02/05 19:32:24 florian Exp $ */ +/* $OpenBSD: resolver.c,v 1.16 2019/02/07 17:20:35 florian Exp $ */ /* * Copyright (c) 2018 Florian Obser <florian@openbsd.org> @@ -56,13 +56,18 @@ #include "unwind.h" #include "resolver.h" -#define CHROOT "/etc/unwind" -#define DB_DIR "/trustanchor/" -#define ROOT_KEY DB_DIR"root.key" - #define UB_LOG_VERBOSE 4 #define UB_LOG_BRIEF 0 +/* don't cause churn when trust anchor comes from a cache */ +#define ROOT_DNSKEY_TTL 172800 + +#define PORTAL_CHECK_SEC 15 +#define PORTAL_CHECK_MAXSEC 600 + +#define TRUST_ANCHOR_RETRY_INTERVAL 8640 +#define TRUST_ANCHOR_QUERY_INTERVAL 43200 + struct unwind_resolver { struct event check_ev; struct event free_ev; @@ -125,6 +130,11 @@ void check_captive_portal_resolve_done(void *, int, void *, int, int, char *, int); int check_captive_portal_changed(struct unwind_conf *, struct unwind_conf *); +void trust_anchor_resolve(void); +void trust_anchor_timo(int, short, void *); +void trust_anchor_resolve_done(void *, int, void *, int, + int, char *, int); + /* for openssl */ void init_locks(void); unsigned long id_callback(void); @@ -138,10 +148,14 @@ struct unwind_forwarder_head dhcp_forwarder_list; struct unwind_resolver *recursor, *forwarder, *static_forwarder; struct unwind_resolver *static_dot_forwarder; struct timeval resolver_check_pause = { 30, 0}; -#define PORTAL_CHECK_SEC 15 -#define PORTAL_CHECK_MAXSEC 600 + struct timeval captive_portal_check_tv = {PORTAL_CHECK_SEC, 0}; struct event captive_portal_check_ev; + +struct event trust_anchor_timer; + +static struct trust_anchor_head trust_anchors, new_trust_anchors; + struct event_base *ev_base; /* for openssl */ @@ -181,7 +195,7 @@ resolver(int debug, int verbose) if ((pw = getpwnam(UNWIND_USER)) == NULL) fatal("getpwnam"); - if (chroot(CHROOT) == -1) + if (chroot(pw->pw_dir) == -1) fatal("chroot"); if (chdir("/") == -1) fatal("chdir(\"/\")"); @@ -195,9 +209,7 @@ resolver(int debug, int verbose) setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("can't drop privileges"); - unveil(DB_DIR, "rwc"); - - if (pledge("stdio inet dns rpath wpath cpath recvfd", NULL) == -1) + if (pledge("stdio inet dns recvfd", NULL) == -1) fatal("pledge"); ev_base = event_init(); @@ -224,6 +236,7 @@ resolver(int debug, int verbose) event_add(&iev_main->ev, NULL); evtimer_set(&captive_portal_check_ev, check_captive_portal_timo, NULL); + evtimer_set(&trust_anchor_timer, trust_anchor_timo, NULL); init_locks(); CRYPTO_set_id_callback(id_callback); @@ -232,6 +245,8 @@ resolver(int debug, int verbose) new_recursor(); SIMPLEQ_INIT(&dhcp_forwarder_list); + TAILQ_INIT(&trust_anchors); + TAILQ_INIT(&new_trust_anchors); event_dispatch(); @@ -295,6 +310,7 @@ resolver_dispatch_frontend(int fd, short event, void *bula) ssize_t n; int shut = 0, verbose, err; int update_resolvers; + char *ta; ibuf = &iev->ibuf; @@ -382,6 +398,26 @@ resolver_dispatch_frontend(int fd, short event, void *bula) case IMSG_CTL_RECHECK_CAPTIVEPORTAL: check_captive_portal(1); break; + case IMSG_NEW_TA: + /* make sure this is a string */ + ((char *)imsg.data)[imsg.hdr.len - IMSG_HEADER_SIZE - 1] + = '\0'; + ta = imsg.data; + add_new_ta(&new_trust_anchors, ta); + break; + case IMSG_NEW_TAS_ABORT: + log_debug("%s: IMSG_NEW_TAS_ABORT", __func__); + free_tas(&new_trust_anchors); + break; + case IMSG_NEW_TAS_DONE: + log_debug("%s: IMSG_NEW_TAS_DONE", __func__); + if (merge_tas(&new_trust_anchors, &trust_anchors)) { + new_recursor(); + new_forwarders(); + new_static_forwarders(); + new_static_dot_forwarders(); + } + break; default: log_debug("%s: unexpected imsg %d", __func__, imsg.hdr.type); @@ -549,6 +585,10 @@ resolver_dispatch_main(int fd, short event, void *bula) iev_captiveportal); event_add(&iev_captiveportal->ev, NULL); break; + case IMSG_STARTUP: + if (pledge("stdio inet dns", NULL) == -1) + fatal("pledge"); + break; case IMSG_RECONF_CONF: if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct unwind_conf)) @@ -770,6 +810,11 @@ void new_recursor(void) { free_resolver(recursor); + recursor = NULL; + + if (TAILQ_EMPTY(&trust_anchors)) + return; + recursor = create_resolver(RECURSOR); check_resolver(recursor); } @@ -778,11 +823,13 @@ void new_forwarders(void) { free_resolver(forwarder); + forwarder = NULL; - if (SIMPLEQ_EMPTY(&dhcp_forwarder_list)) { - forwarder = NULL; + if (SIMPLEQ_EMPTY(&dhcp_forwarder_list)) + return; + + if (TAILQ_EMPTY(&trust_anchors)) return; - } log_debug("%s: create_resolver", __func__); forwarder = create_resolver(FORWARDER); @@ -795,11 +842,13 @@ void new_static_forwarders(void) { free_resolver(static_forwarder); + static_forwarder = NULL; - if (SIMPLEQ_EMPTY(&resolver_conf->unwind_forwarder_list)) { - static_forwarder = NULL; + if (SIMPLEQ_EMPTY(&resolver_conf->unwind_forwarder_list)) + return; + + if (TAILQ_EMPTY(&trust_anchors)) return; - } log_debug("%s: create_resolver", __func__); static_forwarder = create_resolver(STATIC_FORWARDER); @@ -812,11 +861,13 @@ void new_static_dot_forwarders(void) { free_resolver(static_dot_forwarder); + static_dot_forwarder = NULL; - if (SIMPLEQ_EMPTY(&resolver_conf->unwind_dot_forwarder_list)) { - static_dot_forwarder = NULL; + if (SIMPLEQ_EMPTY(&resolver_conf->unwind_dot_forwarder_list)) + return; + + if (TAILQ_EMPTY(&trust_anchors)) return; - } log_debug("%s: create_resolver", __func__); static_dot_forwarder = create_resolver(STATIC_DOT_FORWARDER); @@ -831,6 +882,7 @@ struct unwind_resolver * create_resolver(enum unwind_resolver_type type) { struct unwind_resolver *res; + struct trust_anchor *ta; int err; if ((res = calloc(1, sizeof(*res))) == NULL) { @@ -853,12 +905,14 @@ create_resolver(enum unwind_resolver_type type) ub_ctx_debuglevel(res->ctx, log_getverbose() & OPT_VERBOSE2 ? UB_LOG_VERBOSE : UB_LOG_BRIEF); - if ((err = ub_ctx_add_ta_autr(res->ctx, ROOT_KEY)) != 0) { - ub_ctx_delete(res->ctx); - free(res); - log_warnx("error adding trust anchor: %s", - ub_strerror(err)); - return (NULL); + TAILQ_FOREACH(ta, &trust_anchors, entry) { + if ((err = ub_ctx_add_ta(res->ctx, ta->ta)) != 0) { + ub_ctx_delete(res->ctx); + free(res); + log_warnx("error adding trust anchor: %s", + ub_strerror(err)); + return (NULL); + } } if((err = ub_ctx_set_option(res->ctx, "aggressive-nsec:", "yes")) @@ -981,6 +1035,7 @@ check_resolver_done(void *arg, int rcode, void *answer_packet, int answer_len, { struct check_resolver_data *data; struct unwind_resolver *best; + struct timeval tv = {0, 1}; char *str; data = (struct check_resolver_data *)arg; @@ -1003,9 +1058,11 @@ check_resolver_done(void *arg, int rcode, void *answer_packet, int answer_len, free(str); } - if (sec == 2) + if (sec == 2) { data->res->state = VALIDATING; - else if (rcode == LDNS_RCODE_NOERROR && + if (!(evtimer_pending(&trust_anchor_timer, NULL))) + evtimer_add(&trust_anchor_timer, &tv); + } else if (rcode == LDNS_RCODE_NOERROR && LDNS_RCODE_WIRE((uint8_t*)answer_packet) == LDNS_RCODE_NOERROR) { log_debug("%s: why bogus: %s", __func__, why_bogus); data->res->state = RESOLVING; @@ -1332,7 +1389,6 @@ check_captive_portal(int timer_reset) evtimer_add(&captive_portal_check_ev, &captive_portal_check_tv); - captive_portal_state = PORTAL_UNKNOWN; res = forwarder; @@ -1387,8 +1443,8 @@ check_captive_portal_resolve_done(void *arg, int rcode, void *answer_packet, sldns_buffer_write(buf, answer_packet, answer_len); sldns_buffer_flip(buf); libworker_enter_result(result, buf, region, sec); - result->answer_packet = NULL; //answer_packet; - result->answer_len = 0; //answer_len; + result->answer_packet = NULL; + result->answer_len = 0; sldns_buffer_free(buf); regional_destroy(region); @@ -1454,3 +1510,132 @@ check_captive_portal_changed(struct unwind_conf *a, struct unwind_conf *b) return (0); } + +void +trust_anchor_resolve(void) +{ + struct unwind_resolver *res; + struct timeval tv = {TRUST_ANCHOR_RETRY_INTERVAL, 0}; + int err; + + log_debug("%s", __func__); + + res = best_resolver(); + + if (res == NULL) { + evtimer_add(&trust_anchor_timer, &tv); + return; + } + + resolver_ref(res); + + if ((err = ub_resolve_event(res->ctx, ".", LDNS_RR_TYPE_DNSKEY, + LDNS_RR_CLASS_IN, res, trust_anchor_resolve_done, + NULL)) != 0) { + log_warn("%s: ub_resolve_async: err: %d, %s", + __func__, err, ub_strerror(err)); + resolver_unref(res); + evtimer_add(&trust_anchor_timer, &tv); + } +} + +void +trust_anchor_timo(int fd, short events, void *arg) +{ + trust_anchor_resolve(); +} + +void +trust_anchor_resolve_done(void *arg, int rcode, void *answer_packet, + int answer_len, int sec, char *why_bogus, int was_ratelimited) +{ + struct unwind_resolver *res; + struct ub_result *result; + sldns_buffer *buf; + struct regional *region; + struct timeval tv = {TRUST_ANCHOR_RETRY_INTERVAL, 0}; + int i, tas, n; + uint16_t dnskey_flags; + char *str, rdata_buf[1024], *ta; + + res = (struct unwind_resolver *)arg; + + if ((result = calloc(1, sizeof(*result))) == NULL) + goto out; + + log_debug("%s: rcode: %d", __func__, rcode); + + if (!sec) { + log_debug("%s: sec: %d", __func__, sec); + goto out; + } + + if ((str = sldns_wire2str_pkt(answer_packet, answer_len)) != NULL) { + log_debug("%s", str); + free(str); + } + + buf = sldns_buffer_new(answer_len); + region = regional_create(); + result->rcode = LDNS_RCODE_SERVFAIL; + if(region && buf) { + sldns_buffer_clear(buf); + sldns_buffer_write(buf, answer_packet, answer_len); + sldns_buffer_flip(buf); + libworker_enter_result(result, buf, region, sec); + result->answer_packet = NULL; + result->answer_len = 0; + sldns_buffer_free(buf); + regional_destroy(region); + + if (result->rcode != LDNS_RCODE_NOERROR) { + log_debug("%s: result->rcode: %d", __func__, + result->rcode); + goto out; + } + + i = 0; + tas = 0; + while(result->data[i] != NULL) { + if (result->len[i] < 2) { + if (tas > 0) + resolver_imsg_compose_frontend( + IMSG_NEW_TAS_ABORT, 0, NULL, 0); + goto out; + } + n = sldns_wire2str_rdata_buf(result->data[i], + result->len[i], rdata_buf, sizeof(rdata_buf), + LDNS_RR_TYPE_DNSKEY); + + if (n < 0 || (size_t)n >= sizeof(rdata_buf)) { + log_warnx("trust anchor buffer to small"); + resolver_imsg_compose_frontend( + IMSG_NEW_TAS_ABORT, 0, NULL, 0); + goto out; + } + + memcpy(&dnskey_flags, result->data[i], 2); + dnskey_flags = ntohs(dnskey_flags); + if ((dnskey_flags & LDNS_KEY_SEP_KEY) && + !(dnskey_flags & LDNS_KEY_REVOKE_KEY)) { + asprintf(&ta, ".\t%d\tIN\tDNSKEY\t%s", + ROOT_DNSKEY_TTL, rdata_buf); + log_debug("%s: ta: %s", __func__, ta); + resolver_imsg_compose_frontend(IMSG_NEW_TA, 0, + ta, strlen(ta) + 1); + tas++; + free(ta); + } + i++; + } + if (tas > 0) { + resolver_imsg_compose_frontend(IMSG_NEW_TAS_DONE, 0, + NULL, 0); + tv.tv_sec = TRUST_ANCHOR_QUERY_INTERVAL; + } + } +out: + ub_resolve_free(result); + resolver_unref(res); + evtimer_add(&trust_anchor_timer, &tv); +} diff --git a/sbin/unwind/unwind.c b/sbin/unwind/unwind.c index 5e829a9c389..2f4ce08119c 100644 --- a/sbin/unwind/unwind.c +++ b/sbin/unwind/unwind.c @@ -1,4 +1,4 @@ -/* $OpenBSD: unwind.c,v 1.10 2019/02/03 12:02:30 florian Exp $ */ +/* $OpenBSD: unwind.c,v 1.11 2019/02/07 17:20:35 florian Exp $ */ /* * Copyright (c) 2018 Florian Obser <florian@openbsd.org> @@ -21,6 +21,7 @@ #include <sys/types.h> #include <sys/queue.h> #include <sys/socket.h> +#include <sys/stat.h> #include <sys/syslog.h> #include <sys/wait.h> @@ -47,6 +48,13 @@ #include "control.h" #include "captiveportal.h" +#define LEASE_DB_DIR "/var/db/" +#define _PATH_LEASE_DB "/var/db/dhclient.leases." + +#define TRUST_ANCHOR_DIR "/etc/unwind/trustanchor/" +#define TRUST_ANCHOR_FILE "/etc/unwind/trustanchor/root.key" +#define TRUST_ANCHOR_TEMPLATE "/etc/unwind/trustanchor/root.key.XXXXXXXXXX" + __dead void usage(void); __dead void main_shutdown(void); @@ -65,6 +73,9 @@ static int main_imsg_send_config(struct unwind_conf *); int main_reload(void); int main_sendall(enum imsg_type, void *, uint16_t); void open_dhcp_lease(int); +void open_trust_anchor(void); +void open_trust_anchor_w(void); +void wrote_trust_anchor(int); void open_ports(void); struct unwind_conf *main_conf; @@ -73,6 +84,9 @@ struct imsgev *iev_resolver; struct imsgev *iev_captiveportal; char *conffile; +char trust_anchor_tmp_filename[sizeof( + TRUST_ANCHOR_TEMPLATE)]; + pid_t frontend_pid; pid_t resolver_pid; pid_t captiveportal_pid; @@ -296,10 +310,18 @@ main(int argc, char *argv[]) main_imsg_compose_frontend_fd(IMSG_ROUTESOCK, 0, frontend_routesock); main_imsg_send_config(main_conf); - if (pledge("stdio inet rpath sendfd", NULL) == -1) + if (unveil(TRUST_ANCHOR_DIR, "rwc") == -1 && errno != ENOENT) + err(1, "unveil"); + + if (unveil(LEASE_DB_DIR, "r") == -1 && errno != ENOENT) + err(1, "unveil"); + + if (pledge("stdio inet dns rpath wpath cpath sendfd", NULL) == -1) fatal("pledge"); main_imsg_compose_frontend(IMSG_STARTUP, 0, NULL, 0); + main_imsg_compose_resolver(IMSG_STARTUP, 0, NULL, 0); + main_imsg_compose_captiveportal(IMSG_STARTUP, 0, NULL, 0); event_dispatch(); @@ -445,6 +467,18 @@ main_dispatch_frontend(int fd, short event, void *bula) memcpy(&rtm_index, imsg.data, sizeof(rtm_index)); open_dhcp_lease(rtm_index); break; + case IMSG_OPEN_TA_RO: + open_trust_anchor(); + break; + case IMSG_OPEN_TA_W: + open_trust_anchor_w(); + break; + case IMSG_TA_W_DONE: + wrote_trust_anchor(0); + break; + case IMSG_TA_W_FAILED: + wrote_trust_anchor(1); + break; case IMSG_OPEN_PORTS: open_ports(); break; @@ -855,9 +889,6 @@ config_clear(struct unwind_conf *conf) free(conf); } - -#define _PATH_LEASE_DB "/var/db/dhclient.leases." - void open_dhcp_lease(int if_idx) { @@ -897,7 +928,6 @@ open_ports(void) hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_PASSIVE; - error = getaddrinfo("127.0.0.1", "domain", &hints, &res0); if (!error && res0) { if ((udp4sock = socket(res0->ai_family, res0->ai_socktype, @@ -909,7 +939,8 @@ open_ports(void) } } } - freeaddrinfo(res0); + if (res0) + freeaddrinfo(res0); hints.ai_family = AF_INET6; error = getaddrinfo("::1", "domain", &hints, &res0); @@ -923,7 +954,8 @@ open_ports(void) } } } - freeaddrinfo(res0); + if (res0) + freeaddrinfo(res0); if (udp4sock == -1 && udp6sock == -1) fatal("could not bind to 127.0.0.1 or ::1 on port 53"); @@ -933,3 +965,57 @@ open_ports(void) if (udp6sock != -1) main_imsg_compose_frontend_fd(IMSG_UDP6SOCK, 0, udp6sock); } + +void +open_trust_anchor(void) +{ + int fd; + + if ((fd = open(TRUST_ANCHOR_FILE, O_RDONLY)) == -1) + log_warn("%s: %s", __func__, TRUST_ANCHOR_FILE); + + /* Send fd == -1, too. Receiver handles it correctly. */ + main_imsg_compose_frontend_fd(IMSG_TAFD, 0, fd); +} + +void +open_trust_anchor_w(void) +{ + int fd; + + if (*trust_anchor_tmp_filename != '\0') { + log_warnx("already writing trust anchor"); + return; + } + strlcpy(trust_anchor_tmp_filename, TRUST_ANCHOR_TEMPLATE, sizeof( + trust_anchor_tmp_filename)); + + if ((fd = mkstemp(trust_anchor_tmp_filename)) == -1) { + log_warn("%s", trust_anchor_tmp_filename); + *trust_anchor_tmp_filename = '\0'; + return; + } + main_imsg_compose_frontend_fd(IMSG_TAFD_W, 0, fd); +} + +void +wrote_trust_anchor(int failure) +{ + if (*trust_anchor_tmp_filename == '\0') { + log_warnx("%s: not writing trust anchor", __func__); + return; + } + + if (failure) + unlink(trust_anchor_tmp_filename); + else { + if (rename(trust_anchor_tmp_filename, TRUST_ANCHOR_FILE) == + -1) { + log_warn("%s", __func__); + unlink(trust_anchor_tmp_filename); + } + } + + *trust_anchor_tmp_filename = '\0'; + +} diff --git a/sbin/unwind/unwind.h b/sbin/unwind/unwind.h index 94d11b45682..df574ee38bc 100644 --- a/sbin/unwind/unwind.h +++ b/sbin/unwind/unwind.h @@ -1,4 +1,4 @@ -/* $OpenBSD: unwind.h,v 1.6 2019/02/03 12:02:30 florian Exp $ */ +/* $OpenBSD: unwind.h,v 1.7 2019/02/07 17:20:35 florian Exp $ */ /* * Copyright (c) 2018 Florian Obser <florian@openbsd.org> @@ -35,6 +35,8 @@ #define OPT_VERBOSE2 0x00000002 #define OPT_NOACTION 0x00000004 +#define KSK2017 ". 172800 IN DNSKEY 257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU=" + enum { PROC_MAIN, PROC_RESOLVER, @@ -94,7 +96,16 @@ enum imsg_type { IMSG_CTL_RECHECK_CAPTIVEPORTAL, IMSG_OPEN_HTTP_PORT, IMSG_HTTPSOCK, - IMSG_CAPTIVEPORTAL_STATE + IMSG_CAPTIVEPORTAL_STATE, + IMSG_OPEN_TA_RO, + IMSG_OPEN_TA_W, + IMSG_TAFD, + IMSG_TAFD_W, + IMSG_NEW_TA, + IMSG_NEW_TAS_ABORT, + IMSG_NEW_TAS_DONE, + IMSG_TA_W_DONE, + IMSG_TA_W_FAILED }; struct unwind_forwarder { |