From be76e7e421d8076b33c4c6b45f408364e6a08a1f Mon Sep 17 00:00:00 2001 From: purplerain Date: Tue, 13 Feb 2024 19:37:16 +0000 Subject: [PATCH] sync with OpenBSD -current --- lib/libcrypto/man/d2i_ASN1_OCTET_STRING.3 | 11 +- lib/libz/zconf.h | 10 +- lib/libz/zlib.h | 6 +- lib/libz/zutil.h | 9 +- sbin/dhcpleased/engine.c | 10 +- sbin/iked/ca.c | 10 +- sbin/iked/config.c | 10 +- sbin/iked/iked.c | 6 +- sbin/unwind/libunbound/services/authzone.c | 3 +- sbin/unwind/libunbound/services/cache/dns.c | 22 + sbin/unwind/libunbound/services/cache/dns.h | 9 + sbin/unwind/libunbound/util/fptr_wlist.c | 1 + sbin/unwind/libunbound/validator/val_nsec.c | 3 +- sbin/unwind/libunbound/validator/val_nsec3.c | 306 ++++++++--- sbin/unwind/libunbound/validator/val_nsec3.h | 60 ++- .../libunbound/validator/val_sigcrypt.c | 37 +- .../libunbound/validator/val_sigcrypt.h | 3 +- sbin/unwind/libunbound/validator/val_utils.c | 22 +- sbin/unwind/libunbound/validator/val_utils.h | 4 +- sbin/unwind/libunbound/validator/validator.c | 489 +++++++++++++++--- sbin/unwind/libunbound/validator/validator.h | 18 + sys/arch/armv7/marvell/mvacc.c | 4 +- sys/dev/fdt/ehci_fdt.c | 74 ++- sys/dev/pci/if_bnxt.c | 6 +- sys/dev/pci/if_em.c | 4 +- sys/dev/pci/if_igc.c | 4 +- sys/dev/pci/if_ix.c | 10 +- sys/dev/pv/if_vio.c | 4 +- sys/kern/kern_clock.c | 9 +- sys/kern/kern_clockintr.c | 10 +- sys/kern/uipc_socket.c | 9 +- sys/kern/uipc_socket2.c | 4 +- sys/lib/libz/zconf.h | 10 +- sys/lib/libz/zlib.h | 6 +- sys/lib/libz/zutil.h | 9 +- sys/net/if_bridge.c | 3 +- sys/net/if_etherip.c | 3 +- sys/net/if_ethersubr.c | 155 +++++- sys/net/if_pfsync.c | 3 +- sys/net/if_veb.c | 3 +- sys/net/route.c | 25 +- sys/net/route.h | 35 +- sys/netinet/if_ether.h | 5 +- sys/netinet/in.h | 4 +- sys/netinet/in_pcb.c | 6 +- sys/netinet/in_pcb.h | 9 +- sys/netinet/ip_carp.c | 3 +- sys/netinet/ip_input.c | 4 +- sys/netinet/ip_output.c | 13 +- sys/netinet/ip_var.h | 3 +- sys/netinet/tcp_input.c | 21 +- sys/netinet/tcp_output.c | 4 +- sys/netinet/tcp_subr.c | 4 +- sys/netinet/tcp_var.h | 13 +- sys/netinet6/dest6.c | 4 +- sys/netinet6/in6.h | 14 +- sys/netinet6/in6_pcb.c | 20 +- sys/netinet6/in6_src.c | 17 +- sys/netinet6/ip6_divert.c | 10 +- sys/netinet6/ip6_forward.c | 8 +- sys/netinet6/ip6_id.c | 3 +- sys/netinet6/ip6_output.c | 24 +- sys/netinet6/ip6_var.h | 10 +- sys/netinet6/mld6.c | 3 +- sys/netinet6/raw_ip6.c | 4 +- sys/netinet6/route6.c | 3 +- sys/netinet6/udp6_output.c | 4 +- sys/sys/socketvar.h | 4 +- sys/uvm/uvm_page.c | 6 +- usr.bin/netstat/inet.c | 10 +- usr.bin/tmux/cmd-paste-buffer.c | 7 +- usr.bin/tmux/cmd-pipe-pane.c | 4 +- usr.bin/tmux/options-table.c | 10 +- usr.bin/tmux/proc.c | 4 +- usr.bin/tmux/server-fn.c | 24 +- usr.bin/tmux/tmux.1 | 20 +- usr.bin/tmux/tmux.h | 3 +- usr.bin/tmux/window.c | 8 +- usr.sbin/bgpd/bgpd.h | 6 +- usr.sbin/bgpd/pftable.c | 9 +- usr.sbin/bgpd/session.c | 11 +- usr.sbin/httpd/server_http.c | 10 +- usr.sbin/unbound/services/authzone.c | 3 +- usr.sbin/unbound/services/cache/dns.c | 22 + usr.sbin/unbound/services/cache/dns.h | 9 + usr.sbin/unbound/testcode/unitverify.c | 7 +- usr.sbin/unbound/util/fptr_wlist.c | 1 + usr.sbin/unbound/validator/val_nsec.c | 3 +- usr.sbin/unbound/validator/val_nsec3.c | 306 ++++++++--- usr.sbin/unbound/validator/val_nsec3.h | 60 ++- usr.sbin/unbound/validator/val_sigcrypt.c | 37 +- usr.sbin/unbound/validator/val_sigcrypt.h | 3 +- usr.sbin/unbound/validator/val_utils.c | 22 +- usr.sbin/unbound/validator/val_utils.h | 4 +- usr.sbin/unbound/validator/validator.c | 487 ++++++++++++++--- usr.sbin/unbound/validator/validator.h | 18 + 96 files changed, 2153 insertions(+), 617 deletions(-) diff --git a/lib/libcrypto/man/d2i_ASN1_OCTET_STRING.3 b/lib/libcrypto/man/d2i_ASN1_OCTET_STRING.3 index 6d79ae403..d544af0fe 100644 --- a/lib/libcrypto/man/d2i_ASN1_OCTET_STRING.3 +++ b/lib/libcrypto/man/d2i_ASN1_OCTET_STRING.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: d2i_ASN1_OCTET_STRING.3,v 1.19 2022/09/12 14:36:09 tb Exp $ +.\" $OpenBSD: d2i_ASN1_OCTET_STRING.3,v 1.20 2024/02/13 12:38:43 job Exp $ .\" .\" Copyright (c) 2017 Ingo Schwarze .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: September 12 2022 $ +.Dd $Mdocdate: February 13 2024 $ .Dt D2I_ASN1_OCTET_STRING 3 .Os .Sh NAME @@ -452,3 +452,10 @@ and .Fn i2d_ASN1_GENERALSTRING first appeared in OpenSSL 0.9.7 and have been available since .Ox 3.2 . +.Sh CAVEATS +Other implementations may accept or emit invalid DER encodings of +GeneralizedTime and UTCTime. +Portable applications should use +.Fn ASN1_STRING_length +to double check whether a given GeneralizedTime or UTCTime object is at least +15 or 13 bytes, respectively. diff --git a/lib/libz/zconf.h b/lib/libz/zconf.h index 30cb9a7b6..0ebaabed7 100644 --- a/lib/libz/zconf.h +++ b/lib/libz/zconf.h @@ -509,12 +509,12 @@ typedef uLong FAR uLongf; #if !defined(_WIN32) && defined(Z_LARGE64) # define z_off64_t off64_t +#elif defined(_WIN32) && !defined(__GNUC__) +# define z_off64_t __int64 +#elif defined(__GO32__) +# define z_off64_t offset_t #else -# if defined(_WIN32) && !defined(__GNUC__) -# define z_off64_t __int64 -# else -# define z_off64_t z_off_t -# endif +# define z_off64_t z_off_t #endif /* MVS linker does not support external names larger than 8 bytes */ diff --git a/lib/libz/zlib.h b/lib/libz/zlib.h index 6fed1b3bf..592d453f5 100644 --- a/lib/libz/zlib.h +++ b/lib/libz/zlib.h @@ -1891,9 +1891,9 @@ ZEXTERN int ZEXPORT gzgetc_(gzFile file); /* backward compatibility */ ZEXTERN z_off_t ZEXPORT gzseek64(gzFile, z_off_t, int); ZEXTERN z_off_t ZEXPORT gztell64(gzFile); ZEXTERN z_off_t ZEXPORT gzoffset64(gzFile); - ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t); - ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t); - ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t); + ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off64_t); # endif #else ZEXTERN gzFile ZEXPORT gzopen(const char *, const char *); diff --git a/lib/libz/zutil.h b/lib/libz/zutil.h index 11b93ebdd..3b186ef9e 100644 --- a/lib/libz/zutil.h +++ b/lib/libz/zutil.h @@ -170,11 +170,10 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ #endif /* provide prototypes for these when building zlib without LFS */ -#if !defined(_WIN32) && \ - (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) - ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t); - ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t); - ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t); +#ifndef Z_LARGE64 + ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off64_t); #endif /* common defaults */ diff --git a/sbin/dhcpleased/engine.c b/sbin/dhcpleased/engine.c index 6d371a5a1..84d02cc12 100644 --- a/sbin/dhcpleased/engine.c +++ b/sbin/dhcpleased/engine.c @@ -1,4 +1,4 @@ -/* $OpenBSD: engine.c,v 1.42 2024/01/26 21:14:08 jan Exp $ */ +/* $OpenBSD: engine.c,v 1.43 2024/02/13 12:53:05 florian Exp $ */ /* * Copyright (c) 2017, 2021 Florian Obser @@ -1385,8 +1385,6 @@ state_transition(struct dhcpleased_iface *iface, enum if_state new_state) char ifnamebuf[IF_NAMESIZE], *if_name; iface->state = new_state; - if (new_state != old_state) - iface->xid = arc4random(); switch (new_state) { case IF_DOWN: @@ -1426,6 +1424,7 @@ state_transition(struct dhcpleased_iface *iface, enum if_state new_state) case IF_DOWN: case IF_IPV6_ONLY: iface->timo.tv_sec = START_EXP_BACKOFF; + iface->xid = arc4random(); break; case IF_BOUND: fatal("invalid transition Bound -> Init"); @@ -1436,8 +1435,10 @@ state_transition(struct dhcpleased_iface *iface, enum if_state new_state) case IF_REBOOTING: if (old_state == IF_REBOOTING) iface->timo.tv_sec *= 2; - else + else { iface->timo.tv_sec = START_EXP_BACKOFF; + iface->xid = arc4random(); + } request_dhcp_request(iface); break; case IF_REQUESTING: @@ -1458,6 +1459,7 @@ state_transition(struct dhcpleased_iface *iface, enum if_state new_state) if (old_state == IF_BOUND) { iface->timo.tv_sec = (iface->rebinding_time - iface->renewal_time) / 2; /* RFC 2131 4.4.5 */ + iface->xid = arc4random(); } else iface->timo.tv_sec /= 2; diff --git a/sbin/iked/ca.c b/sbin/iked/ca.c index 8eb9de537..3c4183440 100644 --- a/sbin/iked/ca.c +++ b/sbin/iked/ca.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ca.c,v 1.100 2024/02/06 13:10:56 tobhe Exp $ */ +/* $OpenBSD: ca.c,v 1.101 2024/02/13 12:25:11 tobhe Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -332,6 +332,14 @@ ca_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) unsigned int mode; switch (imsg->hdr.type) { + case IMSG_CTL_ACTIVE: + case IMSG_CTL_PASSIVE: + /* + * send back to indicate we have processed + * all messages from parent. + */ + proc_compose(&env->sc_ps, PROC_PARENT, imsg->hdr.type, NULL, 0); + break; case IMSG_CTL_RESET: IMSG_SIZE_CHECK(imsg, &mode); memcpy(&mode, imsg->data, sizeof(mode)); diff --git a/sbin/iked/config.c b/sbin/iked/config.c index cc26eeef7..6398e641b 100644 --- a/sbin/iked/config.c +++ b/sbin/iked/config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.95 2024/01/17 08:25:02 claudio Exp $ */ +/* $OpenBSD: config.c,v 1.96 2024/02/13 12:25:11 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider @@ -507,8 +507,14 @@ config_setmode(struct iked *env, unsigned int passive) { unsigned int type; + /* + * In order to control the startup of the processes, + * the messages are sent in this order: + * PROC_PARENT -> PROC_CERT -> PROC_PARENT -> PROC_IKEV2 + * so PROC_CERT is ready before PROC_IKEV2 is activated. + */ type = passive ? IMSG_CTL_PASSIVE : IMSG_CTL_ACTIVE; - proc_compose(&env->sc_ps, PROC_IKEV2, type, NULL, 0); + proc_compose(&env->sc_ps, PROC_CERT, type, NULL, 0); return (0); } diff --git a/sbin/iked/iked.c b/sbin/iked/iked.c index e370424c2..f1e60204d 100644 --- a/sbin/iked/iked.c +++ b/sbin/iked/iked.c @@ -1,4 +1,4 @@ -/* $OpenBSD: iked.c,v 1.67 2024/01/15 15:29:00 tobhe Exp $ */ +/* $OpenBSD: iked.c,v 1.68 2024/02/13 12:25:11 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider @@ -422,6 +422,10 @@ parent_dispatch_ca(int fd, struct privsep_proc *p, struct imsg *imsg) struct iked *env = iked_env; switch (imsg->hdr.type) { + case IMSG_CTL_ACTIVE: + case IMSG_CTL_PASSIVE: + proc_forward_imsg(&env->sc_ps, imsg, PROC_IKEV2, -1); + break; case IMSG_OCSP_FD: ocsp_connect(env, imsg); break; diff --git a/sbin/unwind/libunbound/services/authzone.c b/sbin/unwind/libunbound/services/authzone.c index 61493cc3b..024392d43 100644 --- a/sbin/unwind/libunbound/services/authzone.c +++ b/sbin/unwind/libunbound/services/authzone.c @@ -7771,6 +7771,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z, enum sec_status sec; struct val_env* ve; int m; + int verified = 0; m = modstack_find(mods, "validator"); if(m == -1) { auth_zone_log(z->name, VERB_ALGO, "zonemd dnssec verify: have " @@ -7794,7 +7795,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z, "zonemd: verify %s RRset with DNSKEY", typestr); } sec = dnskeyset_verify_rrset(env, ve, &pk, dnskey, sigalg, why_bogus, NULL, - LDNS_SECTION_ANSWER, NULL); + LDNS_SECTION_ANSWER, NULL, &verified); if(sec == sec_status_secure) { return 1; } diff --git a/sbin/unwind/libunbound/services/cache/dns.c b/sbin/unwind/libunbound/services/cache/dns.c index 58003ebe2..2c45edc08 100644 --- a/sbin/unwind/libunbound/services/cache/dns.c +++ b/sbin/unwind/libunbound/services/cache/dns.c @@ -690,6 +690,28 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, return msg; } +struct dns_msg* +dns_msg_deepcopy_region(struct dns_msg* origin, struct regional* region) +{ + size_t i; + struct dns_msg* res = NULL; + res = gen_dns_msg(region, &origin->qinfo, origin->rep->rrset_count); + if(!res) return NULL; + *res->rep = *origin->rep; + if(origin->rep->reason_bogus_str) { + res->rep->reason_bogus_str = regional_strdup(region, + origin->rep->reason_bogus_str); + } + for(i=0; irep->rrset_count; i++) { + res->rep->rrsets[i] = packed_rrset_copy_region( + origin->rep->rrsets[i], region, 0); + if(!res->rep->rrsets[i]) { + return NULL; + } + } + return res; +} + /** synthesize RRset-only response from cached RRset item */ static struct dns_msg* rrset_msg(struct ub_packed_rrset_key* rrset, struct regional* region, diff --git a/sbin/unwind/libunbound/services/cache/dns.h b/sbin/unwind/libunbound/services/cache/dns.h index 9b39aeb7f..3e0663126 100644 --- a/sbin/unwind/libunbound/services/cache/dns.h +++ b/sbin/unwind/libunbound/services/cache/dns.h @@ -164,6 +164,15 @@ struct dns_msg* tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, struct regional* region, time_t now, int allow_expired, struct regional* scratch); +/** + * Deep copy a dns_msg to a region. + * @param origin: the dns_msg to copy. + * @param region: the region to copy all the data to. + * @return the new dns_msg or NULL on malloc error. + */ +struct dns_msg* dns_msg_deepcopy_region(struct dns_msg* origin, + struct regional* region); + /** * Find cached message * @param env: module environment with the DNS cache. diff --git a/sbin/unwind/libunbound/util/fptr_wlist.c b/sbin/unwind/libunbound/util/fptr_wlist.c index 0ad3f6242..6c94d00fd 100644 --- a/sbin/unwind/libunbound/util/fptr_wlist.c +++ b/sbin/unwind/libunbound/util/fptr_wlist.c @@ -131,6 +131,7 @@ fptr_whitelist_comm_timer(void (*fptr)(void*)) else if(fptr == &pending_udp_timer_delay_cb) return 1; else if(fptr == &worker_stat_timer_cb) return 1; else if(fptr == &worker_probe_timer_cb) return 1; + else if(fptr == &validate_suspend_timer_cb) return 1; #ifdef UB_ON_WINDOWS else if(fptr == &wsvc_cron_cb) return 1; #endif diff --git a/sbin/unwind/libunbound/validator/val_nsec.c b/sbin/unwind/libunbound/validator/val_nsec.c index 9fa760acc..a3aa34ef5 100644 --- a/sbin/unwind/libunbound/validator/val_nsec.c +++ b/sbin/unwind/libunbound/validator/val_nsec.c @@ -181,6 +181,7 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve, { struct packed_rrset_data* d = (struct packed_rrset_data*) nsec->entry.data; + int verified = 0; if(!d) return 0; if(d->security == sec_status_secure) return 1; @@ -188,7 +189,7 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve, if(d->security == sec_status_secure) return 1; d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason, - reason_bogus, LDNS_SECTION_AUTHORITY, qstate); + reason_bogus, LDNS_SECTION_AUTHORITY, qstate, &verified); if(d->security == sec_status_secure) { rrset_update_sec_status(env->rrset_cache, nsec, *env->now); return 1; diff --git a/sbin/unwind/libunbound/validator/val_nsec3.c b/sbin/unwind/libunbound/validator/val_nsec3.c index c5726c3c7..895db19ac 100644 --- a/sbin/unwind/libunbound/validator/val_nsec3.c +++ b/sbin/unwind/libunbound/validator/val_nsec3.c @@ -57,6 +57,19 @@ /* we include nsec.h for the bitmap_has_type function */ #include "validator/val_nsec.h" #include "sldns/sbuffer.h" +#include "util/config_file.h" + +/** + * Max number of NSEC3 calculations at once, suspend query for later. + * 8 is low enough and allows for cases where multiple proofs are needed. + */ +#define MAX_NSEC3_CALCULATIONS 8 +/** + * When all allowed NSEC3 calculations at once resulted in error treat as + * bogus. NSEC3 hash errors are not cached and this helps breaks loops with + * erroneous data. + */ +#define MAX_NSEC3_ERRORS -1 /** * This function we get from ldns-compat or from base system @@ -532,6 +545,17 @@ nsec3_hash_cmp(const void* c1, const void* c2) return memcmp(s1, s2, s1len); } +int +nsec3_cache_table_init(struct nsec3_cache_table* ct, struct regional* region) +{ + if(ct->ct) return 1; + ct->ct = (rbtree_type*)regional_alloc(region, sizeof(*ct->ct)); + if(!ct->ct) return 0; + ct->region = region; + rbtree_init(ct->ct, &nsec3_hash_cmp); + return 1; +} + size_t nsec3_get_hashed(sldns_buffer* buf, uint8_t* nm, size_t nmlen, int algo, size_t iter, uint8_t* salt, size_t saltlen, uint8_t* res, size_t max) @@ -646,7 +670,7 @@ nsec3_hash_name(rbtree_type* table, struct regional* region, sldns_buffer* buf, c = (struct nsec3_cached_hash*)rbtree_search(table, &looki); if(c) { *hash = c; - return 1; + return 2; } /* create a new entry */ c = (struct nsec3_cached_hash*)regional_alloc(region, sizeof(*c)); @@ -658,10 +682,10 @@ nsec3_hash_name(rbtree_type* table, struct regional* region, sldns_buffer* buf, c->dname_len = dname_len; r = nsec3_calc_hash(region, buf, c); if(r != 1) - return r; + return r; /* returns -1 or 0 */ r = nsec3_calc_b32(region, buf, c); if(r != 1) - return r; + return r; /* returns 0 */ #ifdef UNBOUND_DEBUG n = #else @@ -704,6 +728,7 @@ nsec3_hash_matches_owner(struct nsec3_filter* flt, struct nsec3_cached_hash* hash, struct ub_packed_rrset_key* s) { uint8_t* nm = s->rk.dname; + if(!hash) return 0; /* please clang */ /* compare, does hash of name based on params in this NSEC3 * match the owner name of this NSEC3? * name must be: base32 . zone name @@ -730,34 +755,50 @@ nsec3_hash_matches_owner(struct nsec3_filter* flt, * @param nmlen: length of name. * @param rrset: nsec3 that matches is returned here. * @param rr: rr number in nsec3 rrset that matches. + * @param calculations: current hash calculations. * @return true if a matching NSEC3 is found, false if not. */ static int find_matching_nsec3(struct module_env* env, struct nsec3_filter* flt, - rbtree_type* ct, uint8_t* nm, size_t nmlen, - struct ub_packed_rrset_key** rrset, int* rr) + struct nsec3_cache_table* ct, uint8_t* nm, size_t nmlen, + struct ub_packed_rrset_key** rrset, int* rr, + int* calculations) { size_t i_rs; int i_rr; struct ub_packed_rrset_key* s; struct nsec3_cached_hash* hash = NULL; int r; + int calc_errors = 0; /* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */ for(s=filter_first(flt, &i_rs, &i_rr); s; s=filter_next(flt, &i_rs, &i_rr)) { + /* check if we are allowed more calculations */ + if(*calculations >= MAX_NSEC3_CALCULATIONS) { + if(calc_errors == *calculations) { + *calculations = MAX_NSEC3_ERRORS; + } + break; + } /* get name hashed for this NSEC3 RR */ - r = nsec3_hash_name(ct, env->scratch, env->scratch_buffer, + r = nsec3_hash_name(ct->ct, ct->region, env->scratch_buffer, s, i_rr, nm, nmlen, &hash); if(r == 0) { log_err("nsec3: malloc failure"); break; /* alloc failure */ - } else if(r != 1) - continue; /* malformed NSEC3 */ - else if(nsec3_hash_matches_owner(flt, hash, s)) { - *rrset = s; /* rrset with this name */ - *rr = i_rr; /* matches hash with these parameters */ - return 1; + } else if(r < 0) { + /* malformed NSEC3 */ + calc_errors++; + (*calculations)++; + continue; + } else { + if(r == 1) (*calculations)++; + if(nsec3_hash_matches_owner(flt, hash, s)) { + *rrset = s; /* rrset with this name */ + *rr = i_rr; /* matches hash with these parameters */ + return 1; + } } } *rrset = NULL; @@ -775,6 +816,7 @@ nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash, if(!nsec3_get_nextowner(rrset, rr, &next, &nextlen)) return 0; /* malformed RR proves nothing */ + if(!hash) return 0; /* please clang */ /* check the owner name is a hashed value . apex * base32 encoded values must have equal length. * hash_value and next hash value must have equal length. */ @@ -823,35 +865,51 @@ nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash, * @param nmlen: length of name. * @param rrset: covering NSEC3 rrset is returned here. * @param rr: rr of cover is returned here. + * @param calculations: current hash calculations. * @return true if a covering NSEC3 is found, false if not. */ static int find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt, - rbtree_type* ct, uint8_t* nm, size_t nmlen, - struct ub_packed_rrset_key** rrset, int* rr) + struct nsec3_cache_table* ct, uint8_t* nm, size_t nmlen, + struct ub_packed_rrset_key** rrset, int* rr, + int* calculations) { size_t i_rs; int i_rr; struct ub_packed_rrset_key* s; struct nsec3_cached_hash* hash = NULL; int r; + int calc_errors = 0; /* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */ for(s=filter_first(flt, &i_rs, &i_rr); s; s=filter_next(flt, &i_rs, &i_rr)) { + /* check if we are allowed more calculations */ + if(*calculations >= MAX_NSEC3_CALCULATIONS) { + if(calc_errors == *calculations) { + *calculations = MAX_NSEC3_ERRORS; + } + break; + } /* get name hashed for this NSEC3 RR */ - r = nsec3_hash_name(ct, env->scratch, env->scratch_buffer, + r = nsec3_hash_name(ct->ct, ct->region, env->scratch_buffer, s, i_rr, nm, nmlen, &hash); if(r == 0) { log_err("nsec3: malloc failure"); break; /* alloc failure */ - } else if(r != 1) - continue; /* malformed NSEC3 */ - else if(nsec3_covers(flt->zone, hash, s, i_rr, - env->scratch_buffer)) { - *rrset = s; /* rrset with this name */ - *rr = i_rr; /* covers hash with these parameters */ - return 1; + } else if(r < 0) { + /* malformed NSEC3 */ + calc_errors++; + (*calculations)++; + continue; + } else { + if(r == 1) (*calculations)++; + if(nsec3_covers(flt->zone, hash, s, i_rr, + env->scratch_buffer)) { + *rrset = s; /* rrset with this name */ + *rr = i_rr; /* covers hash with these parameters */ + return 1; + } } } *rrset = NULL; @@ -869,11 +927,13 @@ find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt, * @param ct: cached hashes table. * @param qinfo: query that is verified for. * @param ce: closest encloser information is returned in here. + * @param calculations: current hash calculations. * @return true if a closest encloser candidate is found, false if not. */ static int nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt, - rbtree_type* ct, struct query_info* qinfo, struct ce_response* ce) + struct nsec3_cache_table* ct, struct query_info* qinfo, + struct ce_response* ce, int* calculations) { uint8_t* nm = qinfo->qname; size_t nmlen = qinfo->qname_len; @@ -888,8 +948,12 @@ nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt, * may be the case. */ while(dname_subdomain_c(nm, flt->zone)) { + if(*calculations >= MAX_NSEC3_CALCULATIONS || + *calculations == MAX_NSEC3_ERRORS) { + return 0; + } if(find_matching_nsec3(env, flt, ct, nm, nmlen, - &ce->ce_rrset, &ce->ce_rr)) { + &ce->ce_rrset, &ce->ce_rr, calculations)) { ce->ce = nm; ce->ce_len = nmlen; return 1; @@ -933,22 +997,38 @@ next_closer(uint8_t* qname, size_t qnamelen, uint8_t* ce, * If set true, and the return value is true, then you can be * certain that the ce.nc_rrset and ce.nc_rr are set properly. * @param ce: closest encloser information is returned in here. + * @param calculations: pointer to the current NSEC3 hash calculations. * @return bogus if no closest encloser could be proven. * secure if a closest encloser could be proven, ce is set. * insecure if the closest-encloser candidate turns out to prove * that an insecure delegation exists above the qname. + * unchecked if no more hash calculations are allowed at this point. */ static enum sec_status nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt, - rbtree_type* ct, struct query_info* qinfo, int prove_does_not_exist, - struct ce_response* ce) + struct nsec3_cache_table* ct, struct query_info* qinfo, + int prove_does_not_exist, struct ce_response* ce, int* calculations) { uint8_t* nc; size_t nc_len; /* robust: clean out ce, in case it gets abused later */ memset(ce, 0, sizeof(*ce)); - if(!nsec3_find_closest_encloser(env, flt, ct, qinfo, ce)) { + if(!nsec3_find_closest_encloser(env, flt, ct, qinfo, ce, calculations)) { + if(*calculations == MAX_NSEC3_ERRORS) { + verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could " + "not find a candidate for the closest " + "encloser; all attempted hash calculations " + "were erroneous; bogus"); + return sec_status_bogus; + } else if(*calculations >= MAX_NSEC3_CALCULATIONS) { + verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could " + "not find a candidate for the closest " + "encloser; reached MAX_NSEC3_CALCULATIONS " + "(%d); unchecked still", + MAX_NSEC3_CALCULATIONS); + return sec_status_unchecked; + } verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could " "not find a candidate for the closest encloser."); return sec_status_bogus; @@ -989,9 +1069,23 @@ nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt, /* Otherwise, we need to show that the next closer name is covered. */ next_closer(qinfo->qname, qinfo->qname_len, ce->ce, &nc, &nc_len); if(!find_covering_nsec3(env, flt, ct, nc, nc_len, - &ce->nc_rrset, &ce->nc_rr)) { + &ce->nc_rrset, &ce->nc_rr, calculations)) { + if(*calculations == MAX_NSEC3_ERRORS) { + verbose(VERB_ALGO, "nsec3: Could not find proof that the " + "candidate encloser was the closest encloser; " + "all attempted hash calculations were " + "erroneous; bogus"); + return sec_status_bogus; + } else if(*calculations >= MAX_NSEC3_CALCULATIONS) { + verbose(VERB_ALGO, "nsec3: Could not find proof that the " + "candidate encloser was the closest encloser; " + "reached MAX_NSEC3_CALCULATIONS (%d); " + "unchecked still", + MAX_NSEC3_CALCULATIONS); + return sec_status_unchecked; + } verbose(VERB_ALGO, "nsec3: Could not find proof that the " - "candidate encloser was the closest encloser"); + "candidate encloser was the closest encloser"); return sec_status_bogus; } return sec_status_secure; @@ -1020,7 +1114,7 @@ nsec3_ce_wildcard(struct regional* region, uint8_t* ce, size_t celen, /** Do the name error proof */ static enum sec_status nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, - rbtree_type* ct, struct query_info* qinfo) + struct nsec3_cache_table* ct, struct query_info* qinfo, int* calc) { struct ce_response ce; uint8_t* wc; @@ -1032,11 +1126,15 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, /* First locate and prove the closest encloser to qname. We will * use the variant that fails if the closest encloser turns out * to be qname. */ - sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce); + sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce, calc); if(sec != sec_status_secure) { if(sec == sec_status_bogus) verbose(VERB_ALGO, "nsec3 nameerror proof: failed " "to prove a closest encloser"); + else if(sec == sec_status_unchecked) + verbose(VERB_ALGO, "nsec3 nameerror proof: will " + "continue proving closest encloser after " + "suspend"); else verbose(VERB_ALGO, "nsec3 nameerror proof: closest " "nsec3 is an insecure delegation"); return sec; @@ -1046,9 +1144,27 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, /* At this point, we know that qname does not exist. Now we need * to prove that the wildcard does not exist. */ log_assert(ce.ce); - wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen); - if(!wc || !find_covering_nsec3(env, flt, ct, wc, wclen, - &wc_rrset, &wc_rr)) { + wc = nsec3_ce_wildcard(ct->region, ce.ce, ce.ce_len, &wclen); + if(!wc) { + verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " + "that the applicable wildcard did not exist."); + return sec_status_bogus; + } + if(!find_covering_nsec3(env, flt, ct, wc, wclen, &wc_rrset, &wc_rr, calc)) { + if(*calc == MAX_NSEC3_ERRORS) { + verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " + "that the applicable wildcard did not exist; " + "all attempted hash calculations were " + "erroneous; bogus"); + return sec_status_bogus; + } else if(*calc >= MAX_NSEC3_CALCULATIONS) { + verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " + "that the applicable wildcard did not exist; " + "reached MAX_NSEC3_CALCULATIONS (%d); " + "unchecked still", + MAX_NSEC3_CALCULATIONS); + return sec_status_unchecked; + } verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " "that the applicable wildcard did not exist."); return sec_status_bogus; @@ -1064,14 +1180,13 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, enum sec_status nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey) + struct query_info* qinfo, struct key_entry_key* kkey, + struct nsec3_cache_table* ct, int* calc) { - rbtree_type ct; struct nsec3_filter flt; if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) return sec_status_bogus; /* no valid NSEC3s, bogus */ - rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ filter_init(&flt, list, num, qinfo); /* init RR iterator */ if(!flt.zone) return sec_status_bogus; /* no RRs */ @@ -1079,7 +1194,7 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, return sec_status_insecure; /* iteration count too high */ log_nametypeclass(VERB_ALGO, "start nsec3 nameerror proof, zone", flt.zone, 0, 0); - return nsec3_do_prove_nameerror(env, &flt, &ct, qinfo); + return nsec3_do_prove_nameerror(env, &flt, ct, qinfo, calc); } /* @@ -1090,7 +1205,8 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, /** Do the nodata proof */ static enum sec_status nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, - rbtree_type* ct, struct query_info* qinfo) + struct nsec3_cache_table* ct, struct query_info* qinfo, + int* calc) { struct ce_response ce; uint8_t* wc; @@ -1100,7 +1216,7 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, enum sec_status sec; if(find_matching_nsec3(env, flt, ct, qinfo->qname, qinfo->qname_len, - &rrset, &rr)) { + &rrset, &rr, calc)) { /* cases 1 and 2 */ if(nsec3_has_type(rrset, rr, qinfo->qtype)) { verbose(VERB_ALGO, "proveNodata: Matching NSEC3 " @@ -1144,11 +1260,23 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, } return sec_status_secure; } + if(*calc == MAX_NSEC3_ERRORS) { + verbose(VERB_ALGO, "proveNodata: all attempted hash " + "calculations were erroneous while finding a matching " + "NSEC3, bogus"); + return sec_status_bogus; + } else if(*calc >= MAX_NSEC3_CALCULATIONS) { + verbose(VERB_ALGO, "proveNodata: reached " + "MAX_NSEC3_CALCULATIONS (%d) while finding a " + "matching NSEC3; unchecked still", + MAX_NSEC3_CALCULATIONS); + return sec_status_unchecked; + } /* For cases 3 - 5, we need the proven closest encloser, and it * can't match qname. Although, at this point, we know that it * won't since we just checked that. */ - sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce); + sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce, calc); if(sec == sec_status_bogus) { verbose(VERB_ALGO, "proveNodata: did not match qname, " "nor found a proven closest encloser."); @@ -1157,14 +1285,17 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, verbose(VERB_ALGO, "proveNodata: closest nsec3 is insecure " "delegation."); return sec_status_insecure; + } else if(sec==sec_status_unchecked) { + return sec_status_unchecked; } /* Case 3: removed */ /* Case 4: */ log_assert(ce.ce); - wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen); - if(wc && find_matching_nsec3(env, flt, ct, wc, wclen, &rrset, &rr)) { + wc = nsec3_ce_wildcard(ct->region, ce.ce, ce.ce_len, &wclen); + if(wc && find_matching_nsec3(env, flt, ct, wc, wclen, &rrset, &rr, + calc)) { /* found wildcard */ if(nsec3_has_type(rrset, rr, qinfo->qtype)) { verbose(VERB_ALGO, "nsec3 nodata proof: matching " @@ -1195,6 +1326,18 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, } return sec_status_secure; } + if(*calc == MAX_NSEC3_ERRORS) { + verbose(VERB_ALGO, "nsec3 nodata proof: all attempted hash " + "calculations were erroneous while matching " + "wildcard, bogus"); + return sec_status_bogus; + } else if(*calc >= MAX_NSEC3_CALCULATIONS) { + verbose(VERB_ALGO, "nsec3 nodata proof: reached " + "MAX_NSEC3_CALCULATIONS (%d) while matching " + "wildcard, unchecked still", + MAX_NSEC3_CALCULATIONS); + return sec_status_unchecked; + } /* Case 5: */ /* Due to forwarders, cnames, and other collating effects, we @@ -1223,28 +1366,27 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, enum sec_status nsec3_prove_nodata(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey) + struct query_info* qinfo, struct key_entry_key* kkey, + struct nsec3_cache_table* ct, int* calc) { - rbtree_type ct; struct nsec3_filter flt; if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) return sec_status_bogus; /* no valid NSEC3s, bogus */ - rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ filter_init(&flt, list, num, qinfo); /* init RR iterator */ if(!flt.zone) return sec_status_bogus; /* no RRs */ if(nsec3_iteration_count_high(ve, &flt, kkey)) return sec_status_insecure; /* iteration count too high */ - return nsec3_do_prove_nodata(env, &flt, &ct, qinfo); + return nsec3_do_prove_nodata(env, &flt, ct, qinfo, calc); } enum sec_status nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc) + struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc, + struct nsec3_cache_table* ct, int* calc) { - rbtree_type ct; struct nsec3_filter flt; struct ce_response ce; uint8_t* nc; @@ -1254,7 +1396,6 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) return sec_status_bogus; /* no valid NSEC3s, bogus */ - rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ filter_init(&flt, list, num, qinfo); /* init RR iterator */ if(!flt.zone) return sec_status_bogus; /* no RRs */ @@ -1272,8 +1413,22 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, /* Now we still need to prove that the original data did not exist. * Otherwise, we need to show that the next closer name is covered. */ next_closer(qinfo->qname, qinfo->qname_len, ce.ce, &nc, &nc_len); - if(!find_covering_nsec3(env, &flt, &ct, nc, nc_len, - &ce.nc_rrset, &ce.nc_rr)) { + if(!find_covering_nsec3(env, &flt, ct, nc, nc_len, + &ce.nc_rrset, &ce.nc_rr, calc)) { + if(*calc == MAX_NSEC3_ERRORS) { + verbose(VERB_ALGO, "proveWildcard: did not find a " + "covering NSEC3 that covered the next closer " + "name; all attempted hash calculations were " + "erroneous; bogus"); + return sec_status_bogus; + } else if(*calc >= MAX_NSEC3_CALCULATIONS) { + verbose(VERB_ALGO, "proveWildcard: did not find a " + "covering NSEC3 that covered the next closer " + "name; reached MAX_NSEC3_CALCULATIONS " + "(%d); unchecked still", + MAX_NSEC3_CALCULATIONS); + return sec_status_unchecked; + } verbose(VERB_ALGO, "proveWildcard: did not find a covering " "NSEC3 that covered the next closer name."); return sec_status_bogus; @@ -1294,6 +1449,7 @@ list_is_secure(struct module_env* env, struct val_env* ve, { struct packed_rrset_data* d; size_t i; + int verified = 0; for(i=0; ientry.data; if(list[i]->rk.type != htons(LDNS_RR_TYPE_NSEC3)) @@ -1304,7 +1460,8 @@ list_is_secure(struct module_env* env, struct val_env* ve, if(d->security == sec_status_secure) continue; d->security = val_verify_rrset_entry(env, ve, list[i], kkey, - reason, reason_bogus, LDNS_SECTION_AUTHORITY, qstate); + reason, reason_bogus, LDNS_SECTION_AUTHORITY, qstate, + &verified); if(d->security != sec_status_secure) { verbose(VERB_ALGO, "NSEC3 did not verify"); return 0; @@ -1318,13 +1475,16 @@ enum sec_status nsec3_prove_nods(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, struct query_info* qinfo, struct key_entry_key* kkey, char** reason, - sldns_ede_code* reason_bogus, struct module_qstate* qstate) + sldns_ede_code* reason_bogus, struct module_qstate* qstate, + struct nsec3_cache_table* ct) { - rbtree_type ct; struct nsec3_filter flt; struct ce_response ce; struct ub_packed_rrset_key* rrset; int rr; + int calc = 0; + enum sec_status sec; + log_assert(qinfo->qtype == LDNS_RR_TYPE_DS); if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) { @@ -1335,7 +1495,6 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, *reason = "not all NSEC3 records secure"; return sec_status_bogus; /* not all NSEC3 records secure */ } - rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ filter_init(&flt, list, num, qinfo); /* init RR iterator */ if(!flt.zone) { *reason = "no NSEC3 records"; @@ -1346,8 +1505,8 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, /* Look for a matching NSEC3 to qname -- this is the normal * NODATA case. */ - if(find_matching_nsec3(env, &flt, &ct, qinfo->qname, qinfo->qname_len, - &rrset, &rr)) { + if(find_matching_nsec3(env, &flt, ct, qinfo->qname, qinfo->qname_len, + &rrset, &rr, &calc)) { /* If the matching NSEC3 has the SOA bit set, it is from * the wrong zone (the child instead of the parent). If * it has the DS bit set, then we were lied to. */ @@ -1370,10 +1529,24 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, /* Otherwise, this proves no DS. */ return sec_status_secure; } + if(calc == MAX_NSEC3_ERRORS) { + verbose(VERB_ALGO, "nsec3 provenods: all attempted hash " + "calculations were erroneous while finding a matching " + "NSEC3, bogus"); + return sec_status_bogus; + } else if(calc >= MAX_NSEC3_CALCULATIONS) { + verbose(VERB_ALGO, "nsec3 provenods: reached " + "MAX_NSEC3_CALCULATIONS (%d) while finding a " + "matching NSEC3, unchecked still", + MAX_NSEC3_CALCULATIONS); + return sec_status_unchecked; + } /* Otherwise, we are probably in the opt-out case. */ - if(nsec3_prove_closest_encloser(env, &flt, &ct, qinfo, 1, &ce) - != sec_status_secure) { + sec = nsec3_prove_closest_encloser(env, &flt, ct, qinfo, 1, &ce, &calc); + if(sec == sec_status_unchecked) { + return sec_status_unchecked; + } else if(sec != sec_status_secure) { /* an insecure delegation *above* the qname does not prove * anything about this qname exactly, and bogus is bogus */ verbose(VERB_ALGO, "nsec3 provenods: did not match qname, " @@ -1408,16 +1581,15 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, enum sec_status nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, int* nodata) + struct query_info* qinfo, struct key_entry_key* kkey, int* nodata, + struct nsec3_cache_table* ct, int* calc) { enum sec_status sec, secnx; - rbtree_type ct; struct nsec3_filter flt; *nodata = 0; if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) return sec_status_bogus; /* no valid NSEC3s, bogus */ - rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ filter_init(&flt, list, num, qinfo); /* init RR iterator */ if(!flt.zone) return sec_status_bogus; /* no RRs */ @@ -1427,16 +1599,20 @@ nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, /* try nxdomain and nodata after another, while keeping the * hash cache intact */ - secnx = nsec3_do_prove_nameerror(env, &flt, &ct, qinfo); + secnx = nsec3_do_prove_nameerror(env, &flt, ct, qinfo, calc); if(secnx==sec_status_secure) return sec_status_secure; - sec = nsec3_do_prove_nodata(env, &flt, &ct, qinfo); + else if(secnx == sec_status_unchecked) + return sec_status_unchecked; + sec = nsec3_do_prove_nodata(env, &flt, ct, qinfo, calc); if(sec==sec_status_secure) { *nodata = 1; } else if(sec == sec_status_insecure) { *nodata = 1; } else if(secnx == sec_status_insecure) { sec = sec_status_insecure; + } else if(sec == sec_status_unchecked) { + return sec_status_unchecked; } return sec; } diff --git a/sbin/unwind/libunbound/validator/val_nsec3.h b/sbin/unwind/libunbound/validator/val_nsec3.h index f5c4141bf..6874513d1 100644 --- a/sbin/unwind/libunbound/validator/val_nsec3.h +++ b/sbin/unwind/libunbound/validator/val_nsec3.h @@ -98,6 +98,15 @@ struct sldns_buffer; /** The SHA1 hash algorithm for NSEC3 */ #define NSEC3_HASH_SHA1 0x01 +/** +* Cache table for NSEC3 hashes. +* It keeps a *pointer* to the region its items are allocated. +*/ +struct nsec3_cache_table { + rbtree_type* ct; + struct regional* region; +}; + /** * Determine if the set of NSEC3 records provided with a response prove NAME * ERROR. This means that the NSEC3s prove a) the closest encloser exists, @@ -110,14 +119,18 @@ struct sldns_buffer; * @param num: number of RRsets in the array to examine. * @param qinfo: query that is verified for. * @param kkey: key entry that signed the NSEC3s. + * @param ct: cached hashes table. + * @param calc: current hash calculations. * @return: * sec_status SECURE of the Name Error is proven by the NSEC3 RRs, - * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. + * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored, + * UNCHECKED if no more hash calculations are allowed at this point. */ enum sec_status nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey); + struct query_info* qinfo, struct key_entry_key* kkey, + struct nsec3_cache_table* ct, int* calc); /** * Determine if the NSEC3s provided in a response prove the NOERROR/NODATA @@ -144,15 +157,18 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, * @param num: number of RRsets in the array to examine. * @param qinfo: query that is verified for. * @param kkey: key entry that signed the NSEC3s. + * @param ct: cached hashes table. + * @param calc: current hash calculations. * @return: * sec_status SECURE of the proposition is proven by the NSEC3 RRs, - * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. + * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored, + * UNCHECKED if no more hash calculations are allowed at this point. */ enum sec_status nsec3_prove_nodata(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey); - + struct query_info* qinfo, struct key_entry_key* kkey, + struct nsec3_cache_table* ct, int* calc); /** * Prove that a positive wildcard match was appropriate (no direct match @@ -166,14 +182,18 @@ nsec3_prove_nodata(struct module_env* env, struct val_env* ve, * @param kkey: key entry that signed the NSEC3s. * @param wc: The purported wildcard that matched. This is the wildcard name * as *.wildcard.name., with the *. label already removed. + * @param ct: cached hashes table. + * @param calc: current hash calculations. * @return: * sec_status SECURE of the proposition is proven by the NSEC3 RRs, - * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. + * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored, + * UNCHECKED if no more hash calculations are allowed at this point. */ enum sec_status nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc); + struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc, + struct nsec3_cache_table* ct, int* calc); /** * Prove that a DS response either had no DS, or wasn't a delegation point. @@ -189,17 +209,20 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, * @param reason: string for bogus result. * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param qstate: qstate with region. + * @param ct: cached hashes table. * @return: * sec_status SECURE of the proposition is proven by the NSEC3 RRs, * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. * or if there was no DS in an insecure (i.e., opt-in) way, - * INDETERMINATE if it was clear that this wasn't a delegation point. + * INDETERMINATE if it was clear that this wasn't a delegation point, + * UNCHECKED if no more hash calculations are allowed at this point. */ enum sec_status nsec3_prove_nods(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, struct query_info* qinfo, struct key_entry_key* kkey, char** reason, - sldns_ede_code* reason_bogus, struct module_qstate* qstate); + sldns_ede_code* reason_bogus, struct module_qstate* qstate, + struct nsec3_cache_table* ct); /** * Prove NXDOMAIN or NODATA. @@ -212,14 +235,18 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, * @param kkey: key entry that signed the NSEC3s. * @param nodata: if return value is secure, this indicates if nodata or * nxdomain was proven. + * @param ct: cached hashes table. + * @param calc: current hash calculations. * @return: * sec_status SECURE of the proposition is proven by the NSEC3 RRs, - * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. + * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored, + * UNCHECKED if no more hash calculations are allowed at this point. */ enum sec_status nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, int* nodata); + struct query_info* qinfo, struct key_entry_key* kkey, int* nodata, + struct nsec3_cache_table* ct, int* calc); /** * The NSEC3 hash result storage. @@ -256,6 +283,14 @@ struct nsec3_cached_hash { */ int nsec3_hash_cmp(const void* c1, const void* c2); +/** + * Initialise the NSEC3 cache table. + * @param ct: the nsec3 cache table. + * @param region: the region where allocations for the table will happen. + * @return true on success, false on malloc error. + */ +int nsec3_cache_table_init(struct nsec3_cache_table* ct, struct regional* region); + /** * Obtain the hash of an owner name. * Used internally by the nsec3 proof functions in this file. @@ -272,7 +307,8 @@ int nsec3_hash_cmp(const void* c1, const void* c2); * @param dname_len: the length of the name. * @param hash: the hash node is returned on success. * @return: - * 1 on success, either from cache or newly hashed hash is returned. + * 2 on success, hash from cache is returned. + * 1 on success, newly computed hash is returned. * 0 on a malloc failure. * -1 if the NSEC3 rr was badly formatted (i.e. formerr). */ diff --git a/sbin/unwind/libunbound/validator/val_sigcrypt.c b/sbin/unwind/libunbound/validator/val_sigcrypt.c index 596a315db..66b667d79 100644 --- a/sbin/unwind/libunbound/validator/val_sigcrypt.c +++ b/sbin/unwind/libunbound/validator/val_sigcrypt.c @@ -79,6 +79,9 @@ #include #endif +/** Maximum number of RRSIG validations for an RRset. */ +#define MAX_VALIDATE_RRSIGS 8 + /** return number of rrs in an rrset */ static size_t rrset_get_count(struct ub_packed_rrset_key* rrset) @@ -542,6 +545,8 @@ int algo_needs_missing(struct algo_needs* n) * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param section: section of packet where this rrset comes from. * @param qstate: qstate with region. + * @param numverified: incremented when the number of RRSIG validations + * increases. * @return secure if any key signs *this* signature. bogus if no key signs it, * unchecked on error, or indeterminate if all keys are not supported by * the crypto library (openssl3+ only). @@ -552,7 +557,8 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey, size_t sig_idx, struct rbtree_type** sortree, char** reason, sldns_ede_code *reason_bogus, - sldns_pkt_section section, struct module_qstate* qstate) + sldns_pkt_section section, struct module_qstate* qstate, + int* numverified) { /* find matching keys and check them */ enum sec_status sec = sec_status_bogus; @@ -576,6 +582,7 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, tag != dnskey_calc_keytag(dnskey, i)) continue; numchecked ++; + (*numverified)++; /* see if key verifies */ sec = dnskey_verify_rrset_sig(env->scratch, @@ -586,6 +593,13 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, return sec; else if(sec == sec_status_indeterminate) numindeterminate ++; + if(*numverified > MAX_VALIDATE_RRSIGS) { + *reason = "too many RRSIG validations"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; + verbose(VERB_ALGO, "verify sig: too many RRSIG validations"); + return sec_status_bogus; + } } if(numchecked == 0) { *reason = "signatures from unknown keys"; @@ -609,7 +623,7 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus, - sldns_pkt_section section, struct module_qstate* qstate) + sldns_pkt_section section, struct module_qstate* qstate, int* verified) { enum sec_status sec; size_t i, num; @@ -617,6 +631,7 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, /* make sure that for all DNSKEY algorithms there are valid sigs */ struct algo_needs needs; int alg; + *verified = 0; num = rrset_get_sigcount(rrset); if(num == 0) { @@ -641,7 +656,7 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, for(i=0; inow, rrset, dnskey, i, &sortree, reason, reason_bogus, - section, qstate); + section, qstate, verified); /* see which algorithm has been fixed up */ if(sec == sec_status_secure) { if(!sigalg) @@ -653,6 +668,13 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, algo_needs_set_bogus(&needs, (uint8_t)rrset_get_sig_algo(rrset, i)); } + if(*verified > MAX_VALIDATE_RRSIGS) { + verbose(VERB_QUERY, "rrset failed to verify, too many RRSIG validations"); + *reason = "too many RRSIG validations"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; + return sec_status_bogus; + } } if(sigalg && (alg=algo_needs_missing(&needs)) != 0) { verbose(VERB_ALGO, "rrset failed to verify: " @@ -691,6 +713,7 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve, int buf_canon = 0; uint16_t tag = dnskey_calc_keytag(dnskey, dnskey_idx); int algo = dnskey_get_algo(dnskey, dnskey_idx); + int numverified = 0; num = rrset_get_sigcount(rrset); if(num == 0) { @@ -714,8 +737,16 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve, if(sec == sec_status_secure) return sec; numchecked ++; + numverified ++; if(sec == sec_status_indeterminate) numindeterminate ++; + if(numverified > MAX_VALIDATE_RRSIGS) { + verbose(VERB_QUERY, "rrset failed to verify, too many RRSIG validations"); + *reason = "too many RRSIG validations"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; + return sec_status_bogus; + } } verbose(VERB_ALGO, "rrset failed to verify: all signatures are bogus"); if(!numchecked) { diff --git a/sbin/unwind/libunbound/validator/val_sigcrypt.h b/sbin/unwind/libunbound/validator/val_sigcrypt.h index 9baec52c0..06dea97e0 100644 --- a/sbin/unwind/libunbound/validator/val_sigcrypt.h +++ b/sbin/unwind/libunbound/validator/val_sigcrypt.h @@ -260,6 +260,7 @@ uint16_t dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx); * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param section: section of packet where this rrset comes from. * @param qstate: qstate with region. + * @param verified: if not NULL the number of RRSIG validations is returned. * @return SECURE if one key in the set verifies one rrsig. * UNCHECKED on allocation errors, unsupported algorithms, malformed data, * and BOGUS on verification failures (no keys match any signatures). @@ -268,7 +269,7 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus, - sldns_pkt_section section, struct module_qstate* qstate); + sldns_pkt_section section, struct module_qstate* qstate, int* verified); /** diff --git a/sbin/unwind/libunbound/validator/val_utils.c b/sbin/unwind/libunbound/validator/val_utils.c index ba7a66a22..c2b2676c4 100644 --- a/sbin/unwind/libunbound/validator/val_utils.c +++ b/sbin/unwind/libunbound/validator/val_utils.c @@ -58,6 +58,10 @@ #include "sldns/wire2str.h" #include "sldns/parseutil.h" +/** Maximum allowed digest match failures per DS, for DNSKEYs with the same + * properties */ +#define MAX_DS_MATCH_FAILURES 4 + enum val_classification val_classify_response(uint16_t query_flags, struct query_info* origqinf, struct query_info* qinf, struct reply_info* rep, size_t skip) @@ -336,7 +340,8 @@ static enum sec_status val_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys, uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus, - sldns_pkt_section section, struct module_qstate* qstate) + sldns_pkt_section section, struct module_qstate* qstate, + int *verified) { enum sec_status sec; struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> @@ -346,6 +351,7 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, log_nametypeclass(VERB_ALGO, "verify rrset cached", rrset->rk.dname, ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); + *verified = 0; return d->security; } /* check in the cache if verification has already been done */ @@ -354,12 +360,13 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, log_nametypeclass(VERB_ALGO, "verify rrset from cache", rrset->rk.dname, ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); + *verified = 0; return d->security; } log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname, ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason, - reason_bogus, section, qstate); + reason_bogus, section, qstate, verified); verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec)); regional_free_all(env->scratch); @@ -393,7 +400,8 @@ enum sec_status val_verify_rrset_entry(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus, - sldns_pkt_section section, struct module_qstate* qstate) + sldns_pkt_section section, struct module_qstate* qstate, + int* verified) { /* temporary dnskey rrset-key */ struct ub_packed_rrset_key dnskey; @@ -407,7 +415,7 @@ val_verify_rrset_entry(struct module_env* env, struct val_env* ve, dnskey.entry.key = &dnskey; dnskey.entry.data = kd->rrset_data; sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason, - reason_bogus, section, qstate); + reason_bogus, section, qstate, verified); return sec; } @@ -439,6 +447,12 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve, if(!ds_digest_match_dnskey(env, dnskey_rrset, i, ds_rrset, ds_idx)) { verbose(VERB_ALGO, "DS match attempt failed"); + if(numchecked > numhashok + MAX_DS_MATCH_FAILURES) { + verbose(VERB_ALGO, "DS match attempt reached " + "MAX_DS_MATCH_FAILURES (%d); bogus", + MAX_DS_MATCH_FAILURES); + return sec_status_bogus; + } continue; } numhashok++; diff --git a/sbin/unwind/libunbound/validator/val_utils.h b/sbin/unwind/libunbound/validator/val_utils.h index d6abf1235..e2d5c3f5c 100644 --- a/sbin/unwind/libunbound/validator/val_utils.h +++ b/sbin/unwind/libunbound/validator/val_utils.h @@ -124,12 +124,14 @@ void val_find_signer(enum val_classification subtype, * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param section: section of packet where this rrset comes from. * @param qstate: qstate with region. + * @param verified: if not NULL, the number of RRSIG validations is returned. * @return security status of verification. */ enum sec_status val_verify_rrset_entry(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus, - sldns_pkt_section section, struct module_qstate* qstate); + sldns_pkt_section section, struct module_qstate* qstate, + int* verified); /** * Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but diff --git a/sbin/unwind/libunbound/validator/validator.c b/sbin/unwind/libunbound/validator/validator.c index 004846016..73a44b13f 100644 --- a/sbin/unwind/libunbound/validator/validator.c +++ b/sbin/unwind/libunbound/validator/validator.c @@ -64,10 +64,15 @@ #include "sldns/wire2str.h" #include "sldns/str2wire.h" +/** Max number of RRSIGs to validate at once, suspend query for later. */ +#define MAX_VALIDATE_AT_ONCE 8 +/** Max number of validation suspends allowed, error out otherwise. */ +#define MAX_VALIDATION_SUSPENDS 16 + /* forward decl for cache response and normal super inform calls of a DS */ static void process_ds_response(struct module_qstate* qstate, struct val_qstate* vq, int id, int rcode, struct dns_msg* msg, - struct query_info* qinfo, struct sock_list* origin); + struct query_info* qinfo, struct sock_list* origin, int* suspend); /* Updates the suplied EDE (RFC8914) code selectively so we don't lose @@ -112,7 +117,7 @@ fill_nsec3_iter(struct val_env* ve, char* s, int c) s = e; if(i>0 && ve->nsec3_keysize[i-1] >= ve->nsec3_keysize[i]) { log_err("nsec3 key iterations not ascending: %d %d", - (int)ve->nsec3_keysize[i-1], + (int)ve->nsec3_keysize[i-1], (int)ve->nsec3_keysize[i]); return 0; } @@ -281,6 +286,21 @@ val_new(struct module_qstate* qstate, int id) return val_new_getmsg(qstate, vq); } +/** reset validator query state for query restart */ +static void +val_restart(struct val_qstate* vq) +{ + struct comm_timer* temp_timer; + int restart_count; + if(!vq) return; + temp_timer = vq->suspend_timer; + restart_count = vq->restart_count+1; + memset(vq, 0, sizeof(*vq)); + vq->suspend_timer = temp_timer; + vq->restart_count = restart_count; + vq->state = VAL_INIT_STATE; +} + /** * Exit validation with an error status * @@ -587,30 +607,42 @@ prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq, * completed. * * @param qstate: query state. + * @param vq: validator query state. * @param env: module env for verify. * @param ve: validator env for verify. * @param qchase: query that was made. * @param chase_reply: answer to validate. * @param key_entry: the key entry, which is trusted, and which matches * the signer of the answer. The key entry isgood(). + * @param suspend: returned true if the task takes too long and needs to + * suspend to continue the effort later. * @return false if any of the rrsets in the an or ns sections of the message * fail to verify. The message is then set to bogus. */ static int -validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, - struct val_env* ve, struct query_info* qchase, - struct reply_info* chase_reply, struct key_entry_key* key_entry) +validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq, + struct module_env* env, struct val_env* ve, struct query_info* qchase, + struct reply_info* chase_reply, struct key_entry_key* key_entry, + int* suspend) { uint8_t* sname; size_t i, slen; struct ub_packed_rrset_key* s; enum sec_status sec; - int dname_seen = 0; + int dname_seen = 0, num_verifies = 0, verified, have_state = 0; char* reason = NULL; sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; + *suspend = 0; + if(vq->msg_signatures_state) { + /* Pick up the state, and reset it, may not be needed now. */ + vq->msg_signatures_state = 0; + have_state = 1; + } /* validate the ANSWER section */ for(i=0; ian_numrrsets; i++) { + if(have_state && i <= vq->msg_signatures_index) + continue; s = chase_reply->rrsets[i]; /* Skip the CNAME following a (validated) DNAME. * Because of the normalization routines in the iterator, @@ -629,7 +661,7 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, /* Verify the answer rrset */ sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, - &reason_bogus, LDNS_SECTION_ANSWER, qstate); + &reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified); /* If the (answer) rrset failed to validate, then this * message is BAD. */ if(sec != sec_status_secure) { @@ -654,14 +686,33 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME) { dname_seen = 1; } + num_verifies += verified; + if(num_verifies > MAX_VALIDATE_AT_ONCE && + i+1 < (env->cfg->val_clean_additional? + chase_reply->an_numrrsets+chase_reply->ns_numrrsets: + chase_reply->rrset_count)) { + /* If the number of RRSIGs exceeds the maximum in + * one go, suspend. Only suspend if there is a next + * rrset to verify, i+1msg_signatures_state = 1; + vq->msg_signatures_index = i; + verbose(VERB_ALGO, "msg signature validation " + "suspended"); + return 0; + } } /* validate the AUTHORITY section */ for(i=chase_reply->an_numrrsets; ian_numrrsets+ chase_reply->ns_numrrsets; i++) { + if(have_state && i <= vq->msg_signatures_index) + continue; s = chase_reply->rrsets[i]; sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, - &reason_bogus, LDNS_SECTION_AUTHORITY, qstate); + &reason_bogus, LDNS_SECTION_AUTHORITY, qstate, + &verified); /* If anything in the authority section fails to be secure, * we have a bad message. */ if(sec != sec_status_secure) { @@ -675,6 +726,18 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, update_reason_bogus(chase_reply, reason_bogus); return 0; } + num_verifies += verified; + if(num_verifies > MAX_VALIDATE_AT_ONCE && + i+1 < (env->cfg->val_clean_additional? + chase_reply->an_numrrsets+chase_reply->ns_numrrsets: + chase_reply->rrset_count)) { + *suspend = 1; + vq->msg_signatures_state = 1; + vq->msg_signatures_index = i; + verbose(VERB_ALGO, "msg signature validation " + "suspended"); + return 0; + } } /* If set, the validator should clean the additional section of @@ -684,22 +747,103 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, /* attempt to validate the ADDITIONAL section rrsets */ for(i=chase_reply->an_numrrsets+chase_reply->ns_numrrsets; irrset_count; i++) { + if(have_state && i <= vq->msg_signatures_index) + continue; s = chase_reply->rrsets[i]; /* only validate rrs that have signatures with the key */ /* leave others unchecked, those get removed later on too */ val_find_rrset_signer(s, &sname, &slen); + verified = 0; if(sname && query_dname_compare(sname, key_entry->name)==0) (void)val_verify_rrset_entry(env, ve, s, key_entry, - &reason, NULL, LDNS_SECTION_ADDITIONAL, qstate); + &reason, NULL, LDNS_SECTION_ADDITIONAL, qstate, + &verified); /* the additional section can fail to be secure, * it is optional, check signature in case we need * to clean the additional section later. */ + num_verifies += verified; + if(num_verifies > MAX_VALIDATE_AT_ONCE && + i+1 < chase_reply->rrset_count) { + *suspend = 1; + vq->msg_signatures_state = 1; + vq->msg_signatures_index = i; + verbose(VERB_ALGO, "msg signature validation " + "suspended"); + return 0; + } } return 1; } +void +validate_suspend_timer_cb(void* arg) +{ + struct module_qstate* qstate = (struct module_qstate*)arg; + verbose(VERB_ALGO, "validate_suspend timer, continue"); + mesh_run(qstate->env->mesh, qstate->mesh_info, module_event_pass, + NULL); +} + +/** Setup timer to continue validation of msg signatures later */ +static int +validate_suspend_setup_timer(struct module_qstate* qstate, + struct val_qstate* vq, int id, enum val_state resume_state) +{ + struct timeval tv; + int usec, slack, base; + if(vq->suspend_count >= MAX_VALIDATION_SUSPENDS) { + verbose(VERB_ALGO, "validate_suspend timer: " + "reached MAX_VALIDATION_SUSPENDS (%d); error out", + MAX_VALIDATION_SUSPENDS); + errinf(qstate, "max validation suspends reached, " + "too many RRSIG validations"); + return 0; + } + verbose(VERB_ALGO, "validate_suspend timer, set for suspend"); + vq->state = resume_state; + qstate->ext_state[id] = module_wait_reply; + if(!vq->suspend_timer) { + vq->suspend_timer = comm_timer_create( + qstate->env->worker_base, + validate_suspend_timer_cb, qstate); + if(!vq->suspend_timer) { + log_err("validate_suspend_setup_timer: " + "out of memory for comm_timer_create"); + return 0; + } + } + /* The timer is activated later, after other events in the event + * loop have been processed. The query state can also be deleted, + * when the list is full and query states are dropped. */ + /* Extend wait time if there are a lot of queries or if this one + * is taking long, to keep around cpu time for ordinary queries. */ + usec = 50000; /* 50 msec */ + slack = 0; + if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states) + slack += 3; + else if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states/2) + slack += 2; + else if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states/4) + slack += 1; + if(vq->suspend_count > 3) + slack += 3; + else if(vq->suspend_count > 0) + slack += vq->suspend_count; + if(slack != 0 && slack <= 12 /* No numeric overflow. */) { + usec = usec << slack; + } + /* Spread such timeouts within 90%-100% of the original timer. */ + base = usec * 9/10; + usec = base + ub_random_max(qstate->env->rnd, usec-base); + tv.tv_usec = (usec % 1000000); + tv.tv_sec = (usec / 1000000); + vq->suspend_count ++; + comm_timer_set(vq->suspend_timer, &tv); + return 1; +} + /** * Detect wrong truncated response (say from BIND 9.6.1 that is forwarding * and saw the NS record without signatures from a referral). @@ -798,11 +942,17 @@ remove_spurious_authority(struct reply_info* chase_reply, * @param chase_reply: answer to that query to validate. * @param kkey: the key entry, which is trusted, and which matches * the signer of the answer. The key entry isgood(). + * @param qstate: query state for the region. + * @param vq: validator state for the nsec3 cache table. + * @param nsec3_calculations: current nsec3 hash calculations. + * @param suspend: returned true if the task takes too long and needs to + * suspend to continue the effort later. */ static void validate_positive_response(struct module_env* env, struct val_env* ve, struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* kkey) + struct key_entry_key* kkey, struct module_qstate* qstate, + struct val_qstate* vq, int* nsec3_calculations, int* suspend) { uint8_t* wc = NULL; size_t wl; @@ -811,6 +961,7 @@ validate_positive_response(struct module_env* env, struct val_env* ve, int nsec3s_seen = 0; size_t i; struct ub_packed_rrset_key* s; + *suspend = 0; /* validate the ANSWER section - this will be the answer itself */ for(i=0; ian_numrrsets; i++) { @@ -862,17 +1013,23 @@ validate_positive_response(struct module_env* env, struct val_env* ve, /* If this was a positive wildcard response that we haven't already * proven, and we have NSEC3 records, try to prove it using the NSEC3 * records. */ - if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) { + if(wc != NULL && !wc_NSEC_ok && nsec3s_seen && + nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { enum sec_status sec = nsec3_prove_wildcard(env, ve, chase_reply->rrsets+chase_reply->an_numrrsets, - chase_reply->ns_numrrsets, qchase, kkey, wc); + chase_reply->ns_numrrsets, qchase, kkey, wc, + &vq->nsec3_cache_table, nsec3_calculations); if(sec == sec_status_insecure) { verbose(VERB_ALGO, "Positive wildcard response is " "insecure"); chase_reply->security = sec_status_insecure; return; - } else if(sec == sec_status_secure) + } else if(sec == sec_status_secure) { wc_NSEC_ok = 1; + } else if(sec == sec_status_unchecked) { + *suspend = 1; + return; + } } /* If after all this, we still haven't proven the positive wildcard @@ -904,11 +1061,17 @@ validate_positive_response(struct module_env* env, struct val_env* ve, * @param chase_reply: answer to that query to validate. * @param kkey: the key entry, which is trusted, and which matches * the signer of the answer. The key entry isgood(). + * @param qstate: query state for the region. + * @param vq: validator state for the nsec3 cache table. + * @param nsec3_calculations: current nsec3 hash calculations. + * @param suspend: returned true if the task takes too long and needs to + * suspend to continue the effort later. */ static void validate_nodata_response(struct module_env* env, struct val_env* ve, struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* kkey) + struct key_entry_key* kkey, struct module_qstate* qstate, + struct val_qstate* vq, int* nsec3_calculations, int* suspend) { /* Since we are here, there must be nothing in the ANSWER section to * validate. */ @@ -925,6 +1088,7 @@ validate_nodata_response(struct module_env* env, struct val_env* ve, int nsec3s_seen = 0; /* nsec3s seen */ struct ub_packed_rrset_key* s; size_t i; + *suspend = 0; for(i=chase_reply->an_numrrsets; ian_numrrsets+ chase_reply->ns_numrrsets; i++) { @@ -963,16 +1127,23 @@ validate_nodata_response(struct module_env* env, struct val_env* ve, } } - if(!has_valid_nsec && nsec3s_seen) { + if(!has_valid_nsec && nsec3s_seen && + nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { enum sec_status sec = nsec3_prove_nodata(env, ve, chase_reply->rrsets+chase_reply->an_numrrsets, - chase_reply->ns_numrrsets, qchase, kkey); + chase_reply->ns_numrrsets, qchase, kkey, + &vq->nsec3_cache_table, nsec3_calculations); if(sec == sec_status_insecure) { verbose(VERB_ALGO, "NODATA response is insecure"); chase_reply->security = sec_status_insecure; return; - } else if(sec == sec_status_secure) + } else if(sec == sec_status_secure) { has_valid_nsec = 1; + } else if(sec == sec_status_unchecked) { + /* check is incomplete; suspend */ + *suspend = 1; + return; + } } if(!has_valid_nsec) { @@ -1004,11 +1175,18 @@ validate_nodata_response(struct module_env* env, struct val_env* ve, * @param kkey: the key entry, which is trusted, and which matches * the signer of the answer. The key entry isgood(). * @param rcode: adjusted RCODE, in case of RCODE/proof mismatch leniency. + * @param qstate: query state for the region. + * @param vq: validator state for the nsec3 cache table. + * @param nsec3_calculations: current nsec3 hash calculations. + * @param suspend: returned true if the task takes too long and needs to + * suspend to continue the effort later. */ static void validate_nameerror_response(struct module_env* env, struct val_env* ve, struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* kkey, int* rcode) + struct key_entry_key* kkey, int* rcode, + struct module_qstate* qstate, struct val_qstate* vq, + int* nsec3_calculations, int* suspend) { int has_valid_nsec = 0; int has_valid_wnsec = 0; @@ -1018,6 +1196,7 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve, uint8_t* ce; int ce_labs = 0; int prev_ce_labs = 0; + *suspend = 0; for(i=chase_reply->an_numrrsets; ian_numrrsets+ chase_reply->ns_numrrsets; i++) { @@ -1047,13 +1226,18 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve, nsec3s_seen = 1; } - if((!has_valid_nsec || !has_valid_wnsec) && nsec3s_seen) { + if((!has_valid_nsec || !has_valid_wnsec) && nsec3s_seen && + nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { /* use NSEC3 proof, both answer and auth rrsets, in case * NSEC3s end up in the answer (due to qtype=NSEC3 or so) */ chase_reply->security = nsec3_prove_nameerror(env, ve, chase_reply->rrsets, chase_reply->an_numrrsets+ - chase_reply->ns_numrrsets, qchase, kkey); - if(chase_reply->security != sec_status_secure) { + chase_reply->ns_numrrsets, qchase, kkey, + &vq->nsec3_cache_table, nsec3_calculations); + if(chase_reply->security == sec_status_unchecked) { + *suspend = 1; + return; + } else if(chase_reply->security != sec_status_secure) { verbose(VERB_QUERY, "NameError response failed nsec, " "nsec3 proof was %s", sec_status_to_string( chase_reply->security)); @@ -1065,26 +1249,34 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve, /* If the message fails to prove either condition, it is bogus. */ if(!has_valid_nsec) { + validate_nodata_response(env, ve, qchase, chase_reply, kkey, + qstate, vq, nsec3_calculations, suspend); + if(*suspend) return; verbose(VERB_QUERY, "NameError response has failed to prove: " "qname does not exist"); - chase_reply->security = sec_status_bogus; - update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); /* Be lenient with RCODE in NSEC NameError responses */ - validate_nodata_response(env, ve, qchase, chase_reply, kkey); - if (chase_reply->security == sec_status_secure) + if(chase_reply->security == sec_status_secure) { *rcode = LDNS_RCODE_NOERROR; + } else { + chase_reply->security = sec_status_bogus; + update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); + } return; } if(!has_valid_wnsec) { + validate_nodata_response(env, ve, qchase, chase_reply, kkey, + qstate, vq, nsec3_calculations, suspend); + if(*suspend) return; verbose(VERB_QUERY, "NameError response has failed to prove: " "covering wildcard does not exist"); - chase_reply->security = sec_status_bogus; - update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); /* Be lenient with RCODE in NSEC NameError responses */ - validate_nodata_response(env, ve, qchase, chase_reply, kkey); - if (chase_reply->security == sec_status_secure) + if (chase_reply->security == sec_status_secure) { *rcode = LDNS_RCODE_NOERROR; + } else { + chase_reply->security = sec_status_bogus; + update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); + } return; } @@ -1144,11 +1336,17 @@ validate_referral_response(struct reply_info* chase_reply) * @param chase_reply: answer to that query to validate. * @param kkey: the key entry, which is trusted, and which matches * the signer of the answer. The key entry isgood(). + * @param qstate: query state for the region. + * @param vq: validator state for the nsec3 cache table. + * @param nsec3_calculations: current nsec3 hash calculations. + * @param suspend: returned true if the task takes too long and needs to + * suspend to continue the effort later. */ static void validate_any_response(struct module_env* env, struct val_env* ve, struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* kkey) + struct key_entry_key* kkey, struct module_qstate* qstate, + struct val_qstate* vq, int* nsec3_calculations, int* suspend) { /* all answer and auth rrsets already verified */ /* but check if a wildcard response is given, then check NSEC/NSEC3 @@ -1159,6 +1357,7 @@ validate_any_response(struct module_env* env, struct val_env* ve, int nsec3s_seen = 0; size_t i; struct ub_packed_rrset_key* s; + *suspend = 0; if(qchase->qtype != LDNS_RR_TYPE_ANY) { log_err("internal error: ANY validation called for non-ANY"); @@ -1213,19 +1412,25 @@ validate_any_response(struct module_env* env, struct val_env* ve, /* If this was a positive wildcard response that we haven't already * proven, and we have NSEC3 records, try to prove it using the NSEC3 * records. */ - if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) { + if(wc != NULL && !wc_NSEC_ok && nsec3s_seen && + nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { /* look both in answer and auth section for NSEC3s */ enum sec_status sec = nsec3_prove_wildcard(env, ve, chase_reply->rrsets, chase_reply->an_numrrsets+chase_reply->ns_numrrsets, - qchase, kkey, wc); + qchase, kkey, wc, &vq->nsec3_cache_table, + nsec3_calculations); if(sec == sec_status_insecure) { verbose(VERB_ALGO, "Positive ANY wildcard response is " "insecure"); chase_reply->security = sec_status_insecure; return; - } else if(sec == sec_status_secure) + } else if(sec == sec_status_secure) { wc_NSEC_ok = 1; + } else if(sec == sec_status_unchecked) { + *suspend = 1; + return; + } } /* If after all this, we still haven't proven the positive wildcard @@ -1258,11 +1463,17 @@ validate_any_response(struct module_env* env, struct val_env* ve, * @param chase_reply: answer to that query to validate. * @param kkey: the key entry, which is trusted, and which matches * the signer of the answer. The key entry isgood(). + * @param qstate: query state for the region. + * @param vq: validator state for the nsec3 cache table. + * @param nsec3_calculations: current nsec3 hash calculations. + * @param suspend: returned true if the task takes too long and needs to + * suspend to continue the effort later. */ static void validate_cname_response(struct module_env* env, struct val_env* ve, struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* kkey) + struct key_entry_key* kkey, struct module_qstate* qstate, + struct val_qstate* vq, int* nsec3_calculations, int* suspend) { uint8_t* wc = NULL; size_t wl; @@ -1270,6 +1481,7 @@ validate_cname_response(struct module_env* env, struct val_env* ve, int nsec3s_seen = 0; size_t i; struct ub_packed_rrset_key* s; + *suspend = 0; /* validate the ANSWER section - this will be the CNAME (+DNAME) */ for(i=0; ian_numrrsets; i++) { @@ -1334,17 +1546,23 @@ validate_cname_response(struct module_env* env, struct val_env* ve, /* If this was a positive wildcard response that we haven't already * proven, and we have NSEC3 records, try to prove it using the NSEC3 * records. */ - if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) { + if(wc != NULL && !wc_NSEC_ok && nsec3s_seen && + nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { enum sec_status sec = nsec3_prove_wildcard(env, ve, chase_reply->rrsets+chase_reply->an_numrrsets, - chase_reply->ns_numrrsets, qchase, kkey, wc); + chase_reply->ns_numrrsets, qchase, kkey, wc, + &vq->nsec3_cache_table, nsec3_calculations); if(sec == sec_status_insecure) { verbose(VERB_ALGO, "wildcard CNAME response is " "insecure"); chase_reply->security = sec_status_insecure; return; - } else if(sec == sec_status_secure) + } else if(sec == sec_status_secure) { wc_NSEC_ok = 1; + } else if(sec == sec_status_unchecked) { + *suspend = 1; + return; + } } /* If after all this, we still haven't proven the positive wildcard @@ -1375,11 +1593,17 @@ validate_cname_response(struct module_env* env, struct val_env* ve, * @param chase_reply: answer to that query to validate. * @param kkey: the key entry, which is trusted, and which matches * the signer of the answer. The key entry isgood(). + * @param qstate: query state for the region. + * @param vq: validator state for the nsec3 cache table. + * @param nsec3_calculations: current nsec3 hash calculations. + * @param suspend: returned true if the task takes too long and needs to + * suspend to continue the effort later. */ static void validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* kkey) + struct key_entry_key* kkey, struct module_qstate* qstate, + struct val_qstate* vq, int* nsec3_calculations, int* suspend) { int nodata_valid_nsec = 0; /* If true, then NODATA has been proven.*/ uint8_t* ce = NULL; /* for wildcard nodata responses. This is the @@ -1393,6 +1617,7 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, uint8_t* nsec_ce; /* Used to find the NSEC with the longest ce */ int ce_labs = 0; int prev_ce_labs = 0; + *suspend = 0; /* the AUTHORITY section */ for(i=chase_reply->an_numrrsets; ian_numrrsets+ @@ -1458,11 +1683,13 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); return; } - if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen) { + if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen && + nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { int nodata; enum sec_status sec = nsec3_prove_nxornodata(env, ve, chase_reply->rrsets+chase_reply->an_numrrsets, - chase_reply->ns_numrrsets, qchase, kkey, &nodata); + chase_reply->ns_numrrsets, qchase, kkey, &nodata, + &vq->nsec3_cache_table, nsec3_calculations); if(sec == sec_status_insecure) { verbose(VERB_ALGO, "CNAMEchain to noanswer response " "is insecure"); @@ -1472,6 +1699,9 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, if(nodata) nodata_valid_nsec = 1; else nxdomain_valid_nsec = 1; + } else if(sec == sec_status_unchecked) { + *suspend = 1; + return; } } @@ -1815,13 +2045,37 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id) * Uses negative cache for NSEC3 lookup of DS responses. */ /* only if cache not blacklisted, of course */ struct dns_msg* msg; - if(!qstate->blacklist && !vq->chain_blacklist && + int suspend; + if(vq->sub_ds_msg) { + /* We have a suspended DS reply from a sub-query; + * process it. */ + verbose(VERB_ALGO, "Process suspended sub DS response"); + msg = vq->sub_ds_msg; + process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR, + msg, &msg->qinfo, NULL, &suspend); + if(suspend) { + /* we'll come back here later to continue */ + if(!validate_suspend_setup_timer(qstate, vq, + id, VAL_FINDKEY_STATE)) + return val_error(qstate, id); + return 0; + } + vq->sub_ds_msg = NULL; + return 1; /* continue processing ds-response results */ + } else if(!qstate->blacklist && !vq->chain_blacklist && (msg=val_find_DS(qstate->env, target_key_name, target_key_len, vq->qchase.qclass, qstate->region, vq->key_entry->name)) ) { verbose(VERB_ALGO, "Process cached DS response"); process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR, - msg, &msg->qinfo, NULL); + msg, &msg->qinfo, NULL, &suspend); + if(suspend) { + /* we'll come back here later to continue */ + if(!validate_suspend_setup_timer(qstate, vq, + id, VAL_FINDKEY_STATE)) + return val_error(qstate, id); + return 0; + } return 1; /* continue processing ds-response results */ } if(!generate_request(qstate, id, target_key_name, @@ -1864,7 +2118,7 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, struct val_env* ve, int id) { enum val_classification subtype; - int rcode; + int rcode, suspend, nsec3_calculations = 0; if(!vq->key_entry) { verbose(VERB_ALGO, "validate: no key entry, failed"); @@ -1921,8 +2175,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, /* check signatures in the message; * answer and authority must be valid, additional is only checked. */ - if(!validate_msg_signatures(qstate, qstate->env, ve, &vq->qchase, - vq->chase_reply, vq->key_entry)) { + if(!validate_msg_signatures(qstate, vq, qstate->env, ve, &vq->qchase, + vq->chase_reply, vq->key_entry, &suspend)) { + if(suspend) { + if(!validate_suspend_setup_timer(qstate, vq, + id, VAL_VALIDATE_STATE)) + return val_error(qstate, id); + return 0; + } /* workaround bad recursor out there that truncates (even * with EDNS4k) to 512 by removing RRSIG from auth section * for positive replies*/ @@ -1951,7 +2211,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, case VAL_CLASS_POSITIVE: verbose(VERB_ALGO, "Validating a positive response"); validate_positive_response(qstate->env, ve, - &vq->qchase, vq->chase_reply, vq->key_entry); + &vq->qchase, vq->chase_reply, vq->key_entry, + qstate, vq, &nsec3_calculations, &suspend); + if(suspend) { + if(!validate_suspend_setup_timer(qstate, + vq, id, VAL_VALIDATE_STATE)) + return val_error(qstate, id); + return 0; + } verbose(VERB_DETAIL, "validate(positive): %s", sec_status_to_string( vq->chase_reply->security)); @@ -1960,7 +2227,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, case VAL_CLASS_NODATA: verbose(VERB_ALGO, "Validating a nodata response"); validate_nodata_response(qstate->env, ve, - &vq->qchase, vq->chase_reply, vq->key_entry); + &vq->qchase, vq->chase_reply, vq->key_entry, + qstate, vq, &nsec3_calculations, &suspend); + if(suspend) { + if(!validate_suspend_setup_timer(qstate, + vq, id, VAL_VALIDATE_STATE)) + return val_error(qstate, id); + return 0; + } verbose(VERB_DETAIL, "validate(nodata): %s", sec_status_to_string( vq->chase_reply->security)); @@ -1970,7 +2244,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, rcode = (int)FLAGS_GET_RCODE(vq->orig_msg->rep->flags); verbose(VERB_ALGO, "Validating a nxdomain response"); validate_nameerror_response(qstate->env, ve, - &vq->qchase, vq->chase_reply, vq->key_entry, &rcode); + &vq->qchase, vq->chase_reply, vq->key_entry, &rcode, + qstate, vq, &nsec3_calculations, &suspend); + if(suspend) { + if(!validate_suspend_setup_timer(qstate, + vq, id, VAL_VALIDATE_STATE)) + return val_error(qstate, id); + return 0; + } verbose(VERB_DETAIL, "validate(nxdomain): %s", sec_status_to_string( vq->chase_reply->security)); @@ -1981,7 +2262,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, case VAL_CLASS_CNAME: verbose(VERB_ALGO, "Validating a cname response"); validate_cname_response(qstate->env, ve, - &vq->qchase, vq->chase_reply, vq->key_entry); + &vq->qchase, vq->chase_reply, vq->key_entry, + qstate, vq, &nsec3_calculations, &suspend); + if(suspend) { + if(!validate_suspend_setup_timer(qstate, + vq, id, VAL_VALIDATE_STATE)) + return val_error(qstate, id); + return 0; + } verbose(VERB_DETAIL, "validate(cname): %s", sec_status_to_string( vq->chase_reply->security)); @@ -1991,7 +2279,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, verbose(VERB_ALGO, "Validating a cname noanswer " "response"); validate_cname_noanswer_response(qstate->env, ve, - &vq->qchase, vq->chase_reply, vq->key_entry); + &vq->qchase, vq->chase_reply, vq->key_entry, + qstate, vq, &nsec3_calculations, &suspend); + if(suspend) { + if(!validate_suspend_setup_timer(qstate, + vq, id, VAL_VALIDATE_STATE)) + return val_error(qstate, id); + return 0; + } verbose(VERB_DETAIL, "validate(cname_noanswer): %s", sec_status_to_string( vq->chase_reply->security)); @@ -2009,7 +2304,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, verbose(VERB_ALGO, "Validating a positive ANY " "response"); validate_any_response(qstate->env, ve, &vq->qchase, - vq->chase_reply, vq->key_entry); + vq->chase_reply, vq->key_entry, qstate, vq, + &nsec3_calculations, &suspend); + if(suspend) { + if(!validate_suspend_setup_timer(qstate, + vq, id, VAL_VALIDATE_STATE)) + return val_error(qstate, id); + return 0; + } verbose(VERB_DETAIL, "validate(positive_any): %s", sec_status_to_string( vq->chase_reply->security)); @@ -2118,16 +2420,13 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq, if(vq->orig_msg->rep->security == sec_status_bogus) { /* see if we can try again to fetch data */ if(vq->restart_count < ve->max_restart) { - int restart_count = vq->restart_count+1; verbose(VERB_ALGO, "validation failed, " "blacklist and retry to fetch data"); val_blacklist(&qstate->blacklist, qstate->region, qstate->reply_origin, 0); qstate->reply_origin = NULL; qstate->errinf = NULL; - memset(vq, 0, sizeof(*vq)); - vq->restart_count = restart_count; - vq->state = VAL_INIT_STATE; + val_restart(vq); verbose(VERB_ALGO, "pass back to next module"); qstate->ext_state[id] = module_restart_next; return 0; @@ -2454,7 +2753,10 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, * DS response indicated an end to secure space, is_good if the DS * validated. It returns ke=NULL if the DS response indicated that the * request wasn't a delegation point. - * @return 0 on servfail error (malloc failure). + * @return + * 0 on success, + * 1 on servfail error (malloc failure), + * 2 on NSEC3 suspend. */ static int ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, @@ -2465,6 +2767,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, char* reason = NULL; sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; enum val_classification subtype; + int verified; if(rcode != LDNS_RCODE_NOERROR) { char rc[16]; rc[0]=0; @@ -2495,7 +2798,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, /* Verify only returns BOGUS or SECURE. If the rrset is * bogus, then we are done. */ sec = val_verify_rrset_entry(qstate->env, ve, ds, - vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate); + vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified); if(sec != sec_status_secure) { verbose(VERB_DETAIL, "DS rrset in DS response did " "not verify"); @@ -2513,7 +2816,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, ub_packed_rrset_ttl(ds), LDNS_EDE_UNSUPPORTED_DS_DIGEST, NULL, *qstate->env->now); - return (*ke) != NULL; + return (*ke) == NULL; } /* Otherwise, we return the positive response. */ @@ -2521,7 +2824,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, *ke = key_entry_create_rrset(qstate->region, qinfo->qname, qinfo->qname_len, qinfo->qclass, ds, NULL, LDNS_EDE_NONE, NULL, *qstate->env->now); - return (*ke) != NULL; + return (*ke) == NULL; } else if(subtype == VAL_CLASS_NODATA || subtype == VAL_CLASS_NAMEERROR) { /* NODATA means that the qname exists, but that there was @@ -2555,12 +2858,12 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, qinfo->qclass, proof_ttl, LDNS_EDE_NONE, NULL, *qstate->env->now); - return (*ke) != NULL; + return (*ke) == NULL; case sec_status_insecure: verbose(VERB_DETAIL, "NSEC RRset for the " "referral proved not a delegation point"); *ke = NULL; - return 1; + return 0; case sec_status_bogus: verbose(VERB_DETAIL, "NSEC RRset for the " "referral did not prove no DS."); @@ -2572,10 +2875,17 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, break; } + if(!nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { + log_err("malloc failure in ds_response_to_ke for " + "NSEC3 cache"); + reason = "malloc failure"; + errinf_ede(qstate, reason, 0); + goto return_bogus; + } sec = nsec3_prove_nods(qstate->env, ve, msg->rep->rrsets + msg->rep->an_numrrsets, msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason, - &reason_bogus, qstate); + &reason_bogus, qstate, &vq->nsec3_cache_table); switch(sec) { case sec_status_insecure: /* case insecure also continues to unsigned @@ -2589,18 +2899,19 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, qinfo->qclass, proof_ttl, LDNS_EDE_NONE, NULL, *qstate->env->now); - return (*ke) != NULL; + return (*ke) == NULL; case sec_status_indeterminate: verbose(VERB_DETAIL, "NSEC3s for the " "referral proved no delegation"); *ke = NULL; - return 1; + return 0; case sec_status_bogus: verbose(VERB_DETAIL, "NSEC3s for the " "referral did not prove no DS."); errinf_ede(qstate, reason, reason_bogus); goto return_bogus; case sec_status_unchecked: + return 2; default: /* NSEC3 proof did not work */ break; @@ -2641,13 +2952,13 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, } sec = val_verify_rrset_entry(qstate->env, ve, cname, vq->key_entry, &reason, &reason_bogus, - LDNS_SECTION_ANSWER, qstate); + LDNS_SECTION_ANSWER, qstate, &verified); if(sec == sec_status_secure) { verbose(VERB_ALGO, "CNAME validated, " "proof that DS does not exist"); /* and that it is not a referral point */ *ke = NULL; - return 1; + return 0; } errinf(qstate, "CNAME in DS response was not secure."); errinf_ede(qstate, reason, reason_bogus); @@ -2671,7 +2982,7 @@ return_bogus: *ke = key_entry_create_bad(qstate->region, qinfo->qname, qinfo->qname_len, qinfo->qclass, BOGUS_KEY_TTL, reason_bogus, reason, *qstate->env->now); - return (*ke) != NULL; + return (*ke) == NULL; } /** @@ -2692,17 +3003,31 @@ return_bogus: static void process_ds_response(struct module_qstate* qstate, struct val_qstate* vq, int id, int rcode, struct dns_msg* msg, struct query_info* qinfo, - struct sock_list* origin) + struct sock_list* origin, int* suspend) { struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; struct key_entry_key* dske = NULL; uint8_t* olds = vq->empty_DS_name; + int ret; + *suspend = 0; vq->empty_DS_name = NULL; - if(!ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske)) { + ret = ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske); + if(ret != 0) { + switch(ret) { + case 1: log_err("malloc failure in process_ds_response"); vq->key_entry = NULL; /* make it error */ vq->state = VAL_VALIDATE_STATE; return; + case 2: + *suspend = 1; + return; + default: + log_err("unhandled error value for ds_response_to_ke"); + vq->key_entry = NULL; /* make it error */ + vq->state = VAL_VALIDATE_STATE; + return; + } } if(dske == NULL) { vq->empty_DS_name = regional_alloc_init(qstate->region, @@ -2954,9 +3279,26 @@ val_inform_super(struct module_qstate* qstate, int id, return; } if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS) { + int suspend; process_ds_response(super, vq, id, qstate->return_rcode, qstate->return_msg, &qstate->qinfo, - qstate->reply_origin); + qstate->reply_origin, &suspend); + /* If NSEC3 was needed during validation, NULL the NSEC3 cache; + * it will be re-initiated if needed later on. + * Validation (and the cache table) are happening/allocated in + * the super qstate whilst the RRs are allocated (and pointed + * to) in this sub qstate. */ + if(vq->nsec3_cache_table.ct) { + vq->nsec3_cache_table.ct = NULL; + } + if(suspend) { + /* deep copy the return_msg to vq->sub_ds_msg; it will + * be resumed later in the super state with the caveat + * that the initial calculations will be re-caclulated + * and re-suspended there before continuing. */ + vq->sub_ds_msg = dns_msg_deepcopy_region( + qstate->return_msg, super->region); + } return; } else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY) { process_dnskey_response(super, vq, id, qstate->return_rcode, @@ -2970,8 +3312,15 @@ val_inform_super(struct module_qstate* qstate, int id, void val_clear(struct module_qstate* qstate, int id) { + struct val_qstate* vq; if(!qstate) return; + vq = (struct val_qstate*)qstate->minfo[id]; + if(vq) { + if(vq->suspend_timer) { + comm_timer_delete(vq->suspend_timer); + } + } /* everything is allocated in the region, so assign NULL */ qstate->minfo[id] = NULL; } diff --git a/sbin/unwind/libunbound/validator/validator.h b/sbin/unwind/libunbound/validator/validator.h index b620c1e98..ab5cb970f 100644 --- a/sbin/unwind/libunbound/validator/validator.h +++ b/sbin/unwind/libunbound/validator/validator.h @@ -45,11 +45,13 @@ #include "util/module.h" #include "util/data/msgreply.h" #include "validator/val_utils.h" +#include "validator/val_nsec3.h" struct val_anchors; struct key_cache; struct key_entry_key; struct val_neg_cache; struct config_strlist; +struct comm_timer; /** * This is the TTL to use when a trust anchor fails to prime. A trust anchor @@ -215,6 +217,19 @@ struct val_qstate { /** true if this state is waiting to prime a trust anchor */ int wait_prime_ta; + + /** State to continue with RRSIG validation in a message later */ + int msg_signatures_state; + /** The rrset index for the msg signatures to continue from */ + size_t msg_signatures_index; + /** Cache table for NSEC3 hashes */ + struct nsec3_cache_table nsec3_cache_table; + /** DS message from sub if it got suspended from NSEC3 calculations */ + struct dns_msg* sub_ds_msg; + /** The timer to resume processing msg signatures */ + struct comm_timer* suspend_timer; + /** Number of suspends */ + int suspend_count; }; /** @@ -262,4 +277,7 @@ void val_clear(struct module_qstate* qstate, int id); */ size_t val_get_mem(struct module_env* env, int id); +/** Timer callback for msg signatures continue timer */ +void validate_suspend_timer_cb(void* arg); + #endif /* VALIDATOR_VALIDATOR_H */ diff --git a/sys/arch/armv7/marvell/mvacc.c b/sys/arch/armv7/marvell/mvacc.c index 6926aaef7..d5791dfd9 100644 --- a/sys/arch/armv7/marvell/mvacc.c +++ b/sys/arch/armv7/marvell/mvacc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mvacc.c,v 1.5 2022/07/11 10:44:08 jmatthew Exp $ */ +/* $OpenBSD: mvacc.c,v 1.6 2024/02/13 02:14:25 jsg Exp $ */ /* * Copyright (c) 2016 Patrick Wildt * @@ -133,7 +133,7 @@ mvacc_get_frequency(void *cookie, uint32_t *cells) cpu = (sar >> SAR_CPU_DDR_FREQ_OPT) & SAR_CPU_DDR_FREQ_OPT_MASK; tclk = (sar >> SAR_TCLK_FREQ_OPT) & SAR_TCLK_FREQ_OPT_MASK; - if (cpu > nitems(mvacc_cpu_freqs)) { + if (cpu >= nitems(mvacc_cpu_freqs)) { printf("%s: invalid cpu frequency", sc->sc_dev.dv_xname); return 0; } diff --git a/sys/dev/fdt/ehci_fdt.c b/sys/dev/fdt/ehci_fdt.c index a94b94c82..b93b16e4c 100644 --- a/sys/dev/fdt/ehci_fdt.c +++ b/sys/dev/fdt/ehci_fdt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ehci_fdt.c,v 1.11 2024/01/26 17:11:50 kettenis Exp $ */ +/* $OpenBSD: ehci_fdt.c,v 1.12 2024/02/12 21:37:25 uaa Exp $ */ /* * Copyright (c) 2005 David Gwynne @@ -207,6 +207,7 @@ struct ehci_phy ehci_phys[] = { { "allwinner,sun8i-v3s-usb-phy", sun4i_phy_init }, { "allwinner,sun20i-d1-usb-phy", sun4i_phy_init }, { "allwinner,sun50i-h6-usb-phy", sun4i_phy_init }, + { "allwinner,sun50i-h616-usb-phy", sun4i_phy_init }, { "allwinner,sun50i-a64-usb-phy", sun4i_phy_init }, { "allwinner,sun9i-a80-usb-phy", sun9i_phy_init }, }; @@ -286,6 +287,62 @@ ehci_init_phys(struct ehci_fdt_softc *sc) #define SUNXI_AHB_INCR8 (1 << 10) #define SUNXI_AHB_INCR16 (1 << 11) +void +sun50i_h616_phy2_init(struct ehci_fdt_softc *sc, int node) +{ + int len, idx; + uint32_t *reg, val; + bus_size_t size; + bus_space_handle_t ioh; + + /* + * to access USB2-PHY register, get address from "reg" property of + * current "allwinner,...-usb-phy" node + */ + len = OF_getproplen(node, "reg"); + if (len <= 0) + goto out; + + reg = malloc(len, M_TEMP, M_WAITOK); + OF_getpropintarray(node, "reg", reg, len); + + idx = OF_getindex(node, "pmu2", "reg-names"); + if (idx < 0 || (idx + 1) > (len / (sizeof(uint32_t) * 2))) { + printf(": no phy2 register\n"); + goto free; + } + + /* convert "reg-names" index to "reg" (address-size pair) index */ + idx *= 2; + + size = reg[idx + 1]; + if (bus_space_map(sc->sc.iot, reg[idx], size, 0, &ioh)) { + printf(": can't map phy2 registers\n"); + goto free; + } + + clock_enable(node, "usb2_phy"); + reset_deassert(node, "usb2_reset"); + clock_enable(node, "pmu2_clk"); + + /* + * address is offset from "pmu2", not EHCI2 base address + * (normally it points EHCI2 base address + 0x810) + */ + val = bus_space_read_4(sc->sc.iot, ioh, 0x10); + val &= ~(1 << 3); /* clear SIDDQ */ + bus_space_write_4(sc->sc.iot, ioh, 0x10, val); + + clock_disable(node, "pmu2_clk"); + /* "usb2_reset" and "usb2_phy" unchanged */ + + bus_space_unmap(sc->sc.iot, ioh, size); +free: + free(reg, M_TEMP, len); +out: + return; +} + void sun4i_phy_init(struct ehci_fdt_softc *sc, uint32_t *cells) { @@ -298,6 +355,11 @@ sun4i_phy_init(struct ehci_fdt_softc *sc, uint32_t *cells) if (node == -1) return; + /* Allwinner H616 needs to clear PHY2's SIDDQ flag */ + if (OF_is_compatible(node, "allwinner,sun50i-h616-usb-phy") && + cells[1] != 2) + sun50i_h616_phy2_init(sc, node); + val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, SUNXI_HCI_ICR); val |= SUNXI_AHB_INCR8 | SUNXI_AHB_INCR4; val |= SUNXI_AHB_INCRX_ALIGN; @@ -315,11 +377,19 @@ sun4i_phy_init(struct ehci_fdt_softc *sc, uint32_t *cells) val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, 0x810); val &= ~(1 << 1); bus_space_write_4(sc->sc.iot, sc->sc.ioh, 0x810, val); - } else if (OF_is_compatible(node, "allwinner,sun20i-d1-usb-phy")) { + } else if (OF_is_compatible(node, "allwinner,sun8i-a83t-usb-phy") || + OF_is_compatible(node, "allwinner,sun20i-d1-usb-phy") || + OF_is_compatible(node, "allwinner,sun50i-h616-usb-phy")) { val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, 0x810); val &= ~(1 << 3); bus_space_write_4(sc->sc.iot, sc->sc.ioh, 0x810, val); } + if (OF_is_compatible(node, "allwinner,sun8i-a83t-usb-phy") || + OF_is_compatible(node, "allwinner,sun50i-h616-usb-phy")) { + val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, 0x810); + val |= 1 << 5; + bus_space_write_4(sc->sc.iot, sc->sc.ioh, 0x810, val); + } pinctrl_byname(node, "default"); diff --git a/sys/dev/pci/if_bnxt.c b/sys/dev/pci/if_bnxt.c index 68e84952d..262a1832c 100644 --- a/sys/dev/pci/if_bnxt.c +++ b/sys/dev/pci/if_bnxt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_bnxt.c,v 1.45 2024/01/19 03:25:13 jmatthew Exp $ */ +/* $OpenBSD: if_bnxt.c,v 1.46 2024/02/13 13:58:19 bluhm Exp $ */ /*- * Broadcom NetXtreme-C/E network driver. * @@ -1433,13 +1433,13 @@ bnxt_start(struct ifqueue *ifq) lflags |= TX_BD_LONG_LFLAGS_LSO; hdrsize = sizeof(*ext.eh); if (ext.ip4) - hdrsize += ext.ip4->ip_hl << 2; + hdrsize += ext.ip4hlen; else if (ext.ip6) hdrsize += sizeof(*ext.ip6); else tcpstat_inc(tcps_outbadtso); - hdrsize += ext.tcp->th_off << 2; + hdrsize += ext.tcphlen; txhi->hdr_size = htole16(hdrsize / 2); outlen = m->m_pkthdr.ph_mss; diff --git a/sys/dev/pci/if_em.c b/sys/dev/pci/if_em.c index c6137fe0f..9bee73e46 100644 --- a/sys/dev/pci/if_em.c +++ b/sys/dev/pci/if_em.c @@ -31,7 +31,7 @@ POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ -/* $OpenBSD: if_em.c,v 1.371 2024/01/28 18:42:58 mglocker Exp $ */ +/* $OpenBSD: if_em.c,v 1.372 2024/02/13 13:58:19 bluhm Exp $ */ /* $FreeBSD: if_em.c,v 1.46 2004/09/29 18:28:28 mlaier Exp $ */ #include @@ -2433,7 +2433,7 @@ em_tx_ctx_setup(struct em_queue *que, struct mbuf *mp, u_int head, vlan_macip_lens |= (sizeof(*ext.eh) << E1000_ADVTXD_MACLEN_SHIFT); if (ext.ip4) { - iphlen = ext.ip4->ip_hl << 2; + iphlen = ext.ip4hlen; type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_IPV4; if (ISSET(mp->m_pkthdr.csum_flags, M_IPV4_CSUM_OUT)) { diff --git a/sys/dev/pci/if_igc.c b/sys/dev/pci/if_igc.c index f1b6deac9..f108b239f 100644 --- a/sys/dev/pci/if_igc.c +++ b/sys/dev/pci/if_igc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_igc.c,v 1.15 2024/01/23 08:48:12 kevlo Exp $ */ +/* $OpenBSD: if_igc.c,v 1.16 2024/02/13 13:58:19 bluhm Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * @@ -2028,7 +2028,7 @@ igc_tx_ctx_setup(struct tx_ring *txr, struct mbuf *mp, int prod, ether_extract_headers(mp, &ext); if (ext.ip4) { - iphlen = ext.ip4->ip_hl << 2; + iphlen = ext.ip4hlen; type_tucmd_mlhl |= IGC_ADVTXD_TUCMD_IPV4; if (ISSET(mp->m_pkthdr.csum_flags, M_IPV4_CSUM_OUT)) { diff --git a/sys/dev/pci/if_ix.c b/sys/dev/pci/if_ix.c index d75e6d491..45298828a 100644 --- a/sys/dev/pci/if_ix.c +++ b/sys/dev/pci/if_ix.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_ix.c,v 1.206 2023/11/10 15:51:20 bluhm Exp $ */ +/* $OpenBSD: if_ix.c,v 1.207 2024/02/13 13:58:19 bluhm Exp $ */ /****************************************************************************** @@ -2502,7 +2502,7 @@ ixgbe_tx_offload(struct mbuf *mp, uint32_t *vlan_macip_lens, *vlan_macip_lens |= (ethlen << IXGBE_ADVTXD_MACLEN_SHIFT); if (ext.ip4) { - iphlen = ext.ip4->ip_hl << 2; + iphlen = ext.ip4hlen; if (ISSET(mp->m_pkthdr.csum_flags, M_IPV4_CSUM_OUT)) { *olinfo_status |= IXGBE_TXD_POPTS_IXSM << 8; @@ -2542,7 +2542,7 @@ ixgbe_tx_offload(struct mbuf *mp, uint32_t *vlan_macip_lens, if (ext.tcp) { uint32_t hdrlen, thlen, paylen, outlen; - thlen = ext.tcp->th_off << 2; + thlen = ext.tcphlen; outlen = mp->m_pkthdr.ph_mss; *mss_l4len_idx |= outlen << IXGBE_ADVTXD_MSS_SHIFT; @@ -3277,11 +3277,11 @@ ixgbe_rxeof(struct rx_ring *rxr) hdrlen += ETHER_VLAN_ENCAP_LEN; #endif if (ext.ip4) - hdrlen += ext.ip4->ip_hl << 2; + hdrlen += ext.ip4hlen; if (ext.ip6) hdrlen += sizeof(*ext.ip6); if (ext.tcp) { - hdrlen += ext.tcp->th_off << 2; + hdrlen += ext.tcphlen; tcpstat_inc(tcps_inhwlro); tcpstat_add(tcps_inpktlro, pkts); } else { diff --git a/sys/dev/pv/if_vio.c b/sys/dev/pv/if_vio.c index 550c4deb9..3648e4ac3 100644 --- a/sys/dev/pv/if_vio.c +++ b/sys/dev/pv/if_vio.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_vio.c,v 1.29 2023/12/20 09:51:06 jan Exp $ */ +/* $OpenBSD: if_vio.c,v 1.30 2024/02/13 13:58:19 bluhm Exp $ */ /* * Copyright (c) 2012 Stefan Fritsch, Alexander Fiveg. @@ -765,7 +765,7 @@ again: hdr->csum_offset = offsetof(struct udphdr, uh_sum); if (ext.ip4) - hdr->csum_start += ext.ip4->ip_hl << 2; + hdr->csum_start += ext.ip4hlen; #ifdef INET6 else if (ext.ip6) hdr->csum_start += sizeof(*ext.ip6); diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c index f6f98b734..f851089aa 100644 --- a/sys/kern/kern_clock.c +++ b/sys/kern/kern_clock.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_clock.c,v 1.122 2024/02/09 17:42:18 cheloha Exp $ */ +/* $OpenBSD: kern_clock.c,v 1.123 2024/02/12 22:07:33 cheloha Exp $ */ /* $NetBSD: kern_clock.c,v 1.34 1996/06/09 04:51:03 briggs Exp $ */ /*- @@ -140,13 +140,6 @@ initclocks(void) void hardclock(struct clockframe *frame) { - /* - * If we are not the primary CPU, we're not allowed to do - * any more work. - */ - if (CPU_IS_PRIMARY(curcpu()) == 0) - return; - tc_ticktock(); ticks++; jiffies++; diff --git a/sys/kern/kern_clockintr.c b/sys/kern/kern_clockintr.c index 4141a2bbe..1a36e4975 100644 --- a/sys/kern/kern_clockintr.c +++ b/sys/kern/kern_clockintr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_clockintr.c,v 1.66 2024/02/09 16:52:58 cheloha Exp $ */ +/* $OpenBSD: kern_clockintr.c,v 1.67 2024/02/12 22:07:33 cheloha Exp $ */ /* * Copyright (c) 2003 Dale Rahn * Copyright (c) 2020 Mark Kettenis @@ -63,7 +63,7 @@ clockintr_cpu_init(const struct intrclock *ic) clockqueue_intrclock_install(cq, ic); /* TODO: Remove this from struct clockintr_queue. */ - if (cq->cq_hardclock.cl_expiration == 0) { + if (CPU_IS_PRIMARY(ci) && cq->cq_hardclock.cl_expiration == 0) { clockintr_bind(&cq->cq_hardclock, ci, clockintr_hardclock, NULL); } @@ -99,12 +99,6 @@ clockintr_cpu_init(const struct intrclock *ic) clockintr_schedule(&cq->cq_hardclock, 0); else clockintr_advance(&cq->cq_hardclock, hardclock_period); - } else { - if (cq->cq_hardclock.cl_expiration == 0) { - clockintr_stagger(&cq->cq_hardclock, hardclock_period, - multiplier, MAXCPUS); - } - clockintr_advance(&cq->cq_hardclock, hardclock_period); } /* diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index 6d7422c79..85032afee 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uipc_socket.c,v 1.319 2024/02/11 21:36:49 mvs Exp $ */ +/* $OpenBSD: uipc_socket.c,v 1.320 2024/02/12 22:48:27 mvs Exp $ */ /* $NetBSD: uipc_socket.c,v 1.21 1996/02/04 02:17:52 christos Exp $ */ /* @@ -132,8 +132,9 @@ soinit(void) } struct socket * -soalloc(const struct domain *dp, int wait) +soalloc(const struct protosw *prp, int wait) { + const struct domain *dp = prp->pr_domain; struct socket *so; so = pool_get(&socket_pool, (wait == M_WAIT ? PR_WAITOK : PR_NOWAIT) | @@ -153,7 +154,7 @@ soalloc(const struct domain *dp, int wait) switch (dp->dom_family) { case AF_INET: case AF_INET6: - switch (dp->dom_protosw->pr_type) { + switch (prp->pr_type) { case SOCK_DGRAM: case SOCK_RAW: so->so_rcv.sb_flags |= SB_MTXLOCK; @@ -188,7 +189,7 @@ socreate(int dom, struct socket **aso, int type, int proto) return (EPROTONOSUPPORT); if (prp->pr_type != type) return (EPROTOTYPE); - so = soalloc(pffinddomain(dom), M_WAIT); + so = soalloc(prp, M_WAIT); so->so_type = type; if (suser(p) == 0) so->so_state = SS_PRIV; diff --git a/sys/kern/uipc_socket2.c b/sys/kern/uipc_socket2.c index 9322cd273..70f7c150f 100644 --- a/sys/kern/uipc_socket2.c +++ b/sys/kern/uipc_socket2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uipc_socket2.c,v 1.143 2024/02/11 18:14:26 mvs Exp $ */ +/* $OpenBSD: uipc_socket2.c,v 1.144 2024/02/12 22:48:27 mvs Exp $ */ /* $NetBSD: uipc_socket2.c,v 1.11 1996/02/04 02:17:55 christos Exp $ */ /* @@ -188,7 +188,7 @@ sonewconn(struct socket *head, int connstatus, int wait) return (NULL); if (head->so_qlen + head->so_q0len > head->so_qlimit * 3) return (NULL); - so = soalloc(head->so_proto->pr_domain, wait); + so = soalloc(head->so_proto, wait); if (so == NULL) return (NULL); so->so_type = head->so_type; diff --git a/sys/lib/libz/zconf.h b/sys/lib/libz/zconf.h index 973c03088..b3c057c4f 100644 --- a/sys/lib/libz/zconf.h +++ b/sys/lib/libz/zconf.h @@ -513,12 +513,12 @@ typedef uLong FAR uLongf; #if !defined(_WIN32) && defined(Z_LARGE64) # define z_off64_t off64_t +#elif defined(_WIN32) && !defined(__GNUC__) +# define z_off64_t __int64 +#elif defined(__GO32__) +# define z_off64_t offset_t #else -# if defined(_WIN32) && !defined(__GNUC__) -# define z_off64_t __int64 -# else -# define z_off64_t z_off_t -# endif +# define z_off64_t z_off_t #endif /* MVS linker does not support external names larger than 8 bytes */ diff --git a/sys/lib/libz/zlib.h b/sys/lib/libz/zlib.h index 2844c3e17..493d52021 100644 --- a/sys/lib/libz/zlib.h +++ b/sys/lib/libz/zlib.h @@ -1891,9 +1891,9 @@ ZEXTERN int ZEXPORT gzgetc_(gzFile file); /* backward compatibility */ ZEXTERN z_off_t ZEXPORT gzseek64(gzFile, z_off_t, int); ZEXTERN z_off_t ZEXPORT gztell64(gzFile); ZEXTERN z_off_t ZEXPORT gzoffset64(gzFile); - ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t); - ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t); - ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t); + ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off64_t); # endif #else ZEXTERN gzFile ZEXPORT gzopen(const char *, const char *); diff --git a/sys/lib/libz/zutil.h b/sys/lib/libz/zutil.h index 10018086f..583354ed1 100644 --- a/sys/lib/libz/zutil.h +++ b/sys/lib/libz/zutil.h @@ -184,11 +184,10 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ #endif /* provide prototypes for these when building zlib without LFS */ -#if !defined(_WIN32) && \ - (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) - ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t); - ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t); - ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t); +#ifndef Z_LARGE64 + ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off64_t); #endif /* common defaults */ diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index 486b041bc..51c65e8fa 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_bridge.c,v 1.368 2023/05/16 14:32:54 jan Exp $ */ +/* $OpenBSD: if_bridge.c,v 1.369 2024/02/13 12:22:09 bluhm Exp $ */ /* * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) @@ -48,6 +48,7 @@ #include #include #include +#include #include #include diff --git a/sys/net/if_etherip.c b/sys/net/if_etherip.c index 6db68266e..03998a4ed 100644 --- a/sys/net/if_etherip.c +++ b/sys/net/if_etherip.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_etherip.c,v 1.54 2023/12/23 10:52:54 bluhm Exp $ */ +/* $OpenBSD: if_etherip.c,v 1.55 2024/02/13 12:22:09 bluhm Exp $ */ /* * Copyright (c) 2015 Kazuya GODA * @@ -31,6 +31,7 @@ #include #include #include +#include #include #include diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index a77a0d654..2d732535c 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_ethersubr.c,v 1.291 2023/07/27 20:21:25 jan Exp $ */ +/* $OpenBSD: if_ethersubr.c,v 1.292 2024/02/13 13:58:19 bluhm Exp $ */ /* $NetBSD: if_ethersubr.c,v 1.19 1996/05/07 02:40:30 thorpej Exp $ */ /* @@ -140,6 +140,20 @@ didn't get a copy, you may request one from . #include #endif /* MPLS */ +/* #define ETHERDEBUG 1 */ +#ifdef ETHERDEBUG +int etherdebug = ETHERDEBUG; +#define DNPRINTF(level, fmt, args...) \ + do { \ + if (etherdebug >= level) \ + printf("%s: " fmt "\n", __func__, ## args); \ + } while (0) +#else +#define DNPRINTF(level, fmt, args...) \ + do { } while (0) +#endif +#define DPRINTF(fmt, args...) DNPRINTF(1, fmt, args) + u_int8_t etherbroadcastaddr[ETHER_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; u_int8_t etheranyaddr[ETHER_ADDR_LEN] = @@ -1034,56 +1048,126 @@ ether_e64_to_addr(struct ether_addr *ea, uint64_t e64) /* Parse different TCP/IP protocol headers for a quick view inside an mbuf. */ void -ether_extract_headers(struct mbuf *mp, struct ether_extracted *ext) +ether_extract_headers(struct mbuf *m0, struct ether_extracted *ext) { struct mbuf *m; - uint64_t hlen; + size_t hlen; int hoff; uint8_t ipproto; uint16_t ether_type; + /* gcc 4.2.1 on sparc64 may create 32 bit loads on unaligned mbuf */ + union { + u_char hc_data; +#if _BYTE_ORDER == _LITTLE_ENDIAN + struct { + u_int hl:4, /* header length */ + v:4; /* version */ + } hc_ip; + struct { + u_int x2:4, /* (unused) */ + off:4; /* data offset */ + } hc_th; +#endif +#if _BYTE_ORDER == _BIG_ENDIAN + struct { + u_int v:4, /* version */ + hl:4; /* header length */ + } hc_ip; + struct { + u_int off:4, /* data offset */ + x2:4; /* (unused) */ + } hc_th; +#endif + } hdrcpy; /* Return NULL if header was not recognized. */ memset(ext, 0, sizeof(*ext)); - if (mp->m_len < sizeof(*ext->eh)) - return; + KASSERT(ISSET(m0->m_flags, M_PKTHDR)); + ext->paylen = m0->m_pkthdr.len; - ext->eh = mtod(mp, struct ether_header *); + if (m0->m_len < sizeof(*ext->eh)) { + DPRINTF("m_len %d, eh %zu", m0->m_len, sizeof(*ext->eh)); + return; + } + ext->eh = mtod(m0, struct ether_header *); ether_type = ntohs(ext->eh->ether_type); hlen = sizeof(*ext->eh); + if (ext->paylen < hlen) { + DPRINTF("paylen %u, ehlen %zu", ext->paylen, hlen); + ext->eh = NULL; + return; + } + ext->paylen -= hlen; #if NVLAN > 0 if (ether_type == ETHERTYPE_VLAN) { - ext->evh = mtod(mp, struct ether_vlan_header *); + if (m0->m_len < sizeof(*ext->evh)) { + DPRINTF("m_len %d, evh %zu", + m0->m_len, sizeof(*ext->evh)); + return; + } + ext->evh = mtod(m0, struct ether_vlan_header *); ether_type = ntohs(ext->evh->evl_proto); hlen = sizeof(*ext->evh); + if (sizeof(*ext->eh) + ext->paylen < hlen) { + DPRINTF("paylen %zu, evhlen %zu", + sizeof(*ext->eh) + ext->paylen, hlen); + ext->evh = NULL; + return; + } + ext->paylen = sizeof(*ext->eh) + ext->paylen - hlen; } #endif switch (ether_type) { case ETHERTYPE_IP: - m = m_getptr(mp, hlen, &hoff); - if (m == NULL || m->m_len - hoff < sizeof(*ext->ip4)) + m = m_getptr(m0, hlen, &hoff); + if (m == NULL || m->m_len - hoff < sizeof(*ext->ip4)) { + DPRINTF("m_len %d, hoff %d, ip4 %zu", + m ? m->m_len : -1, hoff, sizeof(*ext->ip4)); return; + } ext->ip4 = (struct ip *)(mtod(m, caddr_t) + hoff); + memcpy(&hdrcpy.hc_data, ext->ip4, 1); + hlen = hdrcpy.hc_ip.hl << 2; + if (m->m_len - hoff < hlen) { + DPRINTF("m_len %d, hoff %d, iphl %zu", + m ? m->m_len : -1, hoff, hlen); + ext->ip4 = NULL; + return; + } + if (ext->paylen < hlen) { + DPRINTF("paylen %u, ip4hlen %zu", ext->paylen, hlen); + ext->ip4 = NULL; + return; + } + ext->ip4hlen = hlen; + ext->paylen -= hlen; + ipproto = ext->ip4->ip_p; + if (ISSET(ntohs(ext->ip4->ip_off), IP_MF|IP_OFFMASK)) return; - - hlen = ext->ip4->ip_hl << 2; - ipproto = ext->ip4->ip_p; - break; #ifdef INET6 case ETHERTYPE_IPV6: - m = m_getptr(mp, hlen, &hoff); - if (m == NULL || m->m_len - hoff < sizeof(*ext->ip6)) + m = m_getptr(m0, hlen, &hoff); + if (m == NULL || m->m_len - hoff < sizeof(*ext->ip6)) { + DPRINTF("m_len %d, hoff %d, ip6 %zu", + m ? m->m_len : -1, hoff, sizeof(*ext->ip6)); return; + } ext->ip6 = (struct ip6_hdr *)(mtod(m, caddr_t) + hoff); hlen = sizeof(*ext->ip6); + if (ext->paylen < hlen) { + DPRINTF("paylen %u, ip6hlen %zu", ext->paylen, hlen); + ext->ip6 = NULL; + return; + } + ext->paylen -= hlen; ipproto = ext->ip6->ip6_nxt; - break; #endif default: @@ -1093,16 +1177,51 @@ ether_extract_headers(struct mbuf *mp, struct ether_extracted *ext) switch (ipproto) { case IPPROTO_TCP: m = m_getptr(m, hoff + hlen, &hoff); - if (m == NULL || m->m_len - hoff < sizeof(*ext->tcp)) + if (m == NULL || m->m_len - hoff < sizeof(*ext->tcp)) { + DPRINTF("m_len %d, hoff %d, tcp %zu", + m ? m->m_len : -1, hoff, sizeof(*ext->tcp)); return; + } ext->tcp = (struct tcphdr *)(mtod(m, caddr_t) + hoff); + + memcpy(&hdrcpy.hc_data, &ext->tcp->th_flags - 1, 1); + hlen = hdrcpy.hc_th.off << 2; + if (m->m_len - hoff < hlen) { + DPRINTF("m_len %d, hoff %d, thoff %zu", + m ? m->m_len : -1, hoff, hlen); + ext->tcp = NULL; + return; + } + if (ext->paylen < hlen) { + DPRINTF("paylen %u, tcphlen %zu", ext->paylen, hlen); + ext->tcp = NULL; + return; + } + ext->tcphlen = hlen; + ext->paylen -= hlen; break; case IPPROTO_UDP: m = m_getptr(m, hoff + hlen, &hoff); - if (m == NULL || m->m_len - hoff < sizeof(*ext->udp)) + if (m == NULL || m->m_len - hoff < sizeof(*ext->udp)) { + DPRINTF("m_len %d, hoff %d, tcp %zu", + m ? m->m_len : -1, hoff, sizeof(*ext->tcp)); return; + } ext->udp = (struct udphdr *)(mtod(m, caddr_t) + hoff); + + hlen = sizeof(*ext->udp); + if (ext->paylen < hlen) { + DPRINTF("paylen %u, udphlen %zu", ext->paylen, hlen); + ext->udp = NULL; + return; + } break; } + + DNPRINTF(2, "%s%s%s%s%s%s ip4h %u, tcph %u, payl %u", + ext->eh ? "eh," : "", ext->evh ? "evh," : "", + ext->ip4 ? "ip4," : "", ext->ip6 ? "ip6," : "", + ext->tcp ? "tcp," : "", ext->udp ? "udp," : "", + ext->ip4hlen, ext->tcphlen, ext->paylen); } diff --git a/sys/net/if_pfsync.c b/sys/net/if_pfsync.c index 0f328bec6..e524daa76 100644 --- a/sys/net/if_pfsync.c +++ b/sys/net/if_pfsync.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_pfsync.c,v 1.324 2023/12/23 10:52:54 bluhm Exp $ */ +/* $OpenBSD: if_pfsync.c,v 1.325 2024/02/13 12:22:09 bluhm Exp $ */ /* * Copyright (c) 2002 Michael Shalayeff @@ -69,6 +69,7 @@ #include #include #include +#include #include #include diff --git a/sys/net/if_veb.c b/sys/net/if_veb.c index e6cdf6bbe..ebbca155c 100644 --- a/sys/net/if_veb.c +++ b/sys/net/if_veb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_veb.c,v 1.34 2023/12/23 10:52:54 bluhm Exp $ */ +/* $OpenBSD: if_veb.c,v 1.35 2024/02/13 12:22:09 bluhm Exp $ */ /* * Copyright (c) 2021 David Gwynne @@ -46,7 +46,6 @@ #ifdef INET6 #include #include -#include #endif #if 0 && defined(IPSEC) diff --git a/sys/net/route.c b/sys/net/route.c index bb9c7b050..f9915cdf7 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -1,4 +1,4 @@ -/* $OpenBSD: route.c,v 1.431 2024/02/09 14:02:11 bluhm Exp $ */ +/* $OpenBSD: route.c,v 1.432 2024/02/13 12:22:09 bluhm Exp $ */ /* $NetBSD: route.c,v 1.14 1996/02/13 22:00:46 christos Exp $ */ /* @@ -212,8 +212,8 @@ route_cache(struct route *ro, struct in_addr addr, u_int rtableid) if (rtisvalid(ro->ro_rt) && ro->ro_generation == gen && ro->ro_tableid == rtableid && - ro->ro_dst.sa_family == AF_INET && - satosin(&ro->ro_dst)->sin_addr.s_addr == addr.s_addr) { + ro->ro_dstsa.sa_family == AF_INET && + ro->ro_dstsin.sin_addr.s_addr == addr.s_addr) { ipstat_inc(ips_rtcachehit); return (0); } @@ -225,17 +225,16 @@ route_cache(struct route *ro, struct in_addr addr, u_int rtableid) ro->ro_tableid = rtableid; memset(&ro->ro_dst, 0, sizeof(ro->ro_dst)); - satosin(&ro->ro_dst)->sin_family = AF_INET; - satosin(&ro->ro_dst)->sin_len = sizeof(struct sockaddr_in); - satosin(&ro->ro_dst)->sin_addr = addr; + ro->ro_dstsin.sin_family = AF_INET; + ro->ro_dstsin.sin_len = sizeof(struct sockaddr_in); + ro->ro_dstsin.sin_addr = addr; return (ESRCH); } #ifdef INET6 int -route6_cache(struct route_in6 *ro, const struct in6_addr *addr, - u_int rtableid) +route6_cache(struct route *ro, const struct in6_addr *addr, u_int rtableid) { u_long gen; @@ -245,8 +244,8 @@ route6_cache(struct route_in6 *ro, const struct in6_addr *addr, if (rtisvalid(ro->ro_rt) && ro->ro_generation == gen && ro->ro_tableid == rtableid && - ro->ro_dst.sin6_family == AF_INET6 && - IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr, addr)) { + ro->ro_dstsa.sa_family == AF_INET6 && + IN6_ARE_ADDR_EQUAL(&ro->ro_dstsin6.sin6_addr, addr)) { ip6stat_inc(ip6s_rtcachehit); return (0); } @@ -258,9 +257,9 @@ route6_cache(struct route_in6 *ro, const struct in6_addr *addr, ro->ro_tableid = rtableid; memset(&ro->ro_dst, 0, sizeof(ro->ro_dst)); - ro->ro_dst.sin6_family = AF_INET6; - ro->ro_dst.sin6_len = sizeof(struct sockaddr_in6); - ro->ro_dst.sin6_addr = *addr; + ro->ro_dstsin6.sin6_family = AF_INET6; + ro->ro_dstsin6.sin6_len = sizeof(struct sockaddr_in6); + ro->ro_dstsin6.sin6_addr = *addr; return (ESRCH); } diff --git a/sys/net/route.h b/sys/net/route.h index 9871d820c..7833f7ca9 100644 --- a/sys/net/route.h +++ b/sys/net/route.h @@ -1,4 +1,4 @@ -/* $OpenBSD: route.h,v 1.205 2024/02/05 12:52:11 aoyama Exp $ */ +/* $OpenBSD: route.h,v 1.206 2024/02/13 12:22:09 bluhm Exp $ */ /* $NetBSD: route.h,v 1.9 1996/02/13 22:00:49 christos Exp $ */ /* @@ -370,6 +370,19 @@ struct sockaddr_rtsearch { char sr_search[RTSEARCH_LEN]; }; +struct rt_addrinfo { + int rti_addrs; + const struct sockaddr *rti_info[RTAX_MAX]; + int rti_flags; + struct ifaddr *rti_ifa; + struct rt_msghdr *rti_rtm; + u_char rti_mpls; +}; + +#ifdef __BSD_VISIBLE + +#include + /* * A route consists of a destination address and a reference * to a routing entry. These are often held by protocols @@ -379,17 +392,17 @@ struct route { struct rtentry *ro_rt; u_long ro_generation; u_long ro_tableid; /* u_long because of alignment */ - struct sockaddr ro_dst; + union { + struct sockaddr rod_sa; + struct sockaddr_in rod_sin; + struct sockaddr_in6 rod_sin6; + } ro_dst; +#define ro_dstsa ro_dst.rod_sa +#define ro_dstsin ro_dst.rod_sin +#define ro_dstsin6 ro_dst.rod_sin6 }; -struct rt_addrinfo { - int rti_addrs; - const struct sockaddr *rti_info[RTAX_MAX]; - int rti_flags; - struct ifaddr *rti_ifa; - struct rt_msghdr *rti_rtm; - u_char rti_mpls; -}; +#endif /* __BSD_VISIBLE */ #ifdef _KERNEL @@ -449,6 +462,8 @@ struct if_ieee80211_data; struct bfd_config; void route_init(void); +int route_cache(struct route *, struct in_addr, u_int); +int route6_cache(struct route *, const struct in6_addr *, u_int); void rtm_ifchg(struct ifnet *); void rtm_ifannounce(struct ifnet *, int); void rtm_bfd(struct bfd_config *); diff --git a/sys/netinet/if_ether.h b/sys/netinet/if_ether.h index ed28944e7..4f5edd31b 100644 --- a/sys/netinet/if_ether.h +++ b/sys/netinet/if_ether.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_ether.h,v 1.90 2023/07/27 20:21:25 jan Exp $ */ +/* $OpenBSD: if_ether.h,v 1.91 2024/02/13 13:58:19 bluhm Exp $ */ /* $NetBSD: if_ether.h,v 1.22 1996/05/11 13:00:00 mycroft Exp $ */ /* @@ -307,6 +307,9 @@ struct ether_extracted { struct ip6_hdr *ip6; struct tcphdr *tcp; struct udphdr *udp; + u_int ip4hlen; + u_int tcphlen; + u_int paylen; }; void ether_extract_headers(struct mbuf *, struct ether_extracted *); diff --git a/sys/netinet/in.h b/sys/netinet/in.h index 7b75fe8a7..f3fd58551 100644 --- a/sys/netinet/in.h +++ b/sys/netinet/in.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in.h,v 1.147 2024/02/09 14:02:11 bluhm Exp $ */ +/* $OpenBSD: in.h,v 1.148 2024/02/13 12:22:09 bluhm Exp $ */ /* $NetBSD: in.h,v 1.20 1996/02/13 23:41:47 christos Exp $ */ /* @@ -789,8 +789,6 @@ void in_len2mask(struct in_addr *, int); int in_nam2sin(const struct mbuf *, struct sockaddr_in **); int in_sa2sin(struct sockaddr *, struct sockaddr_in **); -int route_cache(struct route *, struct in_addr, u_int); - char *inet_ntoa(struct in_addr); int inet_nat64(int, const void *, void *, const void *, u_int8_t); int inet_nat46(int, const void *, void *, const void *, u_int8_t); diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index 4a600b717..87241c03e 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in_pcb.c,v 1.292 2024/02/11 01:27:45 bluhm Exp $ */ +/* $OpenBSD: in_pcb.c,v 1.293 2024/02/13 12:22:09 bluhm Exp $ */ /* $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $ */ /* @@ -920,7 +920,7 @@ in_pcbrtentry(struct inpcb *inp) if (inp->inp_faddr.s_addr == INADDR_ANY) return (NULL); if (route_cache(ro, inp->inp_faddr, inp->inp_rtableid)) { - ro->ro_rt = rtalloc_mpath(&ro->ro_dst, + ro->ro_rt = rtalloc_mpath(&ro->ro_dstsa, &inp->inp_laddr.s_addr, ro->ro_tableid); } return (ro->ro_rt); @@ -984,7 +984,7 @@ in_pcbselsrc(struct in_addr *insrc, struct sockaddr_in *sin, */ if (route_cache(ro, sin->sin_addr, rtableid)) { /* No route yet, so try to acquire one */ - ro->ro_rt = rtalloc_mpath(&ro->ro_dst, NULL, ro->ro_tableid); + ro->ro_rt = rtalloc_mpath(&ro->ro_dstsa, NULL, ro->ro_tableid); } /* diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index 6b79f014a..1d67469c6 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in_pcb.h,v 1.151 2024/02/11 01:27:45 bluhm Exp $ */ +/* $OpenBSD: in_pcb.h,v 1.152 2024/02/13 12:22:09 bluhm Exp $ */ /* $NetBSD: in_pcb.h,v 1.14 1996/02/13 23:42:00 christos Exp $ */ /* @@ -150,12 +150,7 @@ struct inpcb { u_int16_t inp_lport; /* [t] local port */ struct socket *inp_socket; /* [I] back pointer to socket */ caddr_t inp_ppcb; /* pointer to per-protocol pcb */ - union { /* Route (notice increased size). */ - struct route ru_route; - struct route_in6 ru_route6; - } inp_ru; -#define inp_route inp_ru.ru_route -#define inp_route6 inp_ru.ru_route6 + struct route inp_route; /* cached route */ struct refcnt inp_refcnt; /* refcount PCB, delay memory free */ struct mutex inp_mtx; /* protect PCB and socket members */ int inp_flags; /* generic IP/datagram flags */ diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c index f2aee3e42..41e9d34ce 100644 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_carp.c,v 1.360 2023/12/23 10:52:54 bluhm Exp $ */ +/* $OpenBSD: ip_carp.c,v 1.361 2024/02/13 12:22:09 bluhm Exp $ */ /* * Copyright (c) 2002 Michael Shalayeff. All rights reserved. @@ -54,6 +54,7 @@ #include #include #include +#include #include diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index 6bbbcf63c..60c6e6a42 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_input.c,v 1.388 2024/01/31 14:56:42 bluhm Exp $ */ +/* $OpenBSD: ip_input.c,v 1.389 2024/02/13 12:22:09 bluhm Exp $ */ /* $NetBSD: ip_input.c,v 1.30 1996/03/16 23:53:58 christos Exp $ */ /* @@ -1494,7 +1494,7 @@ ip_forward(struct mbuf *m, struct ifnet *ifp, struct rtentry *rt, int srcrt) route_cache(&ro, ip->ip_dst, m->m_pkthdr.ph_rtableid); if (!rtisvalid(rt)) { rtfree(rt); - rt = rtalloc_mpath(&ro.ro_dst, &ip->ip_src.s_addr, + rt = rtalloc_mpath(&ro.ro_dstsa, &ip->ip_src.s_addr, m->m_pkthdr.ph_rtableid); if (rt == NULL) { ipstat_inc(ips_noroute); diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index 98d0cd54f..561b690c7 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_output.c,v 1.394 2024/01/31 14:56:43 bluhm Exp $ */ +/* $OpenBSD: ip_output.c,v 1.395 2024/02/13 12:22:09 bluhm Exp $ */ /* $NetBSD: ip_output.c,v 1.28 1996/02/13 23:43:07 christos Exp $ */ /* @@ -167,7 +167,7 @@ reroute: * destination and is still up. If not, free it and try again. */ route_cache(ro, ip->ip_dst, m->m_pkthdr.ph_rtableid); - dst = satosin(&ro->ro_dst); + dst = &ro->ro_dstsin; if ((IN_MULTICAST(ip->ip_dst.s_addr) || (ip->ip_dst.s_addr == INADDR_BROADCAST)) && @@ -185,7 +185,7 @@ reroute: struct in_ifaddr *ia; if (ro->ro_rt == NULL) - ro->ro_rt = rtalloc_mpath(&ro->ro_dst, + ro->ro_rt = rtalloc_mpath(&ro->ro_dstsa, &ip->ip_src.s_addr, ro->ro_tableid); if (ro->ro_rt == NULL) { @@ -253,7 +253,7 @@ reroute: * still points to the address in "ro". (It may have been * changed to point to a gateway address, above.) */ - dst = satosin(&ro->ro_dst); + dst = &ro->ro_dstsin; /* * See if the caller provided any multicast options @@ -455,7 +455,7 @@ sendit: rtfree(ro->ro_rt); ro->ro_tableid = orig_rtableid; ro->ro_rt = icmp_mtudisc_clone( - satosin(&ro->ro_dst)->sin_addr, ro->ro_tableid, 0); + ro->ro_dstsin.sin_addr, ro->ro_tableid, 0); } #endif /* @@ -558,7 +558,8 @@ ip_output_ipsec_pmtu_update(struct tdb *tdb, struct route *ro, rt->rt_mtu = tdb->tdb_mtu; if (ro != NULL && ro->ro_rt != NULL) { rtfree(ro->ro_rt); - ro->ro_rt = rtalloc(&ro->ro_dst, RT_RESOLVE, rtableid); + ro->ro_rt = rtalloc(&ro->ro_dstsa, RT_RESOLVE, + rtableid); } if (rt_mtucloned) rtfree(rt); diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h index cbe670b7f..b0e6ba891 100644 --- a/sys/netinet/ip_var.h +++ b/sys/netinet/ip_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_var.h,v 1.112 2024/02/05 23:16:39 bluhm Exp $ */ +/* $OpenBSD: ip_var.h,v 1.113 2024/02/13 12:22:09 bluhm Exp $ */ /* $NetBSD: ip_var.h,v 1.16 1996/02/13 23:43:20 christos Exp $ */ /* @@ -227,6 +227,7 @@ extern const struct pr_usrreqs rip_usrreqs; extern struct rttimer_queue ip_mtudisc_timeout_q; extern struct pool ipqent_pool; +struct rtentry; struct route; struct inpcb; diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index ec593cc28..a828508b1 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_input.c,v 1.400 2024/02/11 01:27:45 bluhm Exp $ */ +/* $OpenBSD: tcp_input.c,v 1.401 2024/02/13 12:22:09 bluhm Exp $ */ /* $NetBSD: tcp_input.c,v 1.23 1996/02/13 23:43:44 christos Exp $ */ /* @@ -145,8 +145,8 @@ struct timeval tcp_ackdrop_ppslim_last; #define ND6_HINT(tp) \ do { \ if (tp && tp->t_inpcb && (tp->t_inpcb->inp_flags & INP_IPV6) && \ - rtisvalid(tp->t_inpcb->inp_route6.ro_rt)) { \ - nd6_nud_hint(tp->t_inpcb->inp_route6.ro_rt); \ + rtisvalid(tp->t_inpcb->inp_route.ro_rt)) { \ + nd6_nud_hint(tp->t_inpcb->inp_route.ro_rt); \ } \ } while (0) #else @@ -3166,7 +3166,7 @@ syn_cache_put(struct syn_cache *sc) /* Dealing with last reference, no lock needed. */ m_free(sc->sc_ipopts); - rtfree(sc->sc_route4.ro_rt); + rtfree(sc->sc_route.ro_rt); pool_put(&syn_cache_pool, sc); } @@ -3578,13 +3578,8 @@ syn_cache_get(struct sockaddr *src, struct sockaddr *dst, struct tcphdr *th, /* * Give the new socket our cached route reference. */ - if (src->sa_family == AF_INET) - inp->inp_route = sc->sc_route4; /* struct assignment */ -#ifdef INET6 - else - inp->inp_route6 = sc->sc_route6; -#endif - sc->sc_route4.ro_rt = NULL; + inp->inp_route = sc->sc_route; /* struct assignment */ + sc->sc_route.ro_rt = NULL; am = m_get(M_DONTWAIT, MT_SONAME); /* XXX */ if (am == NULL) @@ -4152,7 +4147,7 @@ syn_cache_respond(struct syn_cache *sc, struct mbuf *m, uint64_t now) if (inp != NULL) ip->ip_tos = inp->inp_ip.ip_tos; - error = ip_output(m, sc->sc_ipopts, &sc->sc_route4, + error = ip_output(m, sc->sc_ipopts, &sc->sc_route, (ip_mtudisc ? IP_MTUDISC : 0), NULL, inp ? inp->inp_seclevel : NULL, 0); break; @@ -4164,7 +4159,7 @@ syn_cache_respond(struct syn_cache *sc, struct mbuf *m, uint64_t now) ip6->ip6_hlim = in6_selecthlim(inp); /* leave flowlabel = 0, it is legal and require no state mgmt */ - error = ip6_output(m, NULL /*XXX*/, &sc->sc_route6, 0, + error = ip6_output(m, NULL /*XXX*/, &sc->sc_route, 0, NULL, inp ? inp->inp_seclevel : NULL); break; #endif diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c index c89b3a8cf..cd21dfe9c 100644 --- a/sys/netinet/tcp_output.c +++ b/sys/netinet/tcp_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_output.c,v 1.142 2024/02/11 01:27:45 bluhm Exp $ */ +/* $OpenBSD: tcp_output.c,v 1.143 2024/02/13 12:22:09 bluhm Exp $ */ /* $NetBSD: tcp_output.c,v 1.16 1997/06/03 16:17:09 kml Exp $ */ /* @@ -1109,7 +1109,7 @@ send: #endif } error = ip6_output(m, tp->t_inpcb->inp_outputopts6, - &tp->t_inpcb->inp_route6, 0, NULL, + &tp->t_inpcb->inp_route, 0, NULL, tp->t_inpcb->inp_seclevel); break; #endif /* INET6 */ diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index 0fa13da4b..e8256d042 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_subr.c,v 1.198 2024/02/11 01:27:45 bluhm Exp $ */ +/* $OpenBSD: tcp_subr.c,v 1.199 2024/02/13 12:22:09 bluhm Exp $ */ /* $NetBSD: tcp_subr.c,v 1.22 1996/02/13 23:44:00 christos Exp $ */ /* @@ -401,7 +401,7 @@ tcp_respond(struct tcpcb *tp, caddr_t template, struct tcphdr *th0, ip6->ip6_plen = tlen - sizeof(struct ip6_hdr); ip6->ip6_plen = htons(ip6->ip6_plen); ip6_output(m, tp ? tp->t_inpcb->inp_outputopts6 : NULL, - tp ? &tp->t_inpcb->inp_route6 : NULL, + tp ? &tp->t_inpcb->inp_route : NULL, 0, NULL, tp ? tp->t_inpcb->inp_seclevel : NULL); break; diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h index 0c5263f27..f96f39eef 100644 --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_var.h,v 1.175 2024/01/27 21:13:46 bluhm Exp $ */ +/* $OpenBSD: tcp_var.h,v 1.176 2024/02/13 12:22:09 bluhm Exp $ */ /* $NetBSD: tcp_var.h,v 1.17 1996/02/13 23:44:24 christos Exp $ */ /* @@ -247,16 +247,7 @@ struct syn_cache { TAILQ_ENTRY(syn_cache) sc_bucketq; /* [S] link on bucket list */ struct refcnt sc_refcnt; /* ref count list and timer */ struct timeout sc_timer; /* rexmt timer */ - union { /* cached route */ - struct route route4; -#ifdef INET6 - struct route_in6 route6; -#endif - } sc_route_u; -#define sc_route4 sc_route_u.route4 /* [N] */ -#ifdef INET6 -#define sc_route6 sc_route_u.route6 /* [N] */ -#endif + struct route sc_route; /* [N] cached route */ long sc_win; /* [I] advertised window */ struct syn_cache_head *sc_buckethead; /* [S] our bucket index */ struct syn_cache_set *sc_set; /* [S] our syn cache set */ diff --git a/sys/netinet6/dest6.c b/sys/netinet6/dest6.c index edd223395..32db8b9ed 100644 --- a/sys/netinet6/dest6.c +++ b/sys/netinet6/dest6.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dest6.c,v 1.19 2022/06/29 22:45:24 bluhm Exp $ */ +/* $OpenBSD: dest6.c,v 1.20 2024/02/13 12:22:09 bluhm Exp $ */ /* $KAME: dest6.c,v 1.25 2001/02/22 01:39:16 itojun Exp $ */ /* @@ -38,6 +38,8 @@ #include #include +#include + #include #include #include diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h index da0256128..642a24b19 100644 --- a/sys/netinet6/in6.h +++ b/sys/netinet6/in6.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in6.h,v 1.115 2024/02/09 14:02:12 bluhm Exp $ */ +/* $OpenBSD: in6.h,v 1.116 2024/02/13 12:22:09 bluhm Exp $ */ /* $KAME: in6.h,v 1.83 2001/03/29 02:55:07 jinmei Exp $ */ /* @@ -144,16 +144,6 @@ extern const struct in6_addr in6addr_linklocal_allnodes; extern const struct in6_addr in6addr_linklocal_allrouters; #if __BSD_VISIBLE -/* - * IPv6 route structure, keep fields in sync with struct route - */ -struct route_in6 { - struct rtentry *ro_rt; - u_long ro_generation; - u_long ro_tableid; /* padded to long for alignment */ - struct sockaddr_in6 ro_dst; -}; - /* * Definition of some useful macros to handle IP6 addresses */ @@ -428,8 +418,6 @@ int in6_mask2len(struct in6_addr *, u_char *); int in6_nam2sin6(const struct mbuf *, struct sockaddr_in6 **); int in6_sa2sin6(struct sockaddr *, struct sockaddr_in6 **); -int route6_cache(struct route_in6 *, const struct in6_addr *, u_int); - struct ip6_pktopts; struct ip6_moptions; diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index caa902a11..d622c5dcc 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in6_pcb.c,v 1.137 2024/02/11 01:27:45 bluhm Exp $ */ +/* $OpenBSD: in6_pcb.c,v 1.138 2024/02/13 12:22:09 bluhm Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -114,13 +114,12 @@ #include #include +#include #include #include #include #include -#include - #if NSTOEPLITZ > 0 #include #endif @@ -517,13 +516,10 @@ in6_pcbnotify(struct inpcbtable *table, const struct sockaddr_in6 *dst, if ((PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) && IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) && inp->inp_route.ro_rt && - !(inp->inp_route.ro_rt->rt_flags & RTF_HOST)) { - struct sockaddr_in6 *dst6; - - dst6 = satosin6(&inp->inp_route.ro_dst); - if (IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, - &dst->sin6_addr)) - goto do_notify; + !(inp->inp_route.ro_rt->rt_flags & RTF_HOST) && + IN6_ARE_ADDR_EQUAL(&inp->inp_route.ro_dstsin6.sin6_addr, + &dst->sin6_addr)) { + goto do_notify; } /* @@ -565,12 +561,12 @@ in6_pcbnotify(struct inpcbtable *table, const struct sockaddr_in6 *dst, struct rtentry * in6_pcbrtentry(struct inpcb *inp) { - struct route_in6 *ro = &inp->inp_route6; + struct route *ro = &inp->inp_route; if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6)) return (NULL); if (route6_cache(ro, &inp->inp_faddr6, inp->inp_rtableid)) { - ro->ro_rt = rtalloc_mpath(sin6tosa(&ro->ro_dst), + ro->ro_rt = rtalloc_mpath(&ro->ro_dstsa, &inp->inp_laddr6.s6_addr32[0], ro->ro_tableid); } return (ro->ro_rt); diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c index d34af5065..470ad67eb 100644 --- a/sys/netinet6/in6_src.c +++ b/sys/netinet6/in6_src.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in6_src.c,v 1.93 2024/02/09 14:02:12 bluhm Exp $ */ +/* $OpenBSD: in6_src.c,v 1.94 2024/02/13 12:22:09 bluhm Exp $ */ /* $KAME: in6_src.c,v 1.36 2001/02/06 04:08:17 itojun Exp $ */ /* @@ -83,7 +83,7 @@ #include int in6_selectif(const struct in6_addr *, struct ip6_pktopts *, - struct ip6_moptions *, struct route_in6 *, struct ifnet **, u_int); + struct ip6_moptions *, struct route *, struct ifnet **, u_int); /* * Return an IPv6 address, which is the most appropriate for a given @@ -95,7 +95,7 @@ in6_pcbselsrc(const struct in6_addr **in6src, struct sockaddr_in6 *dstsock, struct inpcb *inp, struct ip6_pktopts *opts) { struct ip6_moptions *mopts = inp->inp_moptions6; - struct route_in6 *ro = &inp->inp_route6; + struct route *ro = &inp->inp_route; const struct in6_addr *laddr = &inp->inp_laddr6; u_int rtableid = inp->inp_rtableid; struct ifnet *ifp = NULL; @@ -180,8 +180,7 @@ in6_pcbselsrc(const struct in6_addr **in6src, struct sockaddr_in6 *dstsock, * our src addr is taken from the i/f, else punt. */ if (route6_cache(ro, dst, rtableid)) { - ro->ro_rt = rtalloc(sin6tosa(&ro->ro_dst), - RT_RESOLVE, ro->ro_tableid); + ro->ro_rt = rtalloc(&ro->ro_dstsa, RT_RESOLVE, ro->ro_tableid); } /* @@ -298,7 +297,7 @@ in6_selectsrc(const struct in6_addr **in6src, struct sockaddr_in6 *dstsock, struct rtentry * in6_selectroute(const struct in6_addr *dst, struct ip6_pktopts *opts, - struct route_in6 *ro, unsigned int rtableid) + struct route *ro, unsigned int rtableid) { /* * Use a cached route if it exists and is valid, else try to allocate @@ -307,8 +306,8 @@ in6_selectroute(const struct in6_addr *dst, struct ip6_pktopts *opts, if (ro) { if (route6_cache(ro, dst, rtableid)) { /* No route yet, so try to acquire one */ - ro->ro_rt = rtalloc_mpath(sin6tosa(&ro->ro_dst), - NULL, ro->ro_tableid); + ro->ro_rt = rtalloc_mpath(&ro->ro_dstsa, NULL, + ro->ro_tableid); } /* @@ -336,7 +335,7 @@ in6_selectroute(const struct in6_addr *dst, struct ip6_pktopts *opts, int in6_selectif(const struct in6_addr *dst, struct ip6_pktopts *opts, - struct ip6_moptions *mopts, struct route_in6 *ro, struct ifnet **retifp, + struct ip6_moptions *mopts, struct route *ro, struct ifnet **retifp, u_int rtableid) { struct rtentry *rt = NULL; diff --git a/sys/netinet6/ip6_divert.c b/sys/netinet6/ip6_divert.c index c7b3e3ed7..0bd114ba3 100644 --- a/sys/netinet6/ip6_divert.c +++ b/sys/netinet6/ip6_divert.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_divert.c,v 1.94 2024/02/11 18:14:27 mvs Exp $ */ +/* $OpenBSD: ip6_divert.c,v 1.95 2024/02/13 12:22:09 bluhm Exp $ */ /* * Copyright (c) 2009 Michele Marchetto @@ -30,13 +30,13 @@ #include #include +#include #include #include -#include -#include #include #include -#include +#include +#include #include #include #include @@ -180,7 +180,7 @@ divert6_output(struct inpcb *inp, struct mbuf *m, struct mbuf *nam, } else { m->m_pkthdr.ph_rtableid = inp->inp_rtableid; - error = ip6_output(m, NULL, &inp->inp_route6, + error = ip6_output(m, NULL, &inp->inp_route, IP_ALLOWBROADCAST | IP_RAWOUTPUT, NULL, NULL); } diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c index 54b7615f5..cdb2fb417 100644 --- a/sys/netinet6/ip6_forward.c +++ b/sys/netinet6/ip6_forward.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_forward.c,v 1.113 2024/02/07 23:40:40 bluhm Exp $ */ +/* $OpenBSD: ip6_forward.c,v 1.114 2024/02/13 12:22:09 bluhm Exp $ */ /* $KAME: ip6_forward.c,v 1.75 2001/06/29 12:42:13 jinmei Exp $ */ /* @@ -86,7 +86,7 @@ ip6_forward(struct mbuf *m, struct rtentry *rt, int srcrt) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct sockaddr *dst; - struct route_in6 ro; + struct route ro; struct ifnet *ifp = NULL; int error = 0, type = 0, code = 0, destmtu = 0; struct mbuf *mcopy = NULL; @@ -167,7 +167,7 @@ reroute: ro.ro_rt = NULL; route6_cache(&ro, &ip6->ip6_dst, m->m_pkthdr.ph_rtableid); - dst = sin6tosa(&ro.ro_dst); + dst = &ro.ro_dstsa; if (!rtisvalid(rt)) { rtfree(rt); rt = rtalloc_mpath(dst, &ip6->ip6_src.s6_addr32[0], @@ -253,7 +253,7 @@ reroute: ip6_sendredirects && (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) { if ((ifp->if_flags & IFF_POINTOPOINT) && - nd6_is_addr_neighbor(&ro.ro_dst, ifp)) { + nd6_is_addr_neighbor(&ro.ro_dstsin6, ifp)) { /* * If the incoming interface is equal to the outgoing * one, the link attached to the interface is diff --git a/sys/netinet6/ip6_id.c b/sys/netinet6/ip6_id.c index dd0b172c7..7fa5a9fde 100644 --- a/sys/netinet6/ip6_id.c +++ b/sys/netinet6/ip6_id.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_id.c,v 1.16 2021/03/10 10:21:49 jsg Exp $ */ +/* $OpenBSD: ip6_id.c,v 1.17 2024/02/13 12:22:09 bluhm Exp $ */ /* $NetBSD: ip6_id.c,v 1.7 2003/09/13 21:32:59 itojun Exp $ */ /* $KAME: ip6_id.c,v 1.8 2003/09/06 13:41:06 itojun Exp $ */ @@ -89,7 +89,6 @@ #include #include -#include struct randomtab { const int ru_bits; /* resulting bits */ diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index 3efd26129..adb5f3055 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_output.c,v 1.285 2024/02/07 23:40:40 bluhm Exp $ */ +/* $OpenBSD: ip6_output.c,v 1.286 2024/02/13 12:22:09 bluhm Exp $ */ /* $KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $ */ /* @@ -143,7 +143,7 @@ static __inline u_int16_t __attribute__((__unused__)) u_int32_t, u_int32_t); void in6_delayed_cksum(struct mbuf *, u_int8_t); -int ip6_output_ipsec_pmtu_update(struct tdb *, struct route_in6 *, +int ip6_output_ipsec_pmtu_update(struct tdb *, struct route *, struct in6_addr *, int, int, int); /* Context for non-repeating IDs */ @@ -160,14 +160,14 @@ struct idgen32_ctx ip6_id_ctx; * We use u_long to hold largest one, * which is rt_mtu. */ int -ip6_output(struct mbuf *m, struct ip6_pktopts *opt, struct route_in6 *ro, +ip6_output(struct mbuf *m, struct ip6_pktopts *opt, struct route *ro, int flags, struct ip6_moptions *im6o, const u_char seclevel[]) { struct ip6_hdr *ip6; struct ifnet *ifp = NULL; struct mbuf_list ml; int hlen, tlen; - struct route_in6 ip6route; + struct route iproute; struct rtentry *rt = NULL; struct sockaddr_in6 *dst; int error = 0; @@ -177,7 +177,7 @@ ip6_output(struct mbuf *m, struct ip6_pktopts *opt, struct route_in6 *ro, u_int32_t optlen = 0, plen = 0, unfragpartlen = 0; struct ip6_exthdrs exthdrs; struct in6_addr finaldst; - struct route_in6 *ro_pmtu = NULL; + struct route *ro_pmtu = NULL; int hdrsplit = 0; u_int8_t sproto = 0; u_char nextproto; @@ -390,13 +390,13 @@ reroute: /* initialize cached route */ if (ro == NULL) { - ro = &ip6route; + ro = &iproute; bzero((caddr_t)ro, sizeof(*ro)); } ro_pmtu = ro; if (opt && opt->ip6po_rthdr) ro = &opt->ip6po_route; - dst = &ro->ro_dst; + dst = &ro->ro_dstsin6; /* * if specified, try to fill in the traffic class field. @@ -750,9 +750,9 @@ reroute: ip6stat_inc(ip6s_fragmented); done: - if (ro == &ip6route && ro->ro_rt) { + if (ro == &iproute && ro->ro_rt) { rtfree(ro->ro_rt); - } else if (ro_pmtu == &ip6route && ro_pmtu->ro_rt) { + } else if (ro_pmtu == &iproute && ro_pmtu->ro_rt) { rtfree(ro_pmtu->ro_rt); } if_put(ifp); @@ -2772,7 +2772,7 @@ ip6_output_ipsec_lookup(struct mbuf *m, const u_char seclevel[], } int -ip6_output_ipsec_pmtu_update(struct tdb *tdb, struct route_in6 *ro, +ip6_output_ipsec_pmtu_update(struct tdb *tdb, struct route *ro, struct in6_addr *dst, int ifidx, int rtableid, int transportmode) { struct rtentry *rt = NULL; @@ -2807,7 +2807,7 @@ ip6_output_ipsec_pmtu_update(struct tdb *tdb, struct route_in6 *ro, rt->rt_mtu = tdb->tdb_mtu; if (ro != NULL && ro->ro_rt != NULL) { rtfree(ro->ro_rt); - ro->ro_rt = rtalloc(sin6tosa(&ro->ro_dst), RT_RESOLVE, + ro->ro_rt = rtalloc(&ro->ro_dstsa, RT_RESOLVE, rtableid); } if (rt_mtucloned) @@ -2817,7 +2817,7 @@ ip6_output_ipsec_pmtu_update(struct tdb *tdb, struct route_in6 *ro, } int -ip6_output_ipsec_send(struct tdb *tdb, struct mbuf *m, struct route_in6 *ro, +ip6_output_ipsec_send(struct tdb *tdb, struct mbuf *m, struct route *ro, int tunalready, int fwd) { struct mbuf_list ml; diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h index f23287b6f..f4c068dc0 100644 --- a/sys/netinet6/ip6_var.h +++ b/sys/netinet6/ip6_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_var.h,v 1.112 2024/02/07 23:40:40 bluhm Exp $ */ +/* $OpenBSD: ip6_var.h,v 1.113 2024/02/13 12:22:09 bluhm Exp $ */ /* $KAME: ip6_var.h,v 1.33 2000/06/11 14:59:20 jinmei Exp $ */ /* @@ -103,7 +103,7 @@ struct ip6_moptions { /* Routing header related info */ struct ip6po_rhinfo { struct ip6_rthdr *ip6po_rhi_rthdr; /* Routing header */ - struct route_in6 ip6po_rhi_route; /* Route to the 1st hop */ + struct route ip6po_rhi_route; /* Route to the 1st hop */ }; #define ip6po_rthdr ip6po_rhinfo.ip6po_rhi_rthdr #define ip6po_route ip6po_rhinfo.ip6po_rhi_route @@ -323,7 +323,7 @@ int ip6_sysctl(int *, u_int, void *, size_t *, void *, size_t); void ip6_forward(struct mbuf *, struct rtentry *, int); void ip6_mloopback(struct ifnet *, struct mbuf *, struct sockaddr_in6 *); -int ip6_output(struct mbuf *, struct ip6_pktopts *, struct route_in6 *, int, +int ip6_output(struct mbuf *, struct ip6_pktopts *, struct route *, int, struct ip6_moptions *, const u_char[]); int ip6_fragment(struct mbuf *, struct mbuf_list *, int, u_char, u_long); int ip6_ctloutput(int, struct socket *, int, int, struct mbuf *); @@ -370,14 +370,14 @@ int in6_pcbselsrc(const struct in6_addr **, struct sockaddr_in6 *, int in6_selectsrc(const struct in6_addr **, struct sockaddr_in6 *, struct ip6_moptions *, unsigned int); struct rtentry *in6_selectroute(const struct in6_addr *, struct ip6_pktopts *, - struct route_in6 *, unsigned int rtableid); + struct route *, unsigned int rtableid); u_int32_t ip6_randomflowlabel(void); #ifdef IPSEC struct tdb; int ip6_output_ipsec_lookup(struct mbuf *, const u_char[], struct tdb **); -int ip6_output_ipsec_send(struct tdb *, struct mbuf *, struct route_in6 *, +int ip6_output_ipsec_send(struct tdb *, struct mbuf *, struct route *, int, int); #endif /* IPSEC */ diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c index f05224db1..0c5b49e02 100644 --- a/sys/netinet6/mld6.c +++ b/sys/netinet6/mld6.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mld6.c,v 1.61 2022/09/08 10:22:07 kn Exp $ */ +/* $OpenBSD: mld6.c,v 1.62 2024/02/13 12:22:09 bluhm Exp $ */ /* $KAME: mld6.c,v 1.26 2001/02/16 14:50:35 itojun Exp $ */ /* @@ -74,6 +74,7 @@ #include #include +#include #include #include diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c index 653c30056..177eeb8e8 100644 --- a/sys/netinet6/raw_ip6.c +++ b/sys/netinet6/raw_ip6.c @@ -1,4 +1,4 @@ -/* $OpenBSD: raw_ip6.c,v 1.181 2024/02/11 18:14:27 mvs Exp $ */ +/* $OpenBSD: raw_ip6.c,v 1.182 2024/02/13 12:22:09 bluhm Exp $ */ /* $KAME: raw_ip6.c,v 1.69 2001/03/04 15:55:44 itojun Exp $ */ /* @@ -512,7 +512,7 @@ rip6_output(struct mbuf *m, struct socket *so, struct sockaddr *dstaddr, pf_mbuf_link_inpcb(m, inp); #endif - error = ip6_output(m, optp, &inp->inp_route6, flags, + error = ip6_output(m, optp, &inp->inp_route, flags, inp->inp_moptions6, inp->inp_seclevel); if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { icmp6stat_inc(icp6s_outhist + type); diff --git a/sys/netinet6/route6.c b/sys/netinet6/route6.c index 914059abc..f07334d3f 100644 --- a/sys/netinet6/route6.c +++ b/sys/netinet6/route6.c @@ -1,4 +1,4 @@ -/* $OpenBSD: route6.c,v 1.21 2017/04/14 20:46:31 bluhm Exp $ */ +/* $OpenBSD: route6.c,v 1.22 2024/02/13 12:22:09 bluhm Exp $ */ /* $KAME: route6.c,v 1.22 2000/12/03 00:54:00 itojun Exp $ */ /* @@ -37,6 +37,7 @@ #include #include +#include #include #include diff --git a/sys/netinet6/udp6_output.c b/sys/netinet6/udp6_output.c index f655f445d..324092ce7 100644 --- a/sys/netinet6/udp6_output.c +++ b/sys/netinet6/udp6_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: udp6_output.c,v 1.63 2023/12/03 20:36:24 bluhm Exp $ */ +/* $OpenBSD: udp6_output.c,v 1.64 2024/02/13 12:22:09 bluhm Exp $ */ /* $KAME: udp6_output.c,v 1.21 2001/02/07 11:51:54 itojun Exp $ */ /* @@ -232,7 +232,7 @@ udp6_output(struct inpcb *inp, struct mbuf *m, struct mbuf *addr6, pf_mbuf_link_inpcb(m, inp); #endif - error = ip6_output(m, optp, &inp->inp_route6, + error = ip6_output(m, optp, &inp->inp_route, flags, inp->inp_moptions6, inp->inp_seclevel); goto releaseopt; diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h index f1922f6b6..9ef1cafe7 100644 --- a/sys/sys/socketvar.h +++ b/sys/sys/socketvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: socketvar.h,v 1.123 2024/02/11 18:14:27 mvs Exp $ */ +/* $OpenBSD: socketvar.h,v 1.124 2024/02/12 22:48:27 mvs Exp $ */ /* $NetBSD: socketvar.h,v 1.18 1996/02/09 18:25:38 christos Exp $ */ /*- @@ -375,7 +375,7 @@ int soconnect(struct socket *, struct mbuf *); int soconnect2(struct socket *, struct socket *); int socreate(int, struct socket **, int, int); int sodisconnect(struct socket *); -struct socket *soalloc(const struct domain *, int); +struct socket *soalloc(const struct protosw *, int); void sofree(struct socket *, int); int sogetopt(struct socket *, int, int, struct mbuf *); void sohasoutofband(struct socket *); diff --git a/sys/uvm/uvm_page.c b/sys/uvm/uvm_page.c index 91b24cc1e..179fd72c6 100644 --- a/sys/uvm/uvm_page.c +++ b/sys/uvm/uvm_page.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_page.c,v 1.173 2023/08/12 07:22:56 mpi Exp $ */ +/* $OpenBSD: uvm_page.c,v 1.174 2024/02/13 10:16:28 miod Exp $ */ /* $NetBSD: uvm_page.c,v 1.44 2000/11/27 08:40:04 chs Exp $ */ /* @@ -1024,10 +1024,6 @@ uvm_pageclean(struct vm_page *pg) void uvm_pagefree(struct vm_page *pg) { - if ((pg->pg_flags & (PG_TABLED|PQ_ACTIVE|PQ_INACTIVE)) && - (pg->uobject == NULL || !UVM_OBJ_IS_PMAP(pg->uobject))) - MUTEX_ASSERT_LOCKED(&uvm.pageqlock); - uvm_pageclean(pg); uvm_pmr_freepages(pg, 1); } diff --git a/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c index 0e711da4c..d1836a743 100644 --- a/usr.bin/netstat/inet.c +++ b/usr.bin/netstat/inet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: inet.c,v 1.180 2024/02/05 23:16:39 bluhm Exp $ */ +/* $OpenBSD: inet.c,v 1.181 2024/02/13 12:22:09 bluhm Exp $ */ /* $NetBSD: inet.c,v 1.14 1995/10/03 21:42:37 thorpej Exp $ */ /* @@ -1461,14 +1461,14 @@ inpcb_dump(u_long off, short protocol, int af) case AF_INET: inet_ntop(af, &inp.inp_faddr, faddr, sizeof(faddr)); inet_ntop(af, &inp.inp_laddr, laddr, sizeof(laddr)); - inet_ntop(af, &((struct sockaddr_in *) - (&inp.inp_route.ro_dst))->sin_addr, raddr, sizeof(raddr)); + inet_ntop(af, &inp.inp_route.ro_dstsin.sin_addr, raddr, + sizeof(raddr)); break; case AF_INET6: inet_ntop(af, &inp.inp_faddr6, faddr, sizeof(faddr)); inet_ntop(af, &inp.inp_laddr6, laddr, sizeof(laddr)); - inet_ntop(af, &inp.inp_route6.ro_dst.sin6_addr, - raddr, sizeof(raddr)); + inet_ntop(af, &inp.inp_route.ro_dstsin6.sin6_addr, raddr, + sizeof(raddr)); break; default: faddr[0] = laddr[0] = '\0'; diff --git a/usr.bin/tmux/cmd-paste-buffer.c b/usr.bin/tmux/cmd-paste-buffer.c index e26727bdf..6d2c3de39 100644 --- a/usr.bin/tmux/cmd-paste-buffer.c +++ b/usr.bin/tmux/cmd-paste-buffer.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-paste-buffer.c,v 1.41 2021/08/21 10:22:39 nicm Exp $ */ +/* $OpenBSD: cmd-paste-buffer.c,v 1.42 2024/02/13 08:03:50 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -55,6 +55,11 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item) size_t seplen, bufsize; int bracket = args_has(args, 'p'); + if (window_pane_exited(wp)) { + cmdq_error(item, "target pane has exited"); + return (CMD_RETURN_ERROR); + } + bufname = NULL; if (args_has(args, 'b')) bufname = args_get(args, 'b'); diff --git a/usr.bin/tmux/cmd-pipe-pane.c b/usr.bin/tmux/cmd-pipe-pane.c index 1ef733155..8a2174360 100644 --- a/usr.bin/tmux/cmd-pipe-pane.c +++ b/usr.bin/tmux/cmd-pipe-pane.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-pipe-pane.c,v 1.60 2022/05/30 13:03:46 nicm Exp $ */ +/* $OpenBSD: cmd-pipe-pane.c,v 1.61 2024/02/13 08:03:50 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -69,7 +69,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) sigset_t set, oldset; /* Do nothing if pane is dead. */ - if (wp->fd == -1 || (wp->flags & PANE_EXITED)) { + if (window_pane_exited(wp)) { cmdq_error(item, "target pane has exited"); return (CMD_RETURN_ERROR); } diff --git a/usr.bin/tmux/options-table.c b/usr.bin/tmux/options-table.c index 7f51a0a69..3dbc1b847 100644 --- a/usr.bin/tmux/options-table.c +++ b/usr.bin/tmux/options-table.c @@ -1,4 +1,4 @@ -/* $OpenBSD: options-table.c,v 1.168 2023/09/01 13:48:54 nicm Exp $ */ +/* $OpenBSD: options-table.c,v 1.169 2024/02/13 08:10:23 nicm Exp $ */ /* * Copyright (c) 2011 Nicholas Marriott @@ -85,6 +85,9 @@ static const char *options_table_window_size_list[] = { static const char *options_table_remain_on_exit_list[] = { "off", "on", "failed", NULL }; +static const char *options_table_destroy_unattached_list[] = { + "off", "on", "keep-last", "keep-group", NULL +}; static const char *options_table_detach_on_destroy_list[] = { "off", "on", "no-detached", "previous", "next", NULL }; @@ -484,11 +487,12 @@ const struct options_table_entry options_table[] = { }, { .name = "destroy-unattached", - .type = OPTIONS_TABLE_FLAG, + .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, + .choices = options_table_destroy_unattached_list, .default_num = 0, .text = "Whether to destroy sessions when they have no attached " - "clients." + "clients, or keep the last session whether in the group." }, { .name = "detach-on-destroy", diff --git a/usr.bin/tmux/proc.c b/usr.bin/tmux/proc.c index f0c7e547c..d001ba9c3 100644 --- a/usr.bin/tmux/proc.c +++ b/usr.bin/tmux/proc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: proc.c,v 1.23 2024/01/16 13:09:11 claudio Exp $ */ +/* $OpenBSD: proc.c,v 1.24 2024/02/13 08:10:23 nicm Exp $ */ /* * Copyright (c) 2015 Nicholas Marriott @@ -92,7 +92,7 @@ proc_event_cb(__unused int fd, short events, void *arg) log_debug("peer %p message %d", peer, imsg.hdr.type); if (peer_check_version(peer, &imsg) != 0) { - int fd = imsg_get_fd(&imsg); + fd = imsg_get_fd(&imsg); if (fd != -1) close(fd); imsg_free(&imsg); diff --git a/usr.bin/tmux/server-fn.c b/usr.bin/tmux/server-fn.c index 75acd1879..7ddb7e7d2 100644 --- a/usr.bin/tmux/server-fn.c +++ b/usr.bin/tmux/server-fn.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server-fn.c,v 1.134 2023/09/01 13:48:54 nicm Exp $ */ +/* $OpenBSD: server-fn.c,v 1.135 2024/02/13 08:10:23 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -454,7 +454,8 @@ server_destroy_session(struct session *s) void server_check_unattached(void) { - struct session *s; + struct session *s; + struct session_group *sg; /* * If any sessions are no longer attached and have destroy-unattached @@ -463,8 +464,23 @@ server_check_unattached(void) RB_FOREACH(s, sessions, &sessions) { if (s->attached != 0) continue; - if (options_get_number (s->options, "destroy-unattached")) - session_destroy(s, 1, __func__); + switch (options_get_number(s->options, "destroy-unattached")) { + case 0: /* off */ + continue; + case 1: /* on */ + break; + case 2: /* keep-last */ + sg = session_group_contains(s); + if (sg == NULL || session_group_count(sg) <= 1) + continue; + break; + case 3: /* keep-group */ + sg = session_group_contains(s); + if (sg != NULL && session_group_count(sg) == 1) + continue; + break; + } + session_destroy(s, 1, __func__); } } diff --git a/usr.bin/tmux/tmux.1 b/usr.bin/tmux/tmux.1 index bb690cbd5..43fbad273 100644 --- a/usr.bin/tmux/tmux.1 +++ b/usr.bin/tmux/tmux.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: tmux.1,v 1.935 2023/12/27 20:23:59 nicm Exp $ +.\" $OpenBSD: tmux.1,v 1.936 2024/02/13 08:10:23 nicm Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" @@ -14,7 +14,7 @@ .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: December 27 2023 $ +.Dd $Mdocdate: February 13 2024 $ .Dt TMUX 1 .Os .Sh NAME @@ -4023,10 +4023,20 @@ The value is the width and height separated by an character. The default is 80x24. .It Xo Ic destroy-unattached -.Op Ic on | off +.Op Ic off | on | keep-last | keep-group .Xc -If enabled and the session is no longer attached to any clients, it is -destroyed. +If +.Ic on , +destroy the session after the last client has detached. +If +.Ic off +(the default), leave the session orphaned. +If +.Ic keep-last , +destroy the session only if it is in a group and has other sessions in that group. +If +.Ic keep-group , +destroy the session unless it is in a group and is the only session in that group. .It Xo Ic detach-on-destroy .Op Ic off | on | no-detached | previous | next .Xc diff --git a/usr.bin/tmux/tmux.h b/usr.bin/tmux/tmux.h index 43bf574e5..105d18693 100644 --- a/usr.bin/tmux/tmux.h +++ b/usr.bin/tmux/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1211 2023/09/15 15:49:05 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.1212 2024/02/13 08:03:50 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -3047,6 +3047,7 @@ int window_pane_key(struct window_pane *, struct client *, struct session *, struct winlink *, key_code, struct mouse_event *); int window_pane_visible(struct window_pane *); +int window_pane_exited(struct window_pane *); u_int window_pane_search(struct window_pane *, const char *, int, int); const char *window_printable_flags(struct winlink *, int); diff --git a/usr.bin/tmux/window.c b/usr.bin/tmux/window.c index ef86f43b1..7454e5e23 100644 --- a/usr.bin/tmux/window.c +++ b/usr.bin/tmux/window.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window.c,v 1.287 2023/10/23 08:12:00 nicm Exp $ */ +/* $OpenBSD: window.c,v 1.288 2024/02/13 08:03:50 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1204,6 +1204,12 @@ window_pane_visible(struct window_pane *wp) return (wp == wp->window->active); } +int +window_pane_exited(struct window_pane *wp) +{ + return (wp->fd == -1 || (wp->flags & PANE_EXITED)); +} + u_int window_pane_search(struct window_pane *wp, const char *term, int regex, int ignore) diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h index fc31312d8..36a888f98 100644 --- a/usr.sbin/bgpd/bgpd.h +++ b/usr.sbin/bgpd/bgpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.h,v 1.484 2024/01/30 13:50:08 claudio Exp $ */ +/* $OpenBSD: bgpd.h,v 1.485 2024/02/13 16:35:43 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -197,14 +197,12 @@ struct bgpd_addr { struct in_addr v4; struct in6_addr v6; /* maximum size for a prefix is 256 bits */ - } ba; /* 128-bit address */ + }; /* 128-bit address */ uint64_t rd; /* route distinguisher for VPN addrs */ uint32_t scope_id; /* iface scope id for v6 */ uint8_t aid; uint8_t labellen; /* size of the labelstack */ uint8_t labelstack[18]; /* max that makes sense */ -#define v4 ba.v4 -#define v6 ba.v6 }; #define DEFAULT_LISTENER 0x01 diff --git a/usr.sbin/bgpd/pftable.c b/usr.sbin/bgpd/pftable.c index bb60269c3..f1fe60067 100644 --- a/usr.sbin/bgpd/pftable.c +++ b/usr.sbin/bgpd/pftable.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pftable.c,v 1.17 2022/08/17 15:15:26 claudio Exp $ */ +/* $OpenBSD: pftable.c,v 1.18 2024/02/13 16:35:43 claudio Exp $ */ /* * Copyright (c) 2004 Damien Miller @@ -31,12 +31,9 @@ #include "log.h" -/* Namespace collision: these are defined in both bgpd.h and pfvar.h */ +/* Namespace collision: these are defined in pfvar.h and bgpd.h */ #undef v4 #undef v6 -#undef addr8 -#undef addr16 -#undef addr32 #include "bgpd.h" @@ -216,7 +213,7 @@ pftable_add_work(const char *table, struct bgpd_addr *addr, pfa = &pft->worklist[pft->naddrs]; memset(pfa, 0, sizeof(*pfa)); - memcpy(&pfa->pfra_u, &addr->ba, (len + 7U) / 8); + memcpy(&pfa->pfra_u, &addr->v6, (len + 7U) / 8); pfa->pfra_af = aid2af(addr->aid); pfa->pfra_net = len; diff --git a/usr.sbin/bgpd/session.c b/usr.sbin/bgpd/session.c index b17952cde..be8e2fbf6 100644 --- a/usr.sbin/bgpd/session.c +++ b/usr.sbin/bgpd/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.461 2024/01/18 14:56:44 claudio Exp $ */ +/* $OpenBSD: session.c,v 1.462 2024/02/13 16:12:37 claudio Exp $ */ /* * Copyright (c) 2003, 2004, 2005 Henning Brauer @@ -3344,15 +3344,6 @@ session_dispatch_imsg(struct imsgbuf *imsgbuf, int idx, u_int *listener_cnt) "IMSG_SESSION_RESTARTED"); } break; - case IMSG_SESSION_DOWN: - if (idx != PFD_PIPE_ROUTE) - fatalx("session down not from RDE"); - if ((p = getpeerbyid(conf, peerid)) == NULL) { - log_warnx("no such peer: id=%u", peerid); - break; - } - session_stop(p, ERR_CEASE_ADMIN_DOWN); - break; default: break; } diff --git a/usr.sbin/httpd/server_http.c b/usr.sbin/httpd/server_http.c index 08deda7d5..0a814e7a6 100644 --- a/usr.sbin/httpd/server_http.c +++ b/usr.sbin/httpd/server_http.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server_http.c,v 1.153 2022/09/21 05:55:18 yasuoka Exp $ */ +/* $OpenBSD: server_http.c,v 1.154 2024/02/13 14:00:24 claudio Exp $ */ /* * Copyright (c) 2020 Matthias Pressfreund @@ -1762,13 +1762,14 @@ read_errdoc(const char *root, const char *file) struct stat sb; char *path; int fd; - char *ret = NULL; + char *ret; if (asprintf(&path, "%s/%s.html", root, file) == -1) fatal("asprintf"); if ((fd = open(path, O_RDONLY)) == -1) { free(path); - log_warn("%s: open", __func__); + if (errno != ENOENT) + log_warn("%s: open", __func__); return (NULL); } free(path); @@ -1788,8 +1789,7 @@ read_errdoc(const char *root, const char *file) log_warn("%s: read", __func__); close(fd); free(ret); - ret = NULL; - return (ret); + return (NULL); } close(fd); diff --git a/usr.sbin/unbound/services/authzone.c b/usr.sbin/unbound/services/authzone.c index 61493cc3b..024392d43 100644 --- a/usr.sbin/unbound/services/authzone.c +++ b/usr.sbin/unbound/services/authzone.c @@ -7771,6 +7771,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z, enum sec_status sec; struct val_env* ve; int m; + int verified = 0; m = modstack_find(mods, "validator"); if(m == -1) { auth_zone_log(z->name, VERB_ALGO, "zonemd dnssec verify: have " @@ -7794,7 +7795,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z, "zonemd: verify %s RRset with DNSKEY", typestr); } sec = dnskeyset_verify_rrset(env, ve, &pk, dnskey, sigalg, why_bogus, NULL, - LDNS_SECTION_ANSWER, NULL); + LDNS_SECTION_ANSWER, NULL, &verified); if(sec == sec_status_secure) { return 1; } diff --git a/usr.sbin/unbound/services/cache/dns.c b/usr.sbin/unbound/services/cache/dns.c index 58003ebe2..2c45edc08 100644 --- a/usr.sbin/unbound/services/cache/dns.c +++ b/usr.sbin/unbound/services/cache/dns.c @@ -690,6 +690,28 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, return msg; } +struct dns_msg* +dns_msg_deepcopy_region(struct dns_msg* origin, struct regional* region) +{ + size_t i; + struct dns_msg* res = NULL; + res = gen_dns_msg(region, &origin->qinfo, origin->rep->rrset_count); + if(!res) return NULL; + *res->rep = *origin->rep; + if(origin->rep->reason_bogus_str) { + res->rep->reason_bogus_str = regional_strdup(region, + origin->rep->reason_bogus_str); + } + for(i=0; irep->rrset_count; i++) { + res->rep->rrsets[i] = packed_rrset_copy_region( + origin->rep->rrsets[i], region, 0); + if(!res->rep->rrsets[i]) { + return NULL; + } + } + return res; +} + /** synthesize RRset-only response from cached RRset item */ static struct dns_msg* rrset_msg(struct ub_packed_rrset_key* rrset, struct regional* region, diff --git a/usr.sbin/unbound/services/cache/dns.h b/usr.sbin/unbound/services/cache/dns.h index 9b39aeb7f..3e0663126 100644 --- a/usr.sbin/unbound/services/cache/dns.h +++ b/usr.sbin/unbound/services/cache/dns.h @@ -164,6 +164,15 @@ struct dns_msg* tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, struct regional* region, time_t now, int allow_expired, struct regional* scratch); +/** + * Deep copy a dns_msg to a region. + * @param origin: the dns_msg to copy. + * @param region: the region to copy all the data to. + * @return the new dns_msg or NULL on malloc error. + */ +struct dns_msg* dns_msg_deepcopy_region(struct dns_msg* origin, + struct regional* region); + /** * Find cached message * @param env: module environment with the DNS cache. diff --git a/usr.sbin/unbound/testcode/unitverify.c b/usr.sbin/unbound/testcode/unitverify.c index 3376d3299..2a9bf2683 100644 --- a/usr.sbin/unbound/testcode/unitverify.c +++ b/usr.sbin/unbound/testcode/unitverify.c @@ -180,6 +180,7 @@ verifytest_rrset(struct module_env* env, struct val_env* ve, enum sec_status sec; char* reason = NULL; uint8_t sigalg[ALGO_NEEDS_MAX+1]; + int verified = 0; if(vsig) { log_nametypeclass(VERB_QUERY, "verify of rrset", rrset->rk.dname, ntohs(rrset->rk.type), @@ -188,7 +189,7 @@ verifytest_rrset(struct module_env* env, struct val_env* ve, setup_sigalg(dnskey, sigalg); /* check all algorithms in the dnskey */ /* ok to give null as qstate here, won't be used for answer section. */ sec = dnskeyset_verify_rrset(env, ve, rrset, dnskey, sigalg, &reason, NULL, - LDNS_SECTION_ANSWER, NULL); + LDNS_SECTION_ANSWER, NULL, &verified); if(vsig) { printf("verify outcome is: %s %s\n", sec_status_to_string(sec), reason?reason:""); @@ -442,9 +443,9 @@ nsec3_hash_test_entry(struct entry* e, rbtree_type* ct, ret = nsec3_hash_name(ct, region, buf, nsec3, 0, qname, qinfo.qname_len, &hash); - if(ret != 1) { + if(ret < 1) { printf("Bad nsec3_hash_name retcode %d\n", ret); - unit_assert(ret == 1); + unit_assert(ret == 1 || ret == 2); } unit_assert(hash->dname && hash->hash && hash->hash_len && hash->b32 && hash->b32_len); diff --git a/usr.sbin/unbound/util/fptr_wlist.c b/usr.sbin/unbound/util/fptr_wlist.c index 0ad3f6242..6c94d00fd 100644 --- a/usr.sbin/unbound/util/fptr_wlist.c +++ b/usr.sbin/unbound/util/fptr_wlist.c @@ -131,6 +131,7 @@ fptr_whitelist_comm_timer(void (*fptr)(void*)) else if(fptr == &pending_udp_timer_delay_cb) return 1; else if(fptr == &worker_stat_timer_cb) return 1; else if(fptr == &worker_probe_timer_cb) return 1; + else if(fptr == &validate_suspend_timer_cb) return 1; #ifdef UB_ON_WINDOWS else if(fptr == &wsvc_cron_cb) return 1; #endif diff --git a/usr.sbin/unbound/validator/val_nsec.c b/usr.sbin/unbound/validator/val_nsec.c index 9fa760acc..a3aa34ef5 100644 --- a/usr.sbin/unbound/validator/val_nsec.c +++ b/usr.sbin/unbound/validator/val_nsec.c @@ -181,6 +181,7 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve, { struct packed_rrset_data* d = (struct packed_rrset_data*) nsec->entry.data; + int verified = 0; if(!d) return 0; if(d->security == sec_status_secure) return 1; @@ -188,7 +189,7 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve, if(d->security == sec_status_secure) return 1; d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason, - reason_bogus, LDNS_SECTION_AUTHORITY, qstate); + reason_bogus, LDNS_SECTION_AUTHORITY, qstate, &verified); if(d->security == sec_status_secure) { rrset_update_sec_status(env->rrset_cache, nsec, *env->now); return 1; diff --git a/usr.sbin/unbound/validator/val_nsec3.c b/usr.sbin/unbound/validator/val_nsec3.c index c5726c3c7..895db19ac 100644 --- a/usr.sbin/unbound/validator/val_nsec3.c +++ b/usr.sbin/unbound/validator/val_nsec3.c @@ -57,6 +57,19 @@ /* we include nsec.h for the bitmap_has_type function */ #include "validator/val_nsec.h" #include "sldns/sbuffer.h" +#include "util/config_file.h" + +/** + * Max number of NSEC3 calculations at once, suspend query for later. + * 8 is low enough and allows for cases where multiple proofs are needed. + */ +#define MAX_NSEC3_CALCULATIONS 8 +/** + * When all allowed NSEC3 calculations at once resulted in error treat as + * bogus. NSEC3 hash errors are not cached and this helps breaks loops with + * erroneous data. + */ +#define MAX_NSEC3_ERRORS -1 /** * This function we get from ldns-compat or from base system @@ -532,6 +545,17 @@ nsec3_hash_cmp(const void* c1, const void* c2) return memcmp(s1, s2, s1len); } +int +nsec3_cache_table_init(struct nsec3_cache_table* ct, struct regional* region) +{ + if(ct->ct) return 1; + ct->ct = (rbtree_type*)regional_alloc(region, sizeof(*ct->ct)); + if(!ct->ct) return 0; + ct->region = region; + rbtree_init(ct->ct, &nsec3_hash_cmp); + return 1; +} + size_t nsec3_get_hashed(sldns_buffer* buf, uint8_t* nm, size_t nmlen, int algo, size_t iter, uint8_t* salt, size_t saltlen, uint8_t* res, size_t max) @@ -646,7 +670,7 @@ nsec3_hash_name(rbtree_type* table, struct regional* region, sldns_buffer* buf, c = (struct nsec3_cached_hash*)rbtree_search(table, &looki); if(c) { *hash = c; - return 1; + return 2; } /* create a new entry */ c = (struct nsec3_cached_hash*)regional_alloc(region, sizeof(*c)); @@ -658,10 +682,10 @@ nsec3_hash_name(rbtree_type* table, struct regional* region, sldns_buffer* buf, c->dname_len = dname_len; r = nsec3_calc_hash(region, buf, c); if(r != 1) - return r; + return r; /* returns -1 or 0 */ r = nsec3_calc_b32(region, buf, c); if(r != 1) - return r; + return r; /* returns 0 */ #ifdef UNBOUND_DEBUG n = #else @@ -704,6 +728,7 @@ nsec3_hash_matches_owner(struct nsec3_filter* flt, struct nsec3_cached_hash* hash, struct ub_packed_rrset_key* s) { uint8_t* nm = s->rk.dname; + if(!hash) return 0; /* please clang */ /* compare, does hash of name based on params in this NSEC3 * match the owner name of this NSEC3? * name must be: base32 . zone name @@ -730,34 +755,50 @@ nsec3_hash_matches_owner(struct nsec3_filter* flt, * @param nmlen: length of name. * @param rrset: nsec3 that matches is returned here. * @param rr: rr number in nsec3 rrset that matches. + * @param calculations: current hash calculations. * @return true if a matching NSEC3 is found, false if not. */ static int find_matching_nsec3(struct module_env* env, struct nsec3_filter* flt, - rbtree_type* ct, uint8_t* nm, size_t nmlen, - struct ub_packed_rrset_key** rrset, int* rr) + struct nsec3_cache_table* ct, uint8_t* nm, size_t nmlen, + struct ub_packed_rrset_key** rrset, int* rr, + int* calculations) { size_t i_rs; int i_rr; struct ub_packed_rrset_key* s; struct nsec3_cached_hash* hash = NULL; int r; + int calc_errors = 0; /* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */ for(s=filter_first(flt, &i_rs, &i_rr); s; s=filter_next(flt, &i_rs, &i_rr)) { + /* check if we are allowed more calculations */ + if(*calculations >= MAX_NSEC3_CALCULATIONS) { + if(calc_errors == *calculations) { + *calculations = MAX_NSEC3_ERRORS; + } + break; + } /* get name hashed for this NSEC3 RR */ - r = nsec3_hash_name(ct, env->scratch, env->scratch_buffer, + r = nsec3_hash_name(ct->ct, ct->region, env->scratch_buffer, s, i_rr, nm, nmlen, &hash); if(r == 0) { log_err("nsec3: malloc failure"); break; /* alloc failure */ - } else if(r != 1) - continue; /* malformed NSEC3 */ - else if(nsec3_hash_matches_owner(flt, hash, s)) { - *rrset = s; /* rrset with this name */ - *rr = i_rr; /* matches hash with these parameters */ - return 1; + } else if(r < 0) { + /* malformed NSEC3 */ + calc_errors++; + (*calculations)++; + continue; + } else { + if(r == 1) (*calculations)++; + if(nsec3_hash_matches_owner(flt, hash, s)) { + *rrset = s; /* rrset with this name */ + *rr = i_rr; /* matches hash with these parameters */ + return 1; + } } } *rrset = NULL; @@ -775,6 +816,7 @@ nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash, if(!nsec3_get_nextowner(rrset, rr, &next, &nextlen)) return 0; /* malformed RR proves nothing */ + if(!hash) return 0; /* please clang */ /* check the owner name is a hashed value . apex * base32 encoded values must have equal length. * hash_value and next hash value must have equal length. */ @@ -823,35 +865,51 @@ nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash, * @param nmlen: length of name. * @param rrset: covering NSEC3 rrset is returned here. * @param rr: rr of cover is returned here. + * @param calculations: current hash calculations. * @return true if a covering NSEC3 is found, false if not. */ static int find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt, - rbtree_type* ct, uint8_t* nm, size_t nmlen, - struct ub_packed_rrset_key** rrset, int* rr) + struct nsec3_cache_table* ct, uint8_t* nm, size_t nmlen, + struct ub_packed_rrset_key** rrset, int* rr, + int* calculations) { size_t i_rs; int i_rr; struct ub_packed_rrset_key* s; struct nsec3_cached_hash* hash = NULL; int r; + int calc_errors = 0; /* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */ for(s=filter_first(flt, &i_rs, &i_rr); s; s=filter_next(flt, &i_rs, &i_rr)) { + /* check if we are allowed more calculations */ + if(*calculations >= MAX_NSEC3_CALCULATIONS) { + if(calc_errors == *calculations) { + *calculations = MAX_NSEC3_ERRORS; + } + break; + } /* get name hashed for this NSEC3 RR */ - r = nsec3_hash_name(ct, env->scratch, env->scratch_buffer, + r = nsec3_hash_name(ct->ct, ct->region, env->scratch_buffer, s, i_rr, nm, nmlen, &hash); if(r == 0) { log_err("nsec3: malloc failure"); break; /* alloc failure */ - } else if(r != 1) - continue; /* malformed NSEC3 */ - else if(nsec3_covers(flt->zone, hash, s, i_rr, - env->scratch_buffer)) { - *rrset = s; /* rrset with this name */ - *rr = i_rr; /* covers hash with these parameters */ - return 1; + } else if(r < 0) { + /* malformed NSEC3 */ + calc_errors++; + (*calculations)++; + continue; + } else { + if(r == 1) (*calculations)++; + if(nsec3_covers(flt->zone, hash, s, i_rr, + env->scratch_buffer)) { + *rrset = s; /* rrset with this name */ + *rr = i_rr; /* covers hash with these parameters */ + return 1; + } } } *rrset = NULL; @@ -869,11 +927,13 @@ find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt, * @param ct: cached hashes table. * @param qinfo: query that is verified for. * @param ce: closest encloser information is returned in here. + * @param calculations: current hash calculations. * @return true if a closest encloser candidate is found, false if not. */ static int nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt, - rbtree_type* ct, struct query_info* qinfo, struct ce_response* ce) + struct nsec3_cache_table* ct, struct query_info* qinfo, + struct ce_response* ce, int* calculations) { uint8_t* nm = qinfo->qname; size_t nmlen = qinfo->qname_len; @@ -888,8 +948,12 @@ nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt, * may be the case. */ while(dname_subdomain_c(nm, flt->zone)) { + if(*calculations >= MAX_NSEC3_CALCULATIONS || + *calculations == MAX_NSEC3_ERRORS) { + return 0; + } if(find_matching_nsec3(env, flt, ct, nm, nmlen, - &ce->ce_rrset, &ce->ce_rr)) { + &ce->ce_rrset, &ce->ce_rr, calculations)) { ce->ce = nm; ce->ce_len = nmlen; return 1; @@ -933,22 +997,38 @@ next_closer(uint8_t* qname, size_t qnamelen, uint8_t* ce, * If set true, and the return value is true, then you can be * certain that the ce.nc_rrset and ce.nc_rr are set properly. * @param ce: closest encloser information is returned in here. + * @param calculations: pointer to the current NSEC3 hash calculations. * @return bogus if no closest encloser could be proven. * secure if a closest encloser could be proven, ce is set. * insecure if the closest-encloser candidate turns out to prove * that an insecure delegation exists above the qname. + * unchecked if no more hash calculations are allowed at this point. */ static enum sec_status nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt, - rbtree_type* ct, struct query_info* qinfo, int prove_does_not_exist, - struct ce_response* ce) + struct nsec3_cache_table* ct, struct query_info* qinfo, + int prove_does_not_exist, struct ce_response* ce, int* calculations) { uint8_t* nc; size_t nc_len; /* robust: clean out ce, in case it gets abused later */ memset(ce, 0, sizeof(*ce)); - if(!nsec3_find_closest_encloser(env, flt, ct, qinfo, ce)) { + if(!nsec3_find_closest_encloser(env, flt, ct, qinfo, ce, calculations)) { + if(*calculations == MAX_NSEC3_ERRORS) { + verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could " + "not find a candidate for the closest " + "encloser; all attempted hash calculations " + "were erroneous; bogus"); + return sec_status_bogus; + } else if(*calculations >= MAX_NSEC3_CALCULATIONS) { + verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could " + "not find a candidate for the closest " + "encloser; reached MAX_NSEC3_CALCULATIONS " + "(%d); unchecked still", + MAX_NSEC3_CALCULATIONS); + return sec_status_unchecked; + } verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could " "not find a candidate for the closest encloser."); return sec_status_bogus; @@ -989,9 +1069,23 @@ nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt, /* Otherwise, we need to show that the next closer name is covered. */ next_closer(qinfo->qname, qinfo->qname_len, ce->ce, &nc, &nc_len); if(!find_covering_nsec3(env, flt, ct, nc, nc_len, - &ce->nc_rrset, &ce->nc_rr)) { + &ce->nc_rrset, &ce->nc_rr, calculations)) { + if(*calculations == MAX_NSEC3_ERRORS) { + verbose(VERB_ALGO, "nsec3: Could not find proof that the " + "candidate encloser was the closest encloser; " + "all attempted hash calculations were " + "erroneous; bogus"); + return sec_status_bogus; + } else if(*calculations >= MAX_NSEC3_CALCULATIONS) { + verbose(VERB_ALGO, "nsec3: Could not find proof that the " + "candidate encloser was the closest encloser; " + "reached MAX_NSEC3_CALCULATIONS (%d); " + "unchecked still", + MAX_NSEC3_CALCULATIONS); + return sec_status_unchecked; + } verbose(VERB_ALGO, "nsec3: Could not find proof that the " - "candidate encloser was the closest encloser"); + "candidate encloser was the closest encloser"); return sec_status_bogus; } return sec_status_secure; @@ -1020,7 +1114,7 @@ nsec3_ce_wildcard(struct regional* region, uint8_t* ce, size_t celen, /** Do the name error proof */ static enum sec_status nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, - rbtree_type* ct, struct query_info* qinfo) + struct nsec3_cache_table* ct, struct query_info* qinfo, int* calc) { struct ce_response ce; uint8_t* wc; @@ -1032,11 +1126,15 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, /* First locate and prove the closest encloser to qname. We will * use the variant that fails if the closest encloser turns out * to be qname. */ - sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce); + sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce, calc); if(sec != sec_status_secure) { if(sec == sec_status_bogus) verbose(VERB_ALGO, "nsec3 nameerror proof: failed " "to prove a closest encloser"); + else if(sec == sec_status_unchecked) + verbose(VERB_ALGO, "nsec3 nameerror proof: will " + "continue proving closest encloser after " + "suspend"); else verbose(VERB_ALGO, "nsec3 nameerror proof: closest " "nsec3 is an insecure delegation"); return sec; @@ -1046,9 +1144,27 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, /* At this point, we know that qname does not exist. Now we need * to prove that the wildcard does not exist. */ log_assert(ce.ce); - wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen); - if(!wc || !find_covering_nsec3(env, flt, ct, wc, wclen, - &wc_rrset, &wc_rr)) { + wc = nsec3_ce_wildcard(ct->region, ce.ce, ce.ce_len, &wclen); + if(!wc) { + verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " + "that the applicable wildcard did not exist."); + return sec_status_bogus; + } + if(!find_covering_nsec3(env, flt, ct, wc, wclen, &wc_rrset, &wc_rr, calc)) { + if(*calc == MAX_NSEC3_ERRORS) { + verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " + "that the applicable wildcard did not exist; " + "all attempted hash calculations were " + "erroneous; bogus"); + return sec_status_bogus; + } else if(*calc >= MAX_NSEC3_CALCULATIONS) { + verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " + "that the applicable wildcard did not exist; " + "reached MAX_NSEC3_CALCULATIONS (%d); " + "unchecked still", + MAX_NSEC3_CALCULATIONS); + return sec_status_unchecked; + } verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " "that the applicable wildcard did not exist."); return sec_status_bogus; @@ -1064,14 +1180,13 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, enum sec_status nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey) + struct query_info* qinfo, struct key_entry_key* kkey, + struct nsec3_cache_table* ct, int* calc) { - rbtree_type ct; struct nsec3_filter flt; if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) return sec_status_bogus; /* no valid NSEC3s, bogus */ - rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ filter_init(&flt, list, num, qinfo); /* init RR iterator */ if(!flt.zone) return sec_status_bogus; /* no RRs */ @@ -1079,7 +1194,7 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, return sec_status_insecure; /* iteration count too high */ log_nametypeclass(VERB_ALGO, "start nsec3 nameerror proof, zone", flt.zone, 0, 0); - return nsec3_do_prove_nameerror(env, &flt, &ct, qinfo); + return nsec3_do_prove_nameerror(env, &flt, ct, qinfo, calc); } /* @@ -1090,7 +1205,8 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, /** Do the nodata proof */ static enum sec_status nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, - rbtree_type* ct, struct query_info* qinfo) + struct nsec3_cache_table* ct, struct query_info* qinfo, + int* calc) { struct ce_response ce; uint8_t* wc; @@ -1100,7 +1216,7 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, enum sec_status sec; if(find_matching_nsec3(env, flt, ct, qinfo->qname, qinfo->qname_len, - &rrset, &rr)) { + &rrset, &rr, calc)) { /* cases 1 and 2 */ if(nsec3_has_type(rrset, rr, qinfo->qtype)) { verbose(VERB_ALGO, "proveNodata: Matching NSEC3 " @@ -1144,11 +1260,23 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, } return sec_status_secure; } + if(*calc == MAX_NSEC3_ERRORS) { + verbose(VERB_ALGO, "proveNodata: all attempted hash " + "calculations were erroneous while finding a matching " + "NSEC3, bogus"); + return sec_status_bogus; + } else if(*calc >= MAX_NSEC3_CALCULATIONS) { + verbose(VERB_ALGO, "proveNodata: reached " + "MAX_NSEC3_CALCULATIONS (%d) while finding a " + "matching NSEC3; unchecked still", + MAX_NSEC3_CALCULATIONS); + return sec_status_unchecked; + } /* For cases 3 - 5, we need the proven closest encloser, and it * can't match qname. Although, at this point, we know that it * won't since we just checked that. */ - sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce); + sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce, calc); if(sec == sec_status_bogus) { verbose(VERB_ALGO, "proveNodata: did not match qname, " "nor found a proven closest encloser."); @@ -1157,14 +1285,17 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, verbose(VERB_ALGO, "proveNodata: closest nsec3 is insecure " "delegation."); return sec_status_insecure; + } else if(sec==sec_status_unchecked) { + return sec_status_unchecked; } /* Case 3: removed */ /* Case 4: */ log_assert(ce.ce); - wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen); - if(wc && find_matching_nsec3(env, flt, ct, wc, wclen, &rrset, &rr)) { + wc = nsec3_ce_wildcard(ct->region, ce.ce, ce.ce_len, &wclen); + if(wc && find_matching_nsec3(env, flt, ct, wc, wclen, &rrset, &rr, + calc)) { /* found wildcard */ if(nsec3_has_type(rrset, rr, qinfo->qtype)) { verbose(VERB_ALGO, "nsec3 nodata proof: matching " @@ -1195,6 +1326,18 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, } return sec_status_secure; } + if(*calc == MAX_NSEC3_ERRORS) { + verbose(VERB_ALGO, "nsec3 nodata proof: all attempted hash " + "calculations were erroneous while matching " + "wildcard, bogus"); + return sec_status_bogus; + } else if(*calc >= MAX_NSEC3_CALCULATIONS) { + verbose(VERB_ALGO, "nsec3 nodata proof: reached " + "MAX_NSEC3_CALCULATIONS (%d) while matching " + "wildcard, unchecked still", + MAX_NSEC3_CALCULATIONS); + return sec_status_unchecked; + } /* Case 5: */ /* Due to forwarders, cnames, and other collating effects, we @@ -1223,28 +1366,27 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, enum sec_status nsec3_prove_nodata(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey) + struct query_info* qinfo, struct key_entry_key* kkey, + struct nsec3_cache_table* ct, int* calc) { - rbtree_type ct; struct nsec3_filter flt; if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) return sec_status_bogus; /* no valid NSEC3s, bogus */ - rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ filter_init(&flt, list, num, qinfo); /* init RR iterator */ if(!flt.zone) return sec_status_bogus; /* no RRs */ if(nsec3_iteration_count_high(ve, &flt, kkey)) return sec_status_insecure; /* iteration count too high */ - return nsec3_do_prove_nodata(env, &flt, &ct, qinfo); + return nsec3_do_prove_nodata(env, &flt, ct, qinfo, calc); } enum sec_status nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc) + struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc, + struct nsec3_cache_table* ct, int* calc) { - rbtree_type ct; struct nsec3_filter flt; struct ce_response ce; uint8_t* nc; @@ -1254,7 +1396,6 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) return sec_status_bogus; /* no valid NSEC3s, bogus */ - rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ filter_init(&flt, list, num, qinfo); /* init RR iterator */ if(!flt.zone) return sec_status_bogus; /* no RRs */ @@ -1272,8 +1413,22 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, /* Now we still need to prove that the original data did not exist. * Otherwise, we need to show that the next closer name is covered. */ next_closer(qinfo->qname, qinfo->qname_len, ce.ce, &nc, &nc_len); - if(!find_covering_nsec3(env, &flt, &ct, nc, nc_len, - &ce.nc_rrset, &ce.nc_rr)) { + if(!find_covering_nsec3(env, &flt, ct, nc, nc_len, + &ce.nc_rrset, &ce.nc_rr, calc)) { + if(*calc == MAX_NSEC3_ERRORS) { + verbose(VERB_ALGO, "proveWildcard: did not find a " + "covering NSEC3 that covered the next closer " + "name; all attempted hash calculations were " + "erroneous; bogus"); + return sec_status_bogus; + } else if(*calc >= MAX_NSEC3_CALCULATIONS) { + verbose(VERB_ALGO, "proveWildcard: did not find a " + "covering NSEC3 that covered the next closer " + "name; reached MAX_NSEC3_CALCULATIONS " + "(%d); unchecked still", + MAX_NSEC3_CALCULATIONS); + return sec_status_unchecked; + } verbose(VERB_ALGO, "proveWildcard: did not find a covering " "NSEC3 that covered the next closer name."); return sec_status_bogus; @@ -1294,6 +1449,7 @@ list_is_secure(struct module_env* env, struct val_env* ve, { struct packed_rrset_data* d; size_t i; + int verified = 0; for(i=0; ientry.data; if(list[i]->rk.type != htons(LDNS_RR_TYPE_NSEC3)) @@ -1304,7 +1460,8 @@ list_is_secure(struct module_env* env, struct val_env* ve, if(d->security == sec_status_secure) continue; d->security = val_verify_rrset_entry(env, ve, list[i], kkey, - reason, reason_bogus, LDNS_SECTION_AUTHORITY, qstate); + reason, reason_bogus, LDNS_SECTION_AUTHORITY, qstate, + &verified); if(d->security != sec_status_secure) { verbose(VERB_ALGO, "NSEC3 did not verify"); return 0; @@ -1318,13 +1475,16 @@ enum sec_status nsec3_prove_nods(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, struct query_info* qinfo, struct key_entry_key* kkey, char** reason, - sldns_ede_code* reason_bogus, struct module_qstate* qstate) + sldns_ede_code* reason_bogus, struct module_qstate* qstate, + struct nsec3_cache_table* ct) { - rbtree_type ct; struct nsec3_filter flt; struct ce_response ce; struct ub_packed_rrset_key* rrset; int rr; + int calc = 0; + enum sec_status sec; + log_assert(qinfo->qtype == LDNS_RR_TYPE_DS); if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) { @@ -1335,7 +1495,6 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, *reason = "not all NSEC3 records secure"; return sec_status_bogus; /* not all NSEC3 records secure */ } - rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ filter_init(&flt, list, num, qinfo); /* init RR iterator */ if(!flt.zone) { *reason = "no NSEC3 records"; @@ -1346,8 +1505,8 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, /* Look for a matching NSEC3 to qname -- this is the normal * NODATA case. */ - if(find_matching_nsec3(env, &flt, &ct, qinfo->qname, qinfo->qname_len, - &rrset, &rr)) { + if(find_matching_nsec3(env, &flt, ct, qinfo->qname, qinfo->qname_len, + &rrset, &rr, &calc)) { /* If the matching NSEC3 has the SOA bit set, it is from * the wrong zone (the child instead of the parent). If * it has the DS bit set, then we were lied to. */ @@ -1370,10 +1529,24 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, /* Otherwise, this proves no DS. */ return sec_status_secure; } + if(calc == MAX_NSEC3_ERRORS) { + verbose(VERB_ALGO, "nsec3 provenods: all attempted hash " + "calculations were erroneous while finding a matching " + "NSEC3, bogus"); + return sec_status_bogus; + } else if(calc >= MAX_NSEC3_CALCULATIONS) { + verbose(VERB_ALGO, "nsec3 provenods: reached " + "MAX_NSEC3_CALCULATIONS (%d) while finding a " + "matching NSEC3, unchecked still", + MAX_NSEC3_CALCULATIONS); + return sec_status_unchecked; + } /* Otherwise, we are probably in the opt-out case. */ - if(nsec3_prove_closest_encloser(env, &flt, &ct, qinfo, 1, &ce) - != sec_status_secure) { + sec = nsec3_prove_closest_encloser(env, &flt, ct, qinfo, 1, &ce, &calc); + if(sec == sec_status_unchecked) { + return sec_status_unchecked; + } else if(sec != sec_status_secure) { /* an insecure delegation *above* the qname does not prove * anything about this qname exactly, and bogus is bogus */ verbose(VERB_ALGO, "nsec3 provenods: did not match qname, " @@ -1408,16 +1581,15 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, enum sec_status nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, int* nodata) + struct query_info* qinfo, struct key_entry_key* kkey, int* nodata, + struct nsec3_cache_table* ct, int* calc) { enum sec_status sec, secnx; - rbtree_type ct; struct nsec3_filter flt; *nodata = 0; if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) return sec_status_bogus; /* no valid NSEC3s, bogus */ - rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ filter_init(&flt, list, num, qinfo); /* init RR iterator */ if(!flt.zone) return sec_status_bogus; /* no RRs */ @@ -1427,16 +1599,20 @@ nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, /* try nxdomain and nodata after another, while keeping the * hash cache intact */ - secnx = nsec3_do_prove_nameerror(env, &flt, &ct, qinfo); + secnx = nsec3_do_prove_nameerror(env, &flt, ct, qinfo, calc); if(secnx==sec_status_secure) return sec_status_secure; - sec = nsec3_do_prove_nodata(env, &flt, &ct, qinfo); + else if(secnx == sec_status_unchecked) + return sec_status_unchecked; + sec = nsec3_do_prove_nodata(env, &flt, ct, qinfo, calc); if(sec==sec_status_secure) { *nodata = 1; } else if(sec == sec_status_insecure) { *nodata = 1; } else if(secnx == sec_status_insecure) { sec = sec_status_insecure; + } else if(sec == sec_status_unchecked) { + return sec_status_unchecked; } return sec; } diff --git a/usr.sbin/unbound/validator/val_nsec3.h b/usr.sbin/unbound/validator/val_nsec3.h index f5c4141bf..6874513d1 100644 --- a/usr.sbin/unbound/validator/val_nsec3.h +++ b/usr.sbin/unbound/validator/val_nsec3.h @@ -98,6 +98,15 @@ struct sldns_buffer; /** The SHA1 hash algorithm for NSEC3 */ #define NSEC3_HASH_SHA1 0x01 +/** +* Cache table for NSEC3 hashes. +* It keeps a *pointer* to the region its items are allocated. +*/ +struct nsec3_cache_table { + rbtree_type* ct; + struct regional* region; +}; + /** * Determine if the set of NSEC3 records provided with a response prove NAME * ERROR. This means that the NSEC3s prove a) the closest encloser exists, @@ -110,14 +119,18 @@ struct sldns_buffer; * @param num: number of RRsets in the array to examine. * @param qinfo: query that is verified for. * @param kkey: key entry that signed the NSEC3s. + * @param ct: cached hashes table. + * @param calc: current hash calculations. * @return: * sec_status SECURE of the Name Error is proven by the NSEC3 RRs, - * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. + * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored, + * UNCHECKED if no more hash calculations are allowed at this point. */ enum sec_status nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey); + struct query_info* qinfo, struct key_entry_key* kkey, + struct nsec3_cache_table* ct, int* calc); /** * Determine if the NSEC3s provided in a response prove the NOERROR/NODATA @@ -144,15 +157,18 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, * @param num: number of RRsets in the array to examine. * @param qinfo: query that is verified for. * @param kkey: key entry that signed the NSEC3s. + * @param ct: cached hashes table. + * @param calc: current hash calculations. * @return: * sec_status SECURE of the proposition is proven by the NSEC3 RRs, - * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. + * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored, + * UNCHECKED if no more hash calculations are allowed at this point. */ enum sec_status nsec3_prove_nodata(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey); - + struct query_info* qinfo, struct key_entry_key* kkey, + struct nsec3_cache_table* ct, int* calc); /** * Prove that a positive wildcard match was appropriate (no direct match @@ -166,14 +182,18 @@ nsec3_prove_nodata(struct module_env* env, struct val_env* ve, * @param kkey: key entry that signed the NSEC3s. * @param wc: The purported wildcard that matched. This is the wildcard name * as *.wildcard.name., with the *. label already removed. + * @param ct: cached hashes table. + * @param calc: current hash calculations. * @return: * sec_status SECURE of the proposition is proven by the NSEC3 RRs, - * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. + * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored, + * UNCHECKED if no more hash calculations are allowed at this point. */ enum sec_status nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc); + struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc, + struct nsec3_cache_table* ct, int* calc); /** * Prove that a DS response either had no DS, or wasn't a delegation point. @@ -189,17 +209,20 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, * @param reason: string for bogus result. * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param qstate: qstate with region. + * @param ct: cached hashes table. * @return: * sec_status SECURE of the proposition is proven by the NSEC3 RRs, * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. * or if there was no DS in an insecure (i.e., opt-in) way, - * INDETERMINATE if it was clear that this wasn't a delegation point. + * INDETERMINATE if it was clear that this wasn't a delegation point, + * UNCHECKED if no more hash calculations are allowed at this point. */ enum sec_status nsec3_prove_nods(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, struct query_info* qinfo, struct key_entry_key* kkey, char** reason, - sldns_ede_code* reason_bogus, struct module_qstate* qstate); + sldns_ede_code* reason_bogus, struct module_qstate* qstate, + struct nsec3_cache_table* ct); /** * Prove NXDOMAIN or NODATA. @@ -212,14 +235,18 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, * @param kkey: key entry that signed the NSEC3s. * @param nodata: if return value is secure, this indicates if nodata or * nxdomain was proven. + * @param ct: cached hashes table. + * @param calc: current hash calculations. * @return: * sec_status SECURE of the proposition is proven by the NSEC3 RRs, - * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. + * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored, + * UNCHECKED if no more hash calculations are allowed at this point. */ enum sec_status nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, int* nodata); + struct query_info* qinfo, struct key_entry_key* kkey, int* nodata, + struct nsec3_cache_table* ct, int* calc); /** * The NSEC3 hash result storage. @@ -256,6 +283,14 @@ struct nsec3_cached_hash { */ int nsec3_hash_cmp(const void* c1, const void* c2); +/** + * Initialise the NSEC3 cache table. + * @param ct: the nsec3 cache table. + * @param region: the region where allocations for the table will happen. + * @return true on success, false on malloc error. + */ +int nsec3_cache_table_init(struct nsec3_cache_table* ct, struct regional* region); + /** * Obtain the hash of an owner name. * Used internally by the nsec3 proof functions in this file. @@ -272,7 +307,8 @@ int nsec3_hash_cmp(const void* c1, const void* c2); * @param dname_len: the length of the name. * @param hash: the hash node is returned on success. * @return: - * 1 on success, either from cache or newly hashed hash is returned. + * 2 on success, hash from cache is returned. + * 1 on success, newly computed hash is returned. * 0 on a malloc failure. * -1 if the NSEC3 rr was badly formatted (i.e. formerr). */ diff --git a/usr.sbin/unbound/validator/val_sigcrypt.c b/usr.sbin/unbound/validator/val_sigcrypt.c index 596a315db..66b667d79 100644 --- a/usr.sbin/unbound/validator/val_sigcrypt.c +++ b/usr.sbin/unbound/validator/val_sigcrypt.c @@ -79,6 +79,9 @@ #include #endif +/** Maximum number of RRSIG validations for an RRset. */ +#define MAX_VALIDATE_RRSIGS 8 + /** return number of rrs in an rrset */ static size_t rrset_get_count(struct ub_packed_rrset_key* rrset) @@ -542,6 +545,8 @@ int algo_needs_missing(struct algo_needs* n) * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param section: section of packet where this rrset comes from. * @param qstate: qstate with region. + * @param numverified: incremented when the number of RRSIG validations + * increases. * @return secure if any key signs *this* signature. bogus if no key signs it, * unchecked on error, or indeterminate if all keys are not supported by * the crypto library (openssl3+ only). @@ -552,7 +557,8 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey, size_t sig_idx, struct rbtree_type** sortree, char** reason, sldns_ede_code *reason_bogus, - sldns_pkt_section section, struct module_qstate* qstate) + sldns_pkt_section section, struct module_qstate* qstate, + int* numverified) { /* find matching keys and check them */ enum sec_status sec = sec_status_bogus; @@ -576,6 +582,7 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, tag != dnskey_calc_keytag(dnskey, i)) continue; numchecked ++; + (*numverified)++; /* see if key verifies */ sec = dnskey_verify_rrset_sig(env->scratch, @@ -586,6 +593,13 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, return sec; else if(sec == sec_status_indeterminate) numindeterminate ++; + if(*numverified > MAX_VALIDATE_RRSIGS) { + *reason = "too many RRSIG validations"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; + verbose(VERB_ALGO, "verify sig: too many RRSIG validations"); + return sec_status_bogus; + } } if(numchecked == 0) { *reason = "signatures from unknown keys"; @@ -609,7 +623,7 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus, - sldns_pkt_section section, struct module_qstate* qstate) + sldns_pkt_section section, struct module_qstate* qstate, int* verified) { enum sec_status sec; size_t i, num; @@ -617,6 +631,7 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, /* make sure that for all DNSKEY algorithms there are valid sigs */ struct algo_needs needs; int alg; + *verified = 0; num = rrset_get_sigcount(rrset); if(num == 0) { @@ -641,7 +656,7 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, for(i=0; inow, rrset, dnskey, i, &sortree, reason, reason_bogus, - section, qstate); + section, qstate, verified); /* see which algorithm has been fixed up */ if(sec == sec_status_secure) { if(!sigalg) @@ -653,6 +668,13 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, algo_needs_set_bogus(&needs, (uint8_t)rrset_get_sig_algo(rrset, i)); } + if(*verified > MAX_VALIDATE_RRSIGS) { + verbose(VERB_QUERY, "rrset failed to verify, too many RRSIG validations"); + *reason = "too many RRSIG validations"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; + return sec_status_bogus; + } } if(sigalg && (alg=algo_needs_missing(&needs)) != 0) { verbose(VERB_ALGO, "rrset failed to verify: " @@ -691,6 +713,7 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve, int buf_canon = 0; uint16_t tag = dnskey_calc_keytag(dnskey, dnskey_idx); int algo = dnskey_get_algo(dnskey, dnskey_idx); + int numverified = 0; num = rrset_get_sigcount(rrset); if(num == 0) { @@ -714,8 +737,16 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve, if(sec == sec_status_secure) return sec; numchecked ++; + numverified ++; if(sec == sec_status_indeterminate) numindeterminate ++; + if(numverified > MAX_VALIDATE_RRSIGS) { + verbose(VERB_QUERY, "rrset failed to verify, too many RRSIG validations"); + *reason = "too many RRSIG validations"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; + return sec_status_bogus; + } } verbose(VERB_ALGO, "rrset failed to verify: all signatures are bogus"); if(!numchecked) { diff --git a/usr.sbin/unbound/validator/val_sigcrypt.h b/usr.sbin/unbound/validator/val_sigcrypt.h index 9baec52c0..06dea97e0 100644 --- a/usr.sbin/unbound/validator/val_sigcrypt.h +++ b/usr.sbin/unbound/validator/val_sigcrypt.h @@ -260,6 +260,7 @@ uint16_t dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx); * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param section: section of packet where this rrset comes from. * @param qstate: qstate with region. + * @param verified: if not NULL the number of RRSIG validations is returned. * @return SECURE if one key in the set verifies one rrsig. * UNCHECKED on allocation errors, unsupported algorithms, malformed data, * and BOGUS on verification failures (no keys match any signatures). @@ -268,7 +269,7 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus, - sldns_pkt_section section, struct module_qstate* qstate); + sldns_pkt_section section, struct module_qstate* qstate, int* verified); /** diff --git a/usr.sbin/unbound/validator/val_utils.c b/usr.sbin/unbound/validator/val_utils.c index ba7a66a22..c2b2676c4 100644 --- a/usr.sbin/unbound/validator/val_utils.c +++ b/usr.sbin/unbound/validator/val_utils.c @@ -58,6 +58,10 @@ #include "sldns/wire2str.h" #include "sldns/parseutil.h" +/** Maximum allowed digest match failures per DS, for DNSKEYs with the same + * properties */ +#define MAX_DS_MATCH_FAILURES 4 + enum val_classification val_classify_response(uint16_t query_flags, struct query_info* origqinf, struct query_info* qinf, struct reply_info* rep, size_t skip) @@ -336,7 +340,8 @@ static enum sec_status val_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys, uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus, - sldns_pkt_section section, struct module_qstate* qstate) + sldns_pkt_section section, struct module_qstate* qstate, + int *verified) { enum sec_status sec; struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> @@ -346,6 +351,7 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, log_nametypeclass(VERB_ALGO, "verify rrset cached", rrset->rk.dname, ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); + *verified = 0; return d->security; } /* check in the cache if verification has already been done */ @@ -354,12 +360,13 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, log_nametypeclass(VERB_ALGO, "verify rrset from cache", rrset->rk.dname, ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); + *verified = 0; return d->security; } log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname, ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason, - reason_bogus, section, qstate); + reason_bogus, section, qstate, verified); verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec)); regional_free_all(env->scratch); @@ -393,7 +400,8 @@ enum sec_status val_verify_rrset_entry(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus, - sldns_pkt_section section, struct module_qstate* qstate) + sldns_pkt_section section, struct module_qstate* qstate, + int* verified) { /* temporary dnskey rrset-key */ struct ub_packed_rrset_key dnskey; @@ -407,7 +415,7 @@ val_verify_rrset_entry(struct module_env* env, struct val_env* ve, dnskey.entry.key = &dnskey; dnskey.entry.data = kd->rrset_data; sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason, - reason_bogus, section, qstate); + reason_bogus, section, qstate, verified); return sec; } @@ -439,6 +447,12 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve, if(!ds_digest_match_dnskey(env, dnskey_rrset, i, ds_rrset, ds_idx)) { verbose(VERB_ALGO, "DS match attempt failed"); + if(numchecked > numhashok + MAX_DS_MATCH_FAILURES) { + verbose(VERB_ALGO, "DS match attempt reached " + "MAX_DS_MATCH_FAILURES (%d); bogus", + MAX_DS_MATCH_FAILURES); + return sec_status_bogus; + } continue; } numhashok++; diff --git a/usr.sbin/unbound/validator/val_utils.h b/usr.sbin/unbound/validator/val_utils.h index d6abf1235..e2d5c3f5c 100644 --- a/usr.sbin/unbound/validator/val_utils.h +++ b/usr.sbin/unbound/validator/val_utils.h @@ -124,12 +124,14 @@ void val_find_signer(enum val_classification subtype, * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param section: section of packet where this rrset comes from. * @param qstate: qstate with region. + * @param verified: if not NULL, the number of RRSIG validations is returned. * @return security status of verification. */ enum sec_status val_verify_rrset_entry(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus, - sldns_pkt_section section, struct module_qstate* qstate); + sldns_pkt_section section, struct module_qstate* qstate, + int* verified); /** * Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but diff --git a/usr.sbin/unbound/validator/validator.c b/usr.sbin/unbound/validator/validator.c index 004846016..c0666b885 100644 --- a/usr.sbin/unbound/validator/validator.c +++ b/usr.sbin/unbound/validator/validator.c @@ -64,10 +64,15 @@ #include "sldns/wire2str.h" #include "sldns/str2wire.h" +/** Max number of RRSIGs to validate at once, suspend query for later. */ +#define MAX_VALIDATE_AT_ONCE 8 +/** Max number of validation suspends allowed, error out otherwise. */ +#define MAX_VALIDATION_SUSPENDS 16 + /* forward decl for cache response and normal super inform calls of a DS */ static void process_ds_response(struct module_qstate* qstate, struct val_qstate* vq, int id, int rcode, struct dns_msg* msg, - struct query_info* qinfo, struct sock_list* origin); + struct query_info* qinfo, struct sock_list* origin, int* suspend); /* Updates the suplied EDE (RFC8914) code selectively so we don't lose @@ -281,6 +286,21 @@ val_new(struct module_qstate* qstate, int id) return val_new_getmsg(qstate, vq); } +/** reset validator query state for query restart */ +static void +val_restart(struct val_qstate* vq) +{ + struct comm_timer* temp_timer; + int restart_count; + if(!vq) return; + temp_timer = vq->suspend_timer; + restart_count = vq->restart_count+1; + memset(vq, 0, sizeof(*vq)); + vq->suspend_timer = temp_timer; + vq->restart_count = restart_count; + vq->state = VAL_INIT_STATE; +} + /** * Exit validation with an error status * @@ -587,30 +607,42 @@ prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq, * completed. * * @param qstate: query state. + * @param vq: validator query state. * @param env: module env for verify. * @param ve: validator env for verify. * @param qchase: query that was made. * @param chase_reply: answer to validate. * @param key_entry: the key entry, which is trusted, and which matches * the signer of the answer. The key entry isgood(). + * @param suspend: returned true if the task takes too long and needs to + * suspend to continue the effort later. * @return false if any of the rrsets in the an or ns sections of the message * fail to verify. The message is then set to bogus. */ static int -validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, - struct val_env* ve, struct query_info* qchase, - struct reply_info* chase_reply, struct key_entry_key* key_entry) +validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq, + struct module_env* env, struct val_env* ve, struct query_info* qchase, + struct reply_info* chase_reply, struct key_entry_key* key_entry, + int* suspend) { uint8_t* sname; size_t i, slen; struct ub_packed_rrset_key* s; enum sec_status sec; - int dname_seen = 0; + int dname_seen = 0, num_verifies = 0, verified, have_state = 0; char* reason = NULL; sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; + *suspend = 0; + if(vq->msg_signatures_state) { + /* Pick up the state, and reset it, may not be needed now. */ + vq->msg_signatures_state = 0; + have_state = 1; + } /* validate the ANSWER section */ for(i=0; ian_numrrsets; i++) { + if(have_state && i <= vq->msg_signatures_index) + continue; s = chase_reply->rrsets[i]; /* Skip the CNAME following a (validated) DNAME. * Because of the normalization routines in the iterator, @@ -629,7 +661,7 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, /* Verify the answer rrset */ sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, - &reason_bogus, LDNS_SECTION_ANSWER, qstate); + &reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified); /* If the (answer) rrset failed to validate, then this * message is BAD. */ if(sec != sec_status_secure) { @@ -654,14 +686,33 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME) { dname_seen = 1; } + num_verifies += verified; + if(num_verifies > MAX_VALIDATE_AT_ONCE && + i+1 < (env->cfg->val_clean_additional? + chase_reply->an_numrrsets+chase_reply->ns_numrrsets: + chase_reply->rrset_count)) { + /* If the number of RRSIGs exceeds the maximum in + * one go, suspend. Only suspend if there is a next + * rrset to verify, i+1msg_signatures_state = 1; + vq->msg_signatures_index = i; + verbose(VERB_ALGO, "msg signature validation " + "suspended"); + return 0; + } } /* validate the AUTHORITY section */ for(i=chase_reply->an_numrrsets; ian_numrrsets+ chase_reply->ns_numrrsets; i++) { + if(have_state && i <= vq->msg_signatures_index) + continue; s = chase_reply->rrsets[i]; sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, - &reason_bogus, LDNS_SECTION_AUTHORITY, qstate); + &reason_bogus, LDNS_SECTION_AUTHORITY, qstate, + &verified); /* If anything in the authority section fails to be secure, * we have a bad message. */ if(sec != sec_status_secure) { @@ -675,6 +726,18 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, update_reason_bogus(chase_reply, reason_bogus); return 0; } + num_verifies += verified; + if(num_verifies > MAX_VALIDATE_AT_ONCE && + i+1 < (env->cfg->val_clean_additional? + chase_reply->an_numrrsets+chase_reply->ns_numrrsets: + chase_reply->rrset_count)) { + *suspend = 1; + vq->msg_signatures_state = 1; + vq->msg_signatures_index = i; + verbose(VERB_ALGO, "msg signature validation " + "suspended"); + return 0; + } } /* If set, the validator should clean the additional section of @@ -684,22 +747,103 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, /* attempt to validate the ADDITIONAL section rrsets */ for(i=chase_reply->an_numrrsets+chase_reply->ns_numrrsets; irrset_count; i++) { + if(have_state && i <= vq->msg_signatures_index) + continue; s = chase_reply->rrsets[i]; /* only validate rrs that have signatures with the key */ /* leave others unchecked, those get removed later on too */ val_find_rrset_signer(s, &sname, &slen); + verified = 0; if(sname && query_dname_compare(sname, key_entry->name)==0) (void)val_verify_rrset_entry(env, ve, s, key_entry, - &reason, NULL, LDNS_SECTION_ADDITIONAL, qstate); + &reason, NULL, LDNS_SECTION_ADDITIONAL, qstate, + &verified); /* the additional section can fail to be secure, * it is optional, check signature in case we need * to clean the additional section later. */ + num_verifies += verified; + if(num_verifies > MAX_VALIDATE_AT_ONCE && + i+1 < chase_reply->rrset_count) { + *suspend = 1; + vq->msg_signatures_state = 1; + vq->msg_signatures_index = i; + verbose(VERB_ALGO, "msg signature validation " + "suspended"); + return 0; + } } return 1; } +void +validate_suspend_timer_cb(void* arg) +{ + struct module_qstate* qstate = (struct module_qstate*)arg; + verbose(VERB_ALGO, "validate_suspend timer, continue"); + mesh_run(qstate->env->mesh, qstate->mesh_info, module_event_pass, + NULL); +} + +/** Setup timer to continue validation of msg signatures later */ +static int +validate_suspend_setup_timer(struct module_qstate* qstate, + struct val_qstate* vq, int id, enum val_state resume_state) +{ + struct timeval tv; + int usec, slack, base; + if(vq->suspend_count >= MAX_VALIDATION_SUSPENDS) { + verbose(VERB_ALGO, "validate_suspend timer: " + "reached MAX_VALIDATION_SUSPENDS (%d); error out", + MAX_VALIDATION_SUSPENDS); + errinf(qstate, "max validation suspends reached, " + "too many RRSIG validations"); + return 0; + } + verbose(VERB_ALGO, "validate_suspend timer, set for suspend"); + vq->state = resume_state; + qstate->ext_state[id] = module_wait_reply; + if(!vq->suspend_timer) { + vq->suspend_timer = comm_timer_create( + qstate->env->worker_base, + validate_suspend_timer_cb, qstate); + if(!vq->suspend_timer) { + log_err("validate_suspend_setup_timer: " + "out of memory for comm_timer_create"); + return 0; + } + } + /* The timer is activated later, after other events in the event + * loop have been processed. The query state can also be deleted, + * when the list is full and query states are dropped. */ + /* Extend wait time if there are a lot of queries or if this one + * is taking long, to keep around cpu time for ordinary queries. */ + usec = 50000; /* 50 msec */ + slack = 0; + if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states) + slack += 3; + else if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states/2) + slack += 2; + else if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states/4) + slack += 1; + if(vq->suspend_count > 3) + slack += 3; + else if(vq->suspend_count > 0) + slack += vq->suspend_count; + if(slack != 0 && slack <= 12 /* No numeric overflow. */) { + usec = usec << slack; + } + /* Spread such timeouts within 90%-100% of the original timer. */ + base = usec * 9/10; + usec = base + ub_random_max(qstate->env->rnd, usec-base); + tv.tv_usec = (usec % 1000000); + tv.tv_sec = (usec / 1000000); + vq->suspend_count ++; + comm_timer_set(vq->suspend_timer, &tv); + return 1; +} + /** * Detect wrong truncated response (say from BIND 9.6.1 that is forwarding * and saw the NS record without signatures from a referral). @@ -798,11 +942,17 @@ remove_spurious_authority(struct reply_info* chase_reply, * @param chase_reply: answer to that query to validate. * @param kkey: the key entry, which is trusted, and which matches * the signer of the answer. The key entry isgood(). + * @param qstate: query state for the region. + * @param vq: validator state for the nsec3 cache table. + * @param nsec3_calculations: current nsec3 hash calculations. + * @param suspend: returned true if the task takes too long and needs to + * suspend to continue the effort later. */ static void validate_positive_response(struct module_env* env, struct val_env* ve, struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* kkey) + struct key_entry_key* kkey, struct module_qstate* qstate, + struct val_qstate* vq, int* nsec3_calculations, int* suspend) { uint8_t* wc = NULL; size_t wl; @@ -811,6 +961,7 @@ validate_positive_response(struct module_env* env, struct val_env* ve, int nsec3s_seen = 0; size_t i; struct ub_packed_rrset_key* s; + *suspend = 0; /* validate the ANSWER section - this will be the answer itself */ for(i=0; ian_numrrsets; i++) { @@ -862,17 +1013,23 @@ validate_positive_response(struct module_env* env, struct val_env* ve, /* If this was a positive wildcard response that we haven't already * proven, and we have NSEC3 records, try to prove it using the NSEC3 * records. */ - if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) { + if(wc != NULL && !wc_NSEC_ok && nsec3s_seen && + nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { enum sec_status sec = nsec3_prove_wildcard(env, ve, chase_reply->rrsets+chase_reply->an_numrrsets, - chase_reply->ns_numrrsets, qchase, kkey, wc); + chase_reply->ns_numrrsets, qchase, kkey, wc, + &vq->nsec3_cache_table, nsec3_calculations); if(sec == sec_status_insecure) { verbose(VERB_ALGO, "Positive wildcard response is " "insecure"); chase_reply->security = sec_status_insecure; return; - } else if(sec == sec_status_secure) + } else if(sec == sec_status_secure) { wc_NSEC_ok = 1; + } else if(sec == sec_status_unchecked) { + *suspend = 1; + return; + } } /* If after all this, we still haven't proven the positive wildcard @@ -904,11 +1061,17 @@ validate_positive_response(struct module_env* env, struct val_env* ve, * @param chase_reply: answer to that query to validate. * @param kkey: the key entry, which is trusted, and which matches * the signer of the answer. The key entry isgood(). + * @param qstate: query state for the region. + * @param vq: validator state for the nsec3 cache table. + * @param nsec3_calculations: current nsec3 hash calculations. + * @param suspend: returned true if the task takes too long and needs to + * suspend to continue the effort later. */ static void validate_nodata_response(struct module_env* env, struct val_env* ve, struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* kkey) + struct key_entry_key* kkey, struct module_qstate* qstate, + struct val_qstate* vq, int* nsec3_calculations, int* suspend) { /* Since we are here, there must be nothing in the ANSWER section to * validate. */ @@ -925,6 +1088,7 @@ validate_nodata_response(struct module_env* env, struct val_env* ve, int nsec3s_seen = 0; /* nsec3s seen */ struct ub_packed_rrset_key* s; size_t i; + *suspend = 0; for(i=chase_reply->an_numrrsets; ian_numrrsets+ chase_reply->ns_numrrsets; i++) { @@ -963,16 +1127,23 @@ validate_nodata_response(struct module_env* env, struct val_env* ve, } } - if(!has_valid_nsec && nsec3s_seen) { + if(!has_valid_nsec && nsec3s_seen && + nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { enum sec_status sec = nsec3_prove_nodata(env, ve, chase_reply->rrsets+chase_reply->an_numrrsets, - chase_reply->ns_numrrsets, qchase, kkey); + chase_reply->ns_numrrsets, qchase, kkey, + &vq->nsec3_cache_table, nsec3_calculations); if(sec == sec_status_insecure) { verbose(VERB_ALGO, "NODATA response is insecure"); chase_reply->security = sec_status_insecure; return; - } else if(sec == sec_status_secure) + } else if(sec == sec_status_secure) { has_valid_nsec = 1; + } else if(sec == sec_status_unchecked) { + /* check is incomplete; suspend */ + *suspend = 1; + return; + } } if(!has_valid_nsec) { @@ -1004,11 +1175,18 @@ validate_nodata_response(struct module_env* env, struct val_env* ve, * @param kkey: the key entry, which is trusted, and which matches * the signer of the answer. The key entry isgood(). * @param rcode: adjusted RCODE, in case of RCODE/proof mismatch leniency. + * @param qstate: query state for the region. + * @param vq: validator state for the nsec3 cache table. + * @param nsec3_calculations: current nsec3 hash calculations. + * @param suspend: returned true if the task takes too long and needs to + * suspend to continue the effort later. */ static void validate_nameerror_response(struct module_env* env, struct val_env* ve, struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* kkey, int* rcode) + struct key_entry_key* kkey, int* rcode, + struct module_qstate* qstate, struct val_qstate* vq, + int* nsec3_calculations, int* suspend) { int has_valid_nsec = 0; int has_valid_wnsec = 0; @@ -1018,6 +1196,7 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve, uint8_t* ce; int ce_labs = 0; int prev_ce_labs = 0; + *suspend = 0; for(i=chase_reply->an_numrrsets; ian_numrrsets+ chase_reply->ns_numrrsets; i++) { @@ -1047,13 +1226,18 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve, nsec3s_seen = 1; } - if((!has_valid_nsec || !has_valid_wnsec) && nsec3s_seen) { + if((!has_valid_nsec || !has_valid_wnsec) && nsec3s_seen && + nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { /* use NSEC3 proof, both answer and auth rrsets, in case * NSEC3s end up in the answer (due to qtype=NSEC3 or so) */ chase_reply->security = nsec3_prove_nameerror(env, ve, chase_reply->rrsets, chase_reply->an_numrrsets+ - chase_reply->ns_numrrsets, qchase, kkey); - if(chase_reply->security != sec_status_secure) { + chase_reply->ns_numrrsets, qchase, kkey, + &vq->nsec3_cache_table, nsec3_calculations); + if(chase_reply->security == sec_status_unchecked) { + *suspend = 1; + return; + } else if(chase_reply->security != sec_status_secure) { verbose(VERB_QUERY, "NameError response failed nsec, " "nsec3 proof was %s", sec_status_to_string( chase_reply->security)); @@ -1065,26 +1249,34 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve, /* If the message fails to prove either condition, it is bogus. */ if(!has_valid_nsec) { + validate_nodata_response(env, ve, qchase, chase_reply, kkey, + qstate, vq, nsec3_calculations, suspend); + if(*suspend) return; verbose(VERB_QUERY, "NameError response has failed to prove: " "qname does not exist"); - chase_reply->security = sec_status_bogus; - update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); /* Be lenient with RCODE in NSEC NameError responses */ - validate_nodata_response(env, ve, qchase, chase_reply, kkey); - if (chase_reply->security == sec_status_secure) + if(chase_reply->security == sec_status_secure) { *rcode = LDNS_RCODE_NOERROR; + } else { + chase_reply->security = sec_status_bogus; + update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); + } return; } if(!has_valid_wnsec) { + validate_nodata_response(env, ve, qchase, chase_reply, kkey, + qstate, vq, nsec3_calculations, suspend); + if(*suspend) return; verbose(VERB_QUERY, "NameError response has failed to prove: " "covering wildcard does not exist"); - chase_reply->security = sec_status_bogus; - update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); /* Be lenient with RCODE in NSEC NameError responses */ - validate_nodata_response(env, ve, qchase, chase_reply, kkey); - if (chase_reply->security == sec_status_secure) + if (chase_reply->security == sec_status_secure) { *rcode = LDNS_RCODE_NOERROR; + } else { + chase_reply->security = sec_status_bogus; + update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); + } return; } @@ -1144,11 +1336,17 @@ validate_referral_response(struct reply_info* chase_reply) * @param chase_reply: answer to that query to validate. * @param kkey: the key entry, which is trusted, and which matches * the signer of the answer. The key entry isgood(). + * @param qstate: query state for the region. + * @param vq: validator state for the nsec3 cache table. + * @param nsec3_calculations: current nsec3 hash calculations. + * @param suspend: returned true if the task takes too long and needs to + * suspend to continue the effort later. */ static void validate_any_response(struct module_env* env, struct val_env* ve, struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* kkey) + struct key_entry_key* kkey, struct module_qstate* qstate, + struct val_qstate* vq, int* nsec3_calculations, int* suspend) { /* all answer and auth rrsets already verified */ /* but check if a wildcard response is given, then check NSEC/NSEC3 @@ -1159,6 +1357,7 @@ validate_any_response(struct module_env* env, struct val_env* ve, int nsec3s_seen = 0; size_t i; struct ub_packed_rrset_key* s; + *suspend = 0; if(qchase->qtype != LDNS_RR_TYPE_ANY) { log_err("internal error: ANY validation called for non-ANY"); @@ -1213,19 +1412,25 @@ validate_any_response(struct module_env* env, struct val_env* ve, /* If this was a positive wildcard response that we haven't already * proven, and we have NSEC3 records, try to prove it using the NSEC3 * records. */ - if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) { + if(wc != NULL && !wc_NSEC_ok && nsec3s_seen && + nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { /* look both in answer and auth section for NSEC3s */ enum sec_status sec = nsec3_prove_wildcard(env, ve, chase_reply->rrsets, chase_reply->an_numrrsets+chase_reply->ns_numrrsets, - qchase, kkey, wc); + qchase, kkey, wc, &vq->nsec3_cache_table, + nsec3_calculations); if(sec == sec_status_insecure) { verbose(VERB_ALGO, "Positive ANY wildcard response is " "insecure"); chase_reply->security = sec_status_insecure; return; - } else if(sec == sec_status_secure) + } else if(sec == sec_status_secure) { wc_NSEC_ok = 1; + } else if(sec == sec_status_unchecked) { + *suspend = 1; + return; + } } /* If after all this, we still haven't proven the positive wildcard @@ -1258,11 +1463,17 @@ validate_any_response(struct module_env* env, struct val_env* ve, * @param chase_reply: answer to that query to validate. * @param kkey: the key entry, which is trusted, and which matches * the signer of the answer. The key entry isgood(). + * @param qstate: query state for the region. + * @param vq: validator state for the nsec3 cache table. + * @param nsec3_calculations: current nsec3 hash calculations. + * @param suspend: returned true if the task takes too long and needs to + * suspend to continue the effort later. */ static void validate_cname_response(struct module_env* env, struct val_env* ve, struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* kkey) + struct key_entry_key* kkey, struct module_qstate* qstate, + struct val_qstate* vq, int* nsec3_calculations, int* suspend) { uint8_t* wc = NULL; size_t wl; @@ -1270,6 +1481,7 @@ validate_cname_response(struct module_env* env, struct val_env* ve, int nsec3s_seen = 0; size_t i; struct ub_packed_rrset_key* s; + *suspend = 0; /* validate the ANSWER section - this will be the CNAME (+DNAME) */ for(i=0; ian_numrrsets; i++) { @@ -1334,17 +1546,23 @@ validate_cname_response(struct module_env* env, struct val_env* ve, /* If this was a positive wildcard response that we haven't already * proven, and we have NSEC3 records, try to prove it using the NSEC3 * records. */ - if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) { + if(wc != NULL && !wc_NSEC_ok && nsec3s_seen && + nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { enum sec_status sec = nsec3_prove_wildcard(env, ve, chase_reply->rrsets+chase_reply->an_numrrsets, - chase_reply->ns_numrrsets, qchase, kkey, wc); + chase_reply->ns_numrrsets, qchase, kkey, wc, + &vq->nsec3_cache_table, nsec3_calculations); if(sec == sec_status_insecure) { verbose(VERB_ALGO, "wildcard CNAME response is " "insecure"); chase_reply->security = sec_status_insecure; return; - } else if(sec == sec_status_secure) + } else if(sec == sec_status_secure) { wc_NSEC_ok = 1; + } else if(sec == sec_status_unchecked) { + *suspend = 1; + return; + } } /* If after all this, we still haven't proven the positive wildcard @@ -1375,11 +1593,17 @@ validate_cname_response(struct module_env* env, struct val_env* ve, * @param chase_reply: answer to that query to validate. * @param kkey: the key entry, which is trusted, and which matches * the signer of the answer. The key entry isgood(). + * @param qstate: query state for the region. + * @param vq: validator state for the nsec3 cache table. + * @param nsec3_calculations: current nsec3 hash calculations. + * @param suspend: returned true if the task takes too long and needs to + * suspend to continue the effort later. */ static void validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* kkey) + struct key_entry_key* kkey, struct module_qstate* qstate, + struct val_qstate* vq, int* nsec3_calculations, int* suspend) { int nodata_valid_nsec = 0; /* If true, then NODATA has been proven.*/ uint8_t* ce = NULL; /* for wildcard nodata responses. This is the @@ -1393,6 +1617,7 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, uint8_t* nsec_ce; /* Used to find the NSEC with the longest ce */ int ce_labs = 0; int prev_ce_labs = 0; + *suspend = 0; /* the AUTHORITY section */ for(i=chase_reply->an_numrrsets; ian_numrrsets+ @@ -1458,11 +1683,13 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); return; } - if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen) { + if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen && + nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { int nodata; enum sec_status sec = nsec3_prove_nxornodata(env, ve, chase_reply->rrsets+chase_reply->an_numrrsets, - chase_reply->ns_numrrsets, qchase, kkey, &nodata); + chase_reply->ns_numrrsets, qchase, kkey, &nodata, + &vq->nsec3_cache_table, nsec3_calculations); if(sec == sec_status_insecure) { verbose(VERB_ALGO, "CNAMEchain to noanswer response " "is insecure"); @@ -1472,6 +1699,9 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, if(nodata) nodata_valid_nsec = 1; else nxdomain_valid_nsec = 1; + } else if(sec == sec_status_unchecked) { + *suspend = 1; + return; } } @@ -1815,13 +2045,37 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id) * Uses negative cache for NSEC3 lookup of DS responses. */ /* only if cache not blacklisted, of course */ struct dns_msg* msg; - if(!qstate->blacklist && !vq->chain_blacklist && + int suspend; + if(vq->sub_ds_msg) { + /* We have a suspended DS reply from a sub-query; + * process it. */ + verbose(VERB_ALGO, "Process suspended sub DS response"); + msg = vq->sub_ds_msg; + process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR, + msg, &msg->qinfo, NULL, &suspend); + if(suspend) { + /* we'll come back here later to continue */ + if(!validate_suspend_setup_timer(qstate, vq, + id, VAL_FINDKEY_STATE)) + return val_error(qstate, id); + return 0; + } + vq->sub_ds_msg = NULL; + return 1; /* continue processing ds-response results */ + } else if(!qstate->blacklist && !vq->chain_blacklist && (msg=val_find_DS(qstate->env, target_key_name, target_key_len, vq->qchase.qclass, qstate->region, vq->key_entry->name)) ) { verbose(VERB_ALGO, "Process cached DS response"); process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR, - msg, &msg->qinfo, NULL); + msg, &msg->qinfo, NULL, &suspend); + if(suspend) { + /* we'll come back here later to continue */ + if(!validate_suspend_setup_timer(qstate, vq, + id, VAL_FINDKEY_STATE)) + return val_error(qstate, id); + return 0; + } return 1; /* continue processing ds-response results */ } if(!generate_request(qstate, id, target_key_name, @@ -1864,7 +2118,7 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, struct val_env* ve, int id) { enum val_classification subtype; - int rcode; + int rcode, suspend, nsec3_calculations = 0; if(!vq->key_entry) { verbose(VERB_ALGO, "validate: no key entry, failed"); @@ -1921,8 +2175,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, /* check signatures in the message; * answer and authority must be valid, additional is only checked. */ - if(!validate_msg_signatures(qstate, qstate->env, ve, &vq->qchase, - vq->chase_reply, vq->key_entry)) { + if(!validate_msg_signatures(qstate, vq, qstate->env, ve, &vq->qchase, + vq->chase_reply, vq->key_entry, &suspend)) { + if(suspend) { + if(!validate_suspend_setup_timer(qstate, vq, + id, VAL_VALIDATE_STATE)) + return val_error(qstate, id); + return 0; + } /* workaround bad recursor out there that truncates (even * with EDNS4k) to 512 by removing RRSIG from auth section * for positive replies*/ @@ -1951,7 +2211,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, case VAL_CLASS_POSITIVE: verbose(VERB_ALGO, "Validating a positive response"); validate_positive_response(qstate->env, ve, - &vq->qchase, vq->chase_reply, vq->key_entry); + &vq->qchase, vq->chase_reply, vq->key_entry, + qstate, vq, &nsec3_calculations, &suspend); + if(suspend) { + if(!validate_suspend_setup_timer(qstate, + vq, id, VAL_VALIDATE_STATE)) + return val_error(qstate, id); + return 0; + } verbose(VERB_DETAIL, "validate(positive): %s", sec_status_to_string( vq->chase_reply->security)); @@ -1960,7 +2227,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, case VAL_CLASS_NODATA: verbose(VERB_ALGO, "Validating a nodata response"); validate_nodata_response(qstate->env, ve, - &vq->qchase, vq->chase_reply, vq->key_entry); + &vq->qchase, vq->chase_reply, vq->key_entry, + qstate, vq, &nsec3_calculations, &suspend); + if(suspend) { + if(!validate_suspend_setup_timer(qstate, + vq, id, VAL_VALIDATE_STATE)) + return val_error(qstate, id); + return 0; + } verbose(VERB_DETAIL, "validate(nodata): %s", sec_status_to_string( vq->chase_reply->security)); @@ -1970,7 +2244,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, rcode = (int)FLAGS_GET_RCODE(vq->orig_msg->rep->flags); verbose(VERB_ALGO, "Validating a nxdomain response"); validate_nameerror_response(qstate->env, ve, - &vq->qchase, vq->chase_reply, vq->key_entry, &rcode); + &vq->qchase, vq->chase_reply, vq->key_entry, &rcode, + qstate, vq, &nsec3_calculations, &suspend); + if(suspend) { + if(!validate_suspend_setup_timer(qstate, + vq, id, VAL_VALIDATE_STATE)) + return val_error(qstate, id); + return 0; + } verbose(VERB_DETAIL, "validate(nxdomain): %s", sec_status_to_string( vq->chase_reply->security)); @@ -1981,7 +2262,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, case VAL_CLASS_CNAME: verbose(VERB_ALGO, "Validating a cname response"); validate_cname_response(qstate->env, ve, - &vq->qchase, vq->chase_reply, vq->key_entry); + &vq->qchase, vq->chase_reply, vq->key_entry, + qstate, vq, &nsec3_calculations, &suspend); + if(suspend) { + if(!validate_suspend_setup_timer(qstate, + vq, id, VAL_VALIDATE_STATE)) + return val_error(qstate, id); + return 0; + } verbose(VERB_DETAIL, "validate(cname): %s", sec_status_to_string( vq->chase_reply->security)); @@ -1991,7 +2279,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, verbose(VERB_ALGO, "Validating a cname noanswer " "response"); validate_cname_noanswer_response(qstate->env, ve, - &vq->qchase, vq->chase_reply, vq->key_entry); + &vq->qchase, vq->chase_reply, vq->key_entry, + qstate, vq, &nsec3_calculations, &suspend); + if(suspend) { + if(!validate_suspend_setup_timer(qstate, + vq, id, VAL_VALIDATE_STATE)) + return val_error(qstate, id); + return 0; + } verbose(VERB_DETAIL, "validate(cname_noanswer): %s", sec_status_to_string( vq->chase_reply->security)); @@ -2009,7 +2304,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, verbose(VERB_ALGO, "Validating a positive ANY " "response"); validate_any_response(qstate->env, ve, &vq->qchase, - vq->chase_reply, vq->key_entry); + vq->chase_reply, vq->key_entry, qstate, vq, + &nsec3_calculations, &suspend); + if(suspend) { + if(!validate_suspend_setup_timer(qstate, + vq, id, VAL_VALIDATE_STATE)) + return val_error(qstate, id); + return 0; + } verbose(VERB_DETAIL, "validate(positive_any): %s", sec_status_to_string( vq->chase_reply->security)); @@ -2118,16 +2420,13 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq, if(vq->orig_msg->rep->security == sec_status_bogus) { /* see if we can try again to fetch data */ if(vq->restart_count < ve->max_restart) { - int restart_count = vq->restart_count+1; verbose(VERB_ALGO, "validation failed, " "blacklist and retry to fetch data"); val_blacklist(&qstate->blacklist, qstate->region, qstate->reply_origin, 0); qstate->reply_origin = NULL; qstate->errinf = NULL; - memset(vq, 0, sizeof(*vq)); - vq->restart_count = restart_count; - vq->state = VAL_INIT_STATE; + val_restart(vq); verbose(VERB_ALGO, "pass back to next module"); qstate->ext_state[id] = module_restart_next; return 0; @@ -2454,7 +2753,10 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, * DS response indicated an end to secure space, is_good if the DS * validated. It returns ke=NULL if the DS response indicated that the * request wasn't a delegation point. - * @return 0 on servfail error (malloc failure). + * @return + * 0 on success, + * 1 on servfail error (malloc failure), + * 2 on NSEC3 suspend. */ static int ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, @@ -2465,6 +2767,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, char* reason = NULL; sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; enum val_classification subtype; + int verified; if(rcode != LDNS_RCODE_NOERROR) { char rc[16]; rc[0]=0; @@ -2495,7 +2798,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, /* Verify only returns BOGUS or SECURE. If the rrset is * bogus, then we are done. */ sec = val_verify_rrset_entry(qstate->env, ve, ds, - vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate); + vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified); if(sec != sec_status_secure) { verbose(VERB_DETAIL, "DS rrset in DS response did " "not verify"); @@ -2513,7 +2816,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, ub_packed_rrset_ttl(ds), LDNS_EDE_UNSUPPORTED_DS_DIGEST, NULL, *qstate->env->now); - return (*ke) != NULL; + return (*ke) == NULL; } /* Otherwise, we return the positive response. */ @@ -2521,7 +2824,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, *ke = key_entry_create_rrset(qstate->region, qinfo->qname, qinfo->qname_len, qinfo->qclass, ds, NULL, LDNS_EDE_NONE, NULL, *qstate->env->now); - return (*ke) != NULL; + return (*ke) == NULL; } else if(subtype == VAL_CLASS_NODATA || subtype == VAL_CLASS_NAMEERROR) { /* NODATA means that the qname exists, but that there was @@ -2555,12 +2858,12 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, qinfo->qclass, proof_ttl, LDNS_EDE_NONE, NULL, *qstate->env->now); - return (*ke) != NULL; + return (*ke) == NULL; case sec_status_insecure: verbose(VERB_DETAIL, "NSEC RRset for the " "referral proved not a delegation point"); *ke = NULL; - return 1; + return 0; case sec_status_bogus: verbose(VERB_DETAIL, "NSEC RRset for the " "referral did not prove no DS."); @@ -2572,10 +2875,17 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, break; } + if(!nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { + log_err("malloc failure in ds_response_to_ke for " + "NSEC3 cache"); + reason = "malloc failure"; + errinf_ede(qstate, reason, 0); + goto return_bogus; + } sec = nsec3_prove_nods(qstate->env, ve, msg->rep->rrsets + msg->rep->an_numrrsets, msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason, - &reason_bogus, qstate); + &reason_bogus, qstate, &vq->nsec3_cache_table); switch(sec) { case sec_status_insecure: /* case insecure also continues to unsigned @@ -2589,18 +2899,19 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, qinfo->qclass, proof_ttl, LDNS_EDE_NONE, NULL, *qstate->env->now); - return (*ke) != NULL; + return (*ke) == NULL; case sec_status_indeterminate: verbose(VERB_DETAIL, "NSEC3s for the " "referral proved no delegation"); *ke = NULL; - return 1; + return 0; case sec_status_bogus: verbose(VERB_DETAIL, "NSEC3s for the " "referral did not prove no DS."); errinf_ede(qstate, reason, reason_bogus); goto return_bogus; case sec_status_unchecked: + return 2; default: /* NSEC3 proof did not work */ break; @@ -2641,13 +2952,13 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, } sec = val_verify_rrset_entry(qstate->env, ve, cname, vq->key_entry, &reason, &reason_bogus, - LDNS_SECTION_ANSWER, qstate); + LDNS_SECTION_ANSWER, qstate, &verified); if(sec == sec_status_secure) { verbose(VERB_ALGO, "CNAME validated, " "proof that DS does not exist"); /* and that it is not a referral point */ *ke = NULL; - return 1; + return 0; } errinf(qstate, "CNAME in DS response was not secure."); errinf_ede(qstate, reason, reason_bogus); @@ -2671,7 +2982,7 @@ return_bogus: *ke = key_entry_create_bad(qstate->region, qinfo->qname, qinfo->qname_len, qinfo->qclass, BOGUS_KEY_TTL, reason_bogus, reason, *qstate->env->now); - return (*ke) != NULL; + return (*ke) == NULL; } /** @@ -2692,17 +3003,31 @@ return_bogus: static void process_ds_response(struct module_qstate* qstate, struct val_qstate* vq, int id, int rcode, struct dns_msg* msg, struct query_info* qinfo, - struct sock_list* origin) + struct sock_list* origin, int* suspend) { struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; struct key_entry_key* dske = NULL; uint8_t* olds = vq->empty_DS_name; + int ret; + *suspend = 0; vq->empty_DS_name = NULL; - if(!ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske)) { + ret = ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske); + if(ret != 0) { + switch(ret) { + case 1: log_err("malloc failure in process_ds_response"); vq->key_entry = NULL; /* make it error */ vq->state = VAL_VALIDATE_STATE; return; + case 2: + *suspend = 1; + return; + default: + log_err("unhandled error value for ds_response_to_ke"); + vq->key_entry = NULL; /* make it error */ + vq->state = VAL_VALIDATE_STATE; + return; + } } if(dske == NULL) { vq->empty_DS_name = regional_alloc_init(qstate->region, @@ -2954,9 +3279,26 @@ val_inform_super(struct module_qstate* qstate, int id, return; } if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS) { + int suspend; process_ds_response(super, vq, id, qstate->return_rcode, qstate->return_msg, &qstate->qinfo, - qstate->reply_origin); + qstate->reply_origin, &suspend); + /* If NSEC3 was needed during validation, NULL the NSEC3 cache; + * it will be re-initiated if needed later on. + * Validation (and the cache table) are happening/allocated in + * the super qstate whilst the RRs are allocated (and pointed + * to) in this sub qstate. */ + if(vq->nsec3_cache_table.ct) { + vq->nsec3_cache_table.ct = NULL; + } + if(suspend) { + /* deep copy the return_msg to vq->sub_ds_msg; it will + * be resumed later in the super state with the caveat + * that the initial calculations will be re-caclulated + * and re-suspended there before continuing. */ + vq->sub_ds_msg = dns_msg_deepcopy_region( + qstate->return_msg, super->region); + } return; } else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY) { process_dnskey_response(super, vq, id, qstate->return_rcode, @@ -2970,8 +3312,15 @@ val_inform_super(struct module_qstate* qstate, int id, void val_clear(struct module_qstate* qstate, int id) { + struct val_qstate* vq; if(!qstate) return; + vq = (struct val_qstate*)qstate->minfo[id]; + if(vq) { + if(vq->suspend_timer) { + comm_timer_delete(vq->suspend_timer); + } + } /* everything is allocated in the region, so assign NULL */ qstate->minfo[id] = NULL; } diff --git a/usr.sbin/unbound/validator/validator.h b/usr.sbin/unbound/validator/validator.h index b620c1e98..ab5cb970f 100644 --- a/usr.sbin/unbound/validator/validator.h +++ b/usr.sbin/unbound/validator/validator.h @@ -45,11 +45,13 @@ #include "util/module.h" #include "util/data/msgreply.h" #include "validator/val_utils.h" +#include "validator/val_nsec3.h" struct val_anchors; struct key_cache; struct key_entry_key; struct val_neg_cache; struct config_strlist; +struct comm_timer; /** * This is the TTL to use when a trust anchor fails to prime. A trust anchor @@ -215,6 +217,19 @@ struct val_qstate { /** true if this state is waiting to prime a trust anchor */ int wait_prime_ta; + + /** State to continue with RRSIG validation in a message later */ + int msg_signatures_state; + /** The rrset index for the msg signatures to continue from */ + size_t msg_signatures_index; + /** Cache table for NSEC3 hashes */ + struct nsec3_cache_table nsec3_cache_table; + /** DS message from sub if it got suspended from NSEC3 calculations */ + struct dns_msg* sub_ds_msg; + /** The timer to resume processing msg signatures */ + struct comm_timer* suspend_timer; + /** Number of suspends */ + int suspend_count; }; /** @@ -262,4 +277,7 @@ void val_clear(struct module_qstate* qstate, int id); */ size_t val_get_mem(struct module_env* env, int id); +/** Timer callback for msg signatures continue timer */ +void validate_suspend_timer_cb(void* arg); + #endif /* VALIDATOR_VALIDATOR_H */