diff options
| -rw-r--r-- | meta-networking/recipes-support/unbound/unbound/CVE-2025-5994.patch | 275 | ||||
| -rw-r--r-- | meta-networking/recipes-support/unbound/unbound_1.19.3.bb | 1 |
2 files changed, 276 insertions, 0 deletions
diff --git a/meta-networking/recipes-support/unbound/unbound/CVE-2025-5994.patch b/meta-networking/recipes-support/unbound/unbound/CVE-2025-5994.patch new file mode 100644 index 0000000000..b6c0e37e43 --- /dev/null +++ b/meta-networking/recipes-support/unbound/unbound/CVE-2025-5994.patch | |||
| @@ -0,0 +1,275 @@ | |||
| 1 | Backport of: | ||
| 2 | |||
| 3 | From 5bf82f246481098a6473f296b21fc1229d276c0f Mon Sep 17 00:00:00 2001 | ||
| 4 | From: "W.C.A. Wijngaards" <wouter@nlnetlabs.nl> | ||
| 5 | Date: Wed, 16 Jul 2025 10:02:01 +0200 | ||
| 6 | Subject: [PATCH] - Fix RebirthDay Attack CVE-2025-5994, reported by Xiang Li | ||
| 7 | from AOSP Lab Nankai University. | ||
| 8 | |||
| 9 | CVE: CVE-2025-5994 | ||
| 10 | Upstream-Status: Backport [https://github.com/NLnetLabs/unbound/commit/5bf82f246481098a6473f296b21fc1229d276c0f] | ||
| 11 | Signed-off-by: Naman Jain <namanj1@kpit.com> | ||
| 12 | |||
| 13 | --- | ||
| 14 | edns-subnet/subnetmod.c | 152 ++++++++++++++++++++++++++++++++++++---- | ||
| 15 | edns-subnet/subnetmod.h | 4 ++ | ||
| 16 | 2 files changed, 142 insertions(+), 14 deletions(-) | ||
| 17 | |||
| 18 | --- a/edns-subnet/subnetmod.c | ||
| 19 | +++ b/edns-subnet/subnetmod.c | ||
| 20 | @@ -51,6 +51,7 @@ | ||
| 21 | #include "services/cache/dns.h" | ||
| 22 | #include "util/module.h" | ||
| 23 | #include "util/regional.h" | ||
| 24 | +#include "util/fptr_wlist.h" | ||
| 25 | #include "util/storage/slabhash.h" | ||
| 26 | #include "util/config_file.h" | ||
| 27 | #include "util/data/msgreply.h" | ||
| 28 | @@ -152,7 +153,8 @@ int ecs_whitelist_check(struct query_inf | ||
| 29 | |||
| 30 | /* Cache by default, might be disabled after parsing EDNS option | ||
| 31 | * received from nameserver. */ | ||
| 32 | - if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL)) { | ||
| 33 | + if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL) | ||
| 34 | + && sq->ecs_client_in.subnet_validdata) { | ||
| 35 | qstate->no_cache_store = 0; | ||
| 36 | } | ||
| 37 | |||
| 38 | @@ -504,6 +506,69 @@ common_prefix(uint8_t *a, uint8_t *b, ui | ||
| 39 | return !memcmp(a, b, n) && ((net % 8) == 0 || a[n] == b[n]); | ||
| 40 | } | ||
| 41 | |||
| 42 | +/** | ||
| 43 | + * Create sub request that looks up the query. | ||
| 44 | + * @param qstate: query state | ||
| 45 | + * @param sq: subnet qstate | ||
| 46 | + * @return false on failure. | ||
| 47 | + */ | ||
| 48 | +static int | ||
| 49 | +generate_sub_request(struct module_qstate *qstate, struct subnet_qstate* sq) | ||
| 50 | +{ | ||
| 51 | + struct module_qstate* subq = NULL; | ||
| 52 | + uint16_t qflags = 0; /* OPCODE QUERY, no flags */ | ||
| 53 | + int prime = 0; | ||
| 54 | + int valrec = 0; | ||
| 55 | + struct query_info qinf; | ||
| 56 | + qinf.qname = qstate->qinfo.qname; | ||
| 57 | + qinf.qname_len = qstate->qinfo.qname_len; | ||
| 58 | + qinf.qtype = qstate->qinfo.qtype; | ||
| 59 | + qinf.qclass = qstate->qinfo.qclass; | ||
| 60 | + qinf.local_alias = NULL; | ||
| 61 | + | ||
| 62 | + qflags |= BIT_RD; | ||
| 63 | + if((qstate->query_flags & BIT_CD)!=0) { | ||
| 64 | + qflags |= BIT_CD; | ||
| 65 | + valrec = 1; | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); | ||
| 69 | + if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, valrec, | ||
| 70 | + &subq)) { | ||
| 71 | + return 0; | ||
| 72 | + } | ||
| 73 | + if(subq) { | ||
| 74 | + /* It is possible to access the subquery module state. */ | ||
| 75 | + if(sq->ecs_client_in.subnet_source_mask == 0 && | ||
| 76 | + edns_opt_list_find(qstate->edns_opts_front_in, | ||
| 77 | + qstate->env->cfg->client_subnet_opcode)) { | ||
| 78 | + subq->no_cache_store = 1; | ||
| 79 | + } | ||
| 80 | + } | ||
| 81 | + return 1; | ||
| 82 | +} | ||
| 83 | + | ||
| 84 | +/** | ||
| 85 | + * Perform the query without subnet | ||
| 86 | + * @param qstate: query state | ||
| 87 | + * @param sq: subnet qstate | ||
| 88 | + * @return module state | ||
| 89 | + */ | ||
| 90 | +static enum module_ext_state | ||
| 91 | +generate_lookup_without_subnet(struct module_qstate *qstate, | ||
| 92 | + struct subnet_qstate* sq) | ||
| 93 | +{ | ||
| 94 | + verbose(VERB_ALGO, "subnetcache: make subquery to look up without subnet"); | ||
| 95 | + if(!generate_sub_request(qstate, sq)) { | ||
| 96 | + verbose(VERB_ALGO, "Could not generate sub query"); | ||
| 97 | + qstate->return_rcode = LDNS_RCODE_FORMERR; | ||
| 98 | + qstate->return_msg = NULL; | ||
| 99 | + return module_finished; | ||
| 100 | + } | ||
| 101 | + sq->wait_subquery = 1; | ||
| 102 | + return module_wait_subquery; | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | static enum module_ext_state | ||
| 106 | eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) | ||
| 107 | { | ||
| 108 | @@ -539,14 +604,7 @@ eval_response(struct module_qstate *qsta | ||
| 109 | * is still useful to put it in the edns subnet cache for | ||
| 110 | * when a client explicitly asks for subnet specific answer. */ | ||
| 111 | verbose(VERB_QUERY, "subnetcache: Authority indicates no support"); | ||
| 112 | - if(!sq->started_no_cache_store) { | ||
| 113 | - lock_rw_wrlock(&sne->biglock); | ||
| 114 | - update_cache(qstate, id); | ||
| 115 | - lock_rw_unlock(&sne->biglock); | ||
| 116 | - } | ||
| 117 | - if (sq->subnet_downstream) | ||
| 118 | - cp_edns_bad_response(c_out, c_in); | ||
| 119 | - return module_finished; | ||
| 120 | + return generate_lookup_without_subnet(qstate, sq); | ||
| 121 | } | ||
| 122 | |||
| 123 | /* Purposefully there was no sent subnet, and there is consequently | ||
| 124 | @@ -571,14 +629,14 @@ eval_response(struct module_qstate *qsta | ||
| 125 | !common_prefix(s_out->subnet_addr, s_in->subnet_addr, | ||
| 126 | s_out->subnet_source_mask)) | ||
| 127 | { | ||
| 128 | - /* we can not accept, restart query without option */ | ||
| 129 | + /* we can not accept, perform query without option */ | ||
| 130 | verbose(VERB_QUERY, "subnetcache: forged data"); | ||
| 131 | s_out->subnet_validdata = 0; | ||
| 132 | (void)edns_opt_list_remove(&qstate->edns_opts_back_out, | ||
| 133 | qstate->env->cfg->client_subnet_opcode); | ||
| 134 | sq->subnet_sent = 0; | ||
| 135 | sq->subnet_sent_no_subnet = 0; | ||
| 136 | - return module_restart_next; | ||
| 137 | + return generate_lookup_without_subnet(qstate, sq); | ||
| 138 | } | ||
| 139 | |||
| 140 | lock_rw_wrlock(&sne->biglock); | ||
| 141 | @@ -763,6 +821,9 @@ ecs_edns_back_parsed(struct module_qstat | ||
| 142 | } else if(sq->subnet_sent_no_subnet) { | ||
| 143 | /* The answer can be stored as scope 0, not in global cache. */ | ||
| 144 | qstate->no_cache_store = 1; | ||
| 145 | + } else if(sq->subnet_sent) { | ||
| 146 | + /* Need another query to be able to store in global cache. */ | ||
| 147 | + qstate->no_cache_store = 1; | ||
| 148 | } | ||
| 149 | |||
| 150 | return 1; | ||
| 151 | @@ -780,6 +841,32 @@ subnetmod_operate(struct module_qstate * | ||
| 152 | strmodulevent(event)); | ||
| 153 | log_query_info(VERB_QUERY, "subnetcache operate: query", &qstate->qinfo); | ||
| 154 | |||
| 155 | + if(sq && sq->wait_subquery_done) { | ||
| 156 | + /* The subquery lookup returned. */ | ||
| 157 | + if(sq->ecs_client_in.subnet_source_mask == 0 && | ||
| 158 | + edns_opt_list_find(qstate->edns_opts_front_in, | ||
| 159 | + qstate->env->cfg->client_subnet_opcode)) { | ||
| 160 | + if(!sq->started_no_cache_store && | ||
| 161 | + qstate->return_msg) { | ||
| 162 | + lock_rw_wrlock(&sne->biglock); | ||
| 163 | + update_cache(qstate, id); | ||
| 164 | + lock_rw_unlock(&sne->biglock); | ||
| 165 | + } | ||
| 166 | + if (sq->subnet_downstream) | ||
| 167 | + cp_edns_bad_response(&sq->ecs_client_out, | ||
| 168 | + &sq->ecs_client_in); | ||
| 169 | + /* It is a scope zero lookup, append edns subnet | ||
| 170 | + * option to the querier. */ | ||
| 171 | + subnet_ecs_opt_list_append(&sq->ecs_client_out, | ||
| 172 | + &qstate->edns_opts_front_out, qstate, | ||
| 173 | + qstate->region); | ||
| 174 | + } | ||
| 175 | + sq->wait_subquery_done = 0; | ||
| 176 | + qstate->ext_state[id] = module_finished; | ||
| 177 | + qstate->no_cache_store = sq->started_no_cache_store; | ||
| 178 | + qstate->no_cache_lookup = sq->started_no_cache_lookup; | ||
| 179 | + return; | ||
| 180 | + } | ||
| 181 | if((event == module_event_new || event == module_event_pass) && | ||
| 182 | sq == NULL) { | ||
| 183 | struct edns_option* ecs_opt; | ||
| 184 | @@ -790,6 +877,8 @@ subnetmod_operate(struct module_qstate * | ||
| 185 | } | ||
| 186 | |||
| 187 | sq = (struct subnet_qstate*)qstate->minfo[id]; | ||
| 188 | + if(sq->wait_subquery) | ||
| 189 | + return; /* Wait for that subquery to return */ | ||
| 190 | |||
| 191 | if((ecs_opt = edns_opt_list_find( | ||
| 192 | qstate->edns_opts_front_in, | ||
| 193 | @@ -819,6 +908,14 @@ subnetmod_operate(struct module_qstate * | ||
| 194 | /* No clients are interested in result or we could not | ||
| 195 | * parse it, we don't do client subnet */ | ||
| 196 | sq->ecs_server_out.subnet_validdata = 0; | ||
| 197 | + if(edns_opt_list_find(qstate->edns_opts_front_in, | ||
| 198 | + qstate->env->cfg->client_subnet_opcode)) { | ||
| 199 | + /* aggregated this deaggregated state */ | ||
| 200 | + qstate->ext_state[id] = | ||
| 201 | + generate_lookup_without_subnet( | ||
| 202 | + qstate, sq); | ||
| 203 | + return; | ||
| 204 | + } | ||
| 205 | verbose(VERB_ALGO, "subnetcache: pass to next module"); | ||
| 206 | qstate->ext_state[id] = module_wait_module; | ||
| 207 | return; | ||
| 208 | @@ -859,6 +956,14 @@ subnetmod_operate(struct module_qstate * | ||
| 209 | } | ||
| 210 | lock_rw_unlock(&sne->biglock); | ||
| 211 | } | ||
| 212 | + if(sq->ecs_client_in.subnet_source_mask == 0 && | ||
| 213 | + edns_opt_list_find(qstate->edns_opts_front_in, | ||
| 214 | + qstate->env->cfg->client_subnet_opcode)) { | ||
| 215 | + /* client asked for resolution without edns subnet */ | ||
| 216 | + qstate->ext_state[id] = generate_lookup_without_subnet( | ||
| 217 | + qstate, sq); | ||
| 218 | + return; | ||
| 219 | + } | ||
| 220 | |||
| 221 | sq->ecs_server_out.subnet_addr_fam = | ||
| 222 | sq->ecs_client_in.subnet_addr_fam; | ||
| 223 | @@ -895,6 +1000,8 @@ subnetmod_operate(struct module_qstate * | ||
| 224 | qstate->ext_state[id] = module_wait_module; | ||
| 225 | return; | ||
| 226 | } | ||
| 227 | + if(sq && sq->wait_subquery) | ||
| 228 | + return; /* Wait for that subquery to return */ | ||
| 229 | /* Query handed back by next module, we have a 'final' answer */ | ||
| 230 | if(sq && event == module_event_moddone) { | ||
| 231 | qstate->ext_state[id] = eval_response(qstate, id, sq); | ||
| 232 | @@ -943,10 +1050,27 @@ subnetmod_clear(struct module_qstate *AT | ||
| 233 | } | ||
| 234 | |||
| 235 | void | ||
| 236 | -subnetmod_inform_super(struct module_qstate *ATTR_UNUSED(qstate), | ||
| 237 | - int ATTR_UNUSED(id), struct module_qstate *ATTR_UNUSED(super)) | ||
| 238 | +subnetmod_inform_super(struct module_qstate *qstate, int id, | ||
| 239 | + struct module_qstate *super) | ||
| 240 | { | ||
| 241 | - /* Not used */ | ||
| 242 | + struct subnet_qstate* super_sq = | ||
| 243 | + (struct subnet_qstate*)super->minfo[id]; | ||
| 244 | + log_query_info(VERB_ALGO, "subnetcache inform_super: query", | ||
| 245 | + &super->qinfo); | ||
| 246 | + super_sq->wait_subquery = 0; | ||
| 247 | + super_sq->wait_subquery_done = 1; | ||
| 248 | + if(qstate->return_rcode != LDNS_RCODE_NOERROR || | ||
| 249 | + !qstate->return_msg) { | ||
| 250 | + super->return_msg = NULL; | ||
| 251 | + super->return_rcode = LDNS_RCODE_SERVFAIL; | ||
| 252 | + return; | ||
| 253 | + } | ||
| 254 | + super->return_rcode = LDNS_RCODE_NOERROR; | ||
| 255 | + super->return_msg = dns_copy_msg(qstate->return_msg, super->region); | ||
| 256 | + if(!super->return_msg) { | ||
| 257 | + log_err("subnetcache: copy response, out of memory"); | ||
| 258 | + super->return_rcode = LDNS_RCODE_SERVFAIL; | ||
| 259 | + } | ||
| 260 | } | ||
| 261 | |||
| 262 | size_t | ||
| 263 | --- a/edns-subnet/subnetmod.h | ||
| 264 | +++ b/edns-subnet/subnetmod.h | ||
| 265 | @@ -102,6 +102,10 @@ struct subnet_qstate { | ||
| 266 | int started_no_cache_store; | ||
| 267 | /** has the subnet module been started with no_cache_lookup? */ | ||
| 268 | int started_no_cache_lookup; | ||
| 269 | + /** Wait for subquery that has been started for nonsubnet lookup. */ | ||
| 270 | + int wait_subquery; | ||
| 271 | + /** The subquery waited for is done. */ | ||
| 272 | + int wait_subquery_done; | ||
| 273 | }; | ||
| 274 | |||
| 275 | void subnet_data_delete(void* d, void* ATTR_UNUSED(arg)); | ||
diff --git a/meta-networking/recipes-support/unbound/unbound_1.19.3.bb b/meta-networking/recipes-support/unbound/unbound_1.19.3.bb index 076f03f2ae..7e3e37406f 100644 --- a/meta-networking/recipes-support/unbound/unbound_1.19.3.bb +++ b/meta-networking/recipes-support/unbound/unbound_1.19.3.bb | |||
| @@ -16,6 +16,7 @@ SRC_URI = "git://github.com/NLnetLabs/unbound.git;protocol=https;nobranch=1 \ | |||
| 16 | file://CVE-2024-43167.patch \ | 16 | file://CVE-2024-43167.patch \ |
| 17 | file://CVE-2024-43168_1.patch \ | 17 | file://CVE-2024-43168_1.patch \ |
| 18 | file://CVE-2024-43168_2.patch \ | 18 | file://CVE-2024-43168_2.patch \ |
| 19 | file://CVE-2025-5994.patch \ | ||
| 19 | " | 20 | " |
| 20 | SRCREV = "48b6c60a24e9a5d6d369a7a37c9fe2a767f26abd" | 21 | SRCREV = "48b6c60a24e9a5d6d369a7a37c9fe2a767f26abd" |
| 21 | 22 | ||
