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