Open
fix(cve): 修复 CVE-2026-19668 在 bind 中的漏洞 #347
infra_teamAdd on Sep 17
fix(cve): 修复 CVE-2026-19668 在 bind 中的漏洞 #347
Open
Total: 2 files changed+480-1
| @@ -0,0 +1,472 @@ | |||
| 1 | +From: infra_team <zhaiwenjie1@huawei.com> | ||
| 2 | +Date: Thu, 17 Sep 2026 12:00:00 +0800 | ||
| 3 | +Subject: [PATCH] fix CVE-2026-19668: bound the DS-to-DNSKEY matching during DNSSEC validation | ||
| 4 | + | ||
| 5 | +Backport the upstream fix for CVE-2026-19668 (ISC BIND 9.20.29 / 9.21.26, | ||
| 6 | +Resource Exhaustion via Excessive DNSSEC Cryptographic Material Matching, | ||
| 7 | +GitLab #5349). | ||
| 8 | + | ||
| 9 | +A securely delegated zone can serve a DS RRset and a DNSKEY RRset that | ||
| 10 | +together hold many records with distinct key tags but no valid pairing. | ||
| 11 | +Validating the DNSKEY RRset then walks every DS and, for each one, derives | ||
| 12 | +key tags across the whole DNSKEY RRset -- matching work that was never | ||
| 13 | +counted against any limit, so an attacker-controlled zone could force a | ||
| 14 | +large amount of it per query. | ||
| 15 | + | ||
| 16 | +This backport charges each DS that matches no DNSKEY against the per-fetch | ||
| 17 | +validation quota so the walk stops once the quota is reached, rejects a | ||
| 18 | +DNSKEY RRset up front when the number of DS-by-DNSKEY combinations exceeds | ||
| 19 | +a small multiple of that quota | ||
| 20 | +(DS_DNSKEY_COMBINATIONS_PER_VALIDATION = 2), and makes an exhausted quota | ||
| 21 | +terminal instead of relabelling it as a broken trust chain or retrying, | ||
| 22 | +with the specific quota reason logged once at the common validator | ||
| 23 | +completion point. | ||
| 24 | + | ||
| 25 | +Upstream changes are commits ff3fa30f2dcaa6 ("Bound the DS-to-DNSKEY | ||
| 26 | +matching during DNSSEC validation") and ce25d582b475d1 ("Make the | ||
| 27 | +exhausted DNSSEC validation quota terminal"), merged upstream in | ||
| 28 | +a0a61dba9e1d4537b9e948b03a2879ba65000274 for the 9.20 branch. | ||
| 29 | + | ||
| 30 | +Adaptation for 9.20.21: upstream renamed validate_helper_run() to | ||
| 31 | +validate_work_enqueue() after 9.20.21; the two hunks touching | ||
| 32 | +validate_dnskey_dsset_first() / validate_dnskey_dsset_next_done() were | ||
| 33 | +rebased onto the 9.20.21 validate_helper_run()/validate_async_run() | ||
| 34 | +naming, keeping 9.20.21 behaviour otherwise unchanged. The test-only | ||
| 35 | +commits (e0079f0a, ced8e955) are not backported: the 9.20.21 tarball | ||
| 36 | +ships neither the bin/tests/system/dnssec_py suite nor | ||
| 37 | +bin/tests/system/dnssec/tests_validation.py that they extend, and the | ||
| 38 | +RPM build does not run the upstream system test suite. | ||
| 39 | + | ||
| 40 | +Upstream-commit: https://github.com/isc-projects/bind9/commit/a0a61dba9e1d4537b9e948b03a2879ba65000274 | ||
| 41 | +Upstream-commit: https://github.com/isc-projects/bind9/commit/ff3fa30f2dcaa6f48664f0ff4a5eef91f87bd364 | ||
| 42 | +Upstream-commit: https://github.com/isc-projects/bind9/commit/ce25d582b475d1d4ee22f48463cb8c547678584b | ||
| 43 | +Fixes: CVE-2026-19668 | ||
| 44 | +Signed-off-by: infra_team <zhaiwenjie1@huawei.com> | ||
| 45 | +--- | ||
| 46 | + lib/dns/include/dns/validator.h | 16 ++-- | ||
| 47 | + lib/dns/resolver.c | 63 ++++++++++------ | ||
| 48 | + lib/dns/validator.c | 157 ++++++++++++++++++++++++++++++---------- | ||
| 49 | + 3 files changed, 167 insertions(+), 69 deletions(-) | ||
| 50 | +diff --git a/lib/dns/include/dns/validator.h b/lib/dns/include/dns/validator.h | ||
| 51 | +index 362e7e7..2ba2f76 100644 | ||
| 52 | +--- a/lib/dns/include/dns/validator.h | ||
| 53 | ++++ b/lib/dns/include/dns/validator.h | ||
| 54 | +@@ -142,18 +142,18 @@ struct dns_validator { | ||
| 55 | + dns_fixedname_t wild; | ||
| 56 | + dns_fixedname_t closest; | ||
| 57 | + ISC_LINK(dns_validator_t) link; | ||
| 58 | +- bool mustbesecure; | ||
| 59 | +- unsigned int depth; | ||
| 60 | +- unsigned int authcount; | ||
| 61 | +- unsigned int authfail; | ||
| 62 | +- isc_stdtime_t start; | ||
| 63 | +- | ||
| 64 | ++ bool mustbesecure; | ||
| 65 | ++ unsigned int depth; | ||
| 66 | ++ unsigned int authcount; | ||
| 67 | ++ unsigned int authfail; | ||
| 68 | ++ isc_stdtime_t start; | ||
| 69 | ++ bool resume; | ||
| 70 | + bool digest_sha1; | ||
| 71 | + uint8_t unsupported_algorithm; | ||
| 72 | + uint8_t unsupported_digest; | ||
| 73 | +- uint8_t validation_attempts; | ||
| 74 | ++ uint16_t matchds_attempts; | ||
| 75 | ++ uint16_t validation_attempts; | ||
| 76 | + dns_rdata_t rdata; | ||
| 77 | +- bool resume; | ||
| 78 | + isc_counter_t *nvalidations; | ||
| 79 | + isc_counter_t *nfails; | ||
| 80 | + isc_counter_t *qc; | ||
| 81 | +diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c | ||
| 82 | +index aa7ffc4..27754a0 100644 | ||
| 83 | +--- a/lib/dns/resolver.c | ||
| 84 | ++++ b/lib/dns/resolver.c | ||
| 85 | +@@ -5452,27 +5452,14 @@ validated(void *arg) { | ||
| 86 | + inc_stats(res, dns_resstatscounter_valfail); | ||
| 87 | + fctx->valfail++; | ||
| 88 | + fctx->vresult = val->result; | ||
| 89 | +- if (fctx->vresult != DNS_R_BROKENCHAIN) { | ||
| 90 | +- result = ISC_R_NOTFOUND; | ||
| 91 | +- if (val->rdataset != NULL) { | ||
| 92 | +- result = dns_db_findnode(fctx->cache, val->name, | ||
| 93 | +- false, &node); | ||
| 94 | +- } | ||
| 95 | +- if (result == ISC_R_SUCCESS) { | ||
| 96 | +- (void)dns_db_deleterdataset(fctx->cache, node, | ||
| 97 | +- NULL, val->type, 0); | ||
| 98 | +- } | ||
| 99 | +- if (result == ISC_R_SUCCESS && val->sigrdataset != NULL) | ||
| 100 | +- { | ||
| 101 | +- (void)dns_db_deleterdataset( | ||
| 102 | +- fctx->cache, node, NULL, | ||
| 103 | +- dns_rdatatype_rrsig, val->type); | ||
| 104 | +- } | ||
| 105 | +- if (result == ISC_R_SUCCESS) { | ||
| 106 | +- dns_db_detachnode(fctx->cache, &node); | ||
| 107 | ++ switch (fctx->vresult) { | ||
| 108 | ++ case DNS_R_BROKENCHAIN: | ||
| 109 | ++ case ISC_R_CANCELED: | ||
| 110 | ++ case ISC_R_SHUTTINGDOWN: | ||
| 111 | ++ case ISC_R_QUOTA: | ||
| 112 | ++ if (negative) { | ||
| 113 | ++ break; | ||
| 114 | + } | ||
| 115 | +- } | ||
| 116 | +- if (fctx->vresult == DNS_R_BROKENCHAIN && !negative) { | ||
| 117 | + /* | ||
| 118 | + * Cache the data as pending for later | ||
| 119 | + * validation. | ||
| 120 | +@@ -5496,6 +5483,27 @@ validated(void *arg) { | ||
| 121 | + if (result == ISC_R_SUCCESS) { | ||
| 122 | + dns_db_detachnode(fctx->cache, &node); | ||
| 123 | + } | ||
| 124 | ++ break; | ||
| 125 | ++ default: | ||
| 126 | ++ result = ISC_R_NOTFOUND; | ||
| 127 | ++ if (val->rdataset != NULL) { | ||
| 128 | ++ result = dns_db_findnode(fctx->cache, val->name, | ||
| 129 | ++ false, &node); | ||
| 130 | ++ } | ||
| 131 | ++ if (result == ISC_R_SUCCESS) { | ||
| 132 | ++ (void)dns_db_deleterdataset(fctx->cache, node, | ||
| 133 | ++ NULL, val->type, 0); | ||
| 134 | ++ } | ||
| 135 | ++ if (result == ISC_R_SUCCESS && val->sigrdataset != NULL) | ||
| 136 | ++ { | ||
| 137 | ++ (void)dns_db_deleterdataset( | ||
| 138 | ++ fctx->cache, node, NULL, | ||
| 139 | ++ dns_rdatatype_rrsig, val->type); | ||
| 140 | ++ } | ||
| 141 | ++ if (result == ISC_R_SUCCESS) { | ||
| 142 | ++ dns_db_detachnode(fctx->cache, &node); | ||
| 143 | ++ } | ||
| 144 | ++ break; | ||
| 145 | + } | ||
| 146 | + result = fctx->vresult; | ||
| 147 | + add_bad(fctx, message, addrinfo, result, badns_validation); | ||
| 148 | +@@ -5511,10 +5519,21 @@ validated(void *arg) { | ||
| 149 | + } else if (sentresponse) { | ||
| 150 | + done = true; | ||
| 151 | + goto cleanup_fetchctx; | ||
| 152 | +- } else if (result == DNS_R_BROKENCHAIN) { | ||
| 153 | ++ } | ||
| 154 | ++ | ||
| 155 | ++ /* | ||
| 156 | ++ * A broken trust chain isn't recoverable, and neither is an | ||
| 157 | ++ * exhausted DNSSEC validation budget: retrying would only do | ||
| 158 | ++ * more validation work against the same quota. | ||
| 159 | ++ */ | ||
| 160 | ++ switch (result) { | ||
| 161 | ++ case DNS_R_BROKENCHAIN: | ||
| 162 | ++ case ISC_R_CANCELED: | ||
| 163 | ++ case ISC_R_SHUTTINGDOWN: | ||
| 164 | ++ case ISC_R_QUOTA: | ||
| 165 | + done = true; | ||
| 166 | + goto cleanup_fetchctx; | ||
| 167 | +- } else { | ||
| 168 | ++ default: | ||
| 169 | + fctx_try(fctx, true); | ||
| 170 | + goto cleanup_fetchctx; | ||
| 171 | + } | ||
| 172 | +diff --git a/lib/dns/validator.c b/lib/dns/validator.c | ||
| 173 | +index 49a3a1a..d63acb3 100644 | ||
| 174 | +--- a/lib/dns/validator.c | ||
| 175 | ++++ b/lib/dns/validator.c | ||
| 176 | +@@ -121,6 +121,13 @@ enum valattr { | ||
| 177 | + #define MAXVALIDATIONFAILS(r) \ | ||
| 178 | + (((r)->attributes & VALATTR_MAXVALIDATIONFAILS) != 0) | ||
| 179 | + | ||
| 180 | ++/* | ||
| 181 | ++ * How many DS x DNSKEY matching combinations to allow per validation | ||
| 182 | ++ * permitted by max-validations-per-fetch; matching a DS against a DNSKEY | ||
| 183 | ++ * (a keytag computation) is far cheaper than a signature validation. | ||
| 184 | ++ */ | ||
| 185 | ++#define DS_DNSKEY_COMBINATIONS_PER_VALIDATION 2 | ||
| 186 | ++ | ||
| 187 | + static void | ||
| 188 | + destroy_validator(dns_validator_t *val); | ||
| 189 | + | ||
| 190 | +@@ -161,6 +168,12 @@ validator_logcreate(dns_validator_t *val, dns_name_t *name, | ||
| 191 | + dns_rdatatype_t type, const char *caller, | ||
| 192 | + const char *operation); | ||
| 193 | + | ||
| 194 | ++static void | ||
| 195 | ++validate_answer_finish(void *arg); | ||
| 196 | ++ | ||
| 197 | ++static void | ||
| 198 | ++validator_cancel_finish(dns_validator_t *validator); | ||
| 199 | ++ | ||
| 200 | + static isc_result_t | ||
| 201 | + create_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type, | ||
| 202 | + isc_job_cb callback, const char *caller); | ||
| 203 | +@@ -437,17 +450,22 @@ static void | ||
| 204 | + resume_answer_with_key_done(void *arg) { | ||
| 205 | + dns_validator_t *val = arg; | ||
| 206 | + | ||
| 207 | ++ val->attributes &= ~VALATTR_OFFLOADED; | ||
| 208 | ++ if (CANCELING(val)) { | ||
| 209 | ++ validator_cancel_finish(val); | ||
| 210 | ++ val->result = ISC_R_CANCELED; | ||
| 211 | ++ } | ||
| 212 | ++ | ||
| 213 | + switch (val->result) { | ||
| 214 | + case ISC_R_CANCELED: /* Validation was canceled */ | ||
| 215 | + case ISC_R_SHUTTINGDOWN: /* Server shutting down */ | ||
| 216 | + case ISC_R_QUOTA: /* Validation fails quota reached */ | ||
| 217 | +- dns_validator_cancel(val); | ||
| 218 | +- break; | ||
| 219 | ++ validate_answer_finish(val); | ||
| 220 | ++ return; | ||
| 221 | + default: | ||
| 222 | ++ resume_answer(val); | ||
| 223 | + break; | ||
| 224 | + } | ||
| 225 | +- | ||
| 226 | +- resume_answer(val); | ||
| 227 | + } | ||
| 228 | + | ||
| 229 | + /*% | ||
| 230 | +@@ -506,6 +524,12 @@ fetch_callback_dnskey(void *arg) { | ||
| 231 | + result = validate_async_run(val, resume_answer); | ||
| 232 | + } | ||
| 233 | + break; | ||
| 234 | ++ case ISC_R_CANCELED: | ||
| 235 | ++ case ISC_R_SHUTTINGDOWN: | ||
| 236 | ++ case ISC_R_QUOTA: | ||
| 237 | ++ /* Abort, abort, abort */ | ||
| 238 | ++ result = eresult; | ||
| 239 | ++ break; | ||
| 240 | + default: | ||
| 241 | + validator_log(val, ISC_LOG_DEBUG(3), | ||
| 242 | + "fetch_callback_dnskey: got %s", | ||
| 243 | +@@ -585,6 +609,12 @@ fetch_callback_ds(void *arg) { | ||
| 244 | + isc_result_totext(eresult)); | ||
| 245 | + result = proveunsecure(val, false, false); | ||
| 246 | + break; | ||
| 247 | ++ case ISC_R_CANCELED: | ||
| 248 | ++ case ISC_R_SHUTTINGDOWN: | ||
| 249 | ++ case ISC_R_QUOTA: | ||
| 250 | ++ /* Abort, abort, abort */ | ||
| 251 | ++ result = eresult; | ||
| 252 | ++ break; | ||
| 253 | + default: | ||
| 254 | + validator_log(val, ISC_LOG_DEBUG(3), | ||
| 255 | + "fetch_callback_ds: got %s", | ||
| 256 | +@@ -637,6 +667,12 @@ fetch_callback_ds(void *arg) { | ||
| 257 | + */ | ||
| 258 | + result = proveunsecure(val, false, true); | ||
| 259 | + break; | ||
| 260 | ++ case ISC_R_CANCELED: | ||
| 261 | ++ case ISC_R_SHUTTINGDOWN: | ||
| 262 | ++ case ISC_R_QUOTA: | ||
| 263 | ++ /* Abort, abort, abort */ | ||
| 264 | ++ result = eresult; | ||
| 265 | ++ break; | ||
| 266 | + default: | ||
| 267 | + validator_log(val, ISC_LOG_DEBUG(3), | ||
| 268 | + "fetch_callback_ds: got %s", | ||
| 269 | +@@ -1623,9 +1659,6 @@ validate_answer_process(void *arg); | ||
| 270 | + static void | ||
| 271 | + validate_answer_iter_done(dns_validator_t *val, isc_result_t result); | ||
| 272 | + | ||
| 273 | +-static void | ||
| 274 | +-validator_cancel_finish(dns_validator_t *validator); | ||
| 275 | +- | ||
| 276 | + static void | ||
| 277 | + validate_answer_iter_start(dns_validator_t *val) { | ||
| 278 | + isc_result_t result = ISC_R_SUCCESS; | ||
| 279 | +@@ -1685,9 +1718,6 @@ cleanup: | ||
| 280 | + (void)validate_async_run(val, validate_answer_process); | ||
| 281 | + } | ||
| 282 | + | ||
| 283 | +-static void | ||
| 284 | +-validate_answer_finish(void *arg); | ||
| 285 | +- | ||
| 286 | + static void | ||
| 287 | + validate_answer_signing_key_done(void *arg); | ||
| 288 | + | ||
| 289 | +@@ -1854,18 +1884,7 @@ validate_answer_finish(void *arg) { | ||
| 290 | + validate_async_done(val, val->result); | ||
| 291 | + return; | ||
| 292 | + case ISC_R_QUOTA: | ||
| 293 | +- if (MAXVALIDATIONS(val)) { | ||
| 294 | +- validator_log(val, ISC_LOG_DEBUG(3), | ||
| 295 | +- "maximum number of validations exceeded"); | ||
| 296 | +- } else if (MAXVALIDATIONFAILS(val)) { | ||
| 297 | +- validator_log(val, ISC_LOG_DEBUG(3), | ||
| 298 | +- "maximum number of validation failures " | ||
| 299 | +- "exceeded"); | ||
| 300 | +- } else { | ||
| 301 | +- validator_log( | ||
| 302 | +- val, ISC_LOG_DEBUG(3), | ||
| 303 | +- "unknown error: validation quota exceeded"); | ||
| 304 | +- } | ||
| 305 | ++ /* validate_async_done() logs the specific quota reason. */ | ||
| 306 | + validate_async_done(val, val->result); | ||
| 307 | + return; | ||
| 308 | + default: | ||
| 309 | +@@ -1946,6 +1965,22 @@ validate_helper_run(dns_validator_t *val, isc_job_cb cb) { | ||
| 310 | + | ||
| 311 | + static void | ||
| 312 | + validate_async_done(dns_validator_t *val, isc_result_t result) { | ||
| 313 | ++ if (result == ISC_R_QUOTA) { | ||
| 314 | ++ /* | ||
| 315 | ++ * Log the reason on the validator that actually hit the quota | ||
| 316 | ++ * (it set the attribute); a parent that merely inherits the | ||
| 317 | ++ * quota result from a sub-validation stays quiet. | ||
| 318 | ++ */ | ||
| 319 | ++ if (MAXVALIDATIONS(val)) { | ||
| 320 | ++ validator_log(val, ISC_LOG_DEBUG(3), | ||
| 321 | ++ "maximum number of validations exceeded"); | ||
| 322 | ++ } else if (MAXVALIDATIONFAILS(val)) { | ||
| 323 | ++ validator_log(val, ISC_LOG_DEBUG(3), | ||
| 324 | ++ "maximum number of validation failures " | ||
| 325 | ++ "exceeded"); | ||
| 326 | ++ } | ||
| 327 | ++ } | ||
| 328 | ++ | ||
| 329 | + if (result == DNS_R_NOVALIDSIG && | ||
| 330 | + (val->attributes & VALATTR_TRIEDVERIFY) == 0) | ||
| 331 | + { | ||
| 332 | +@@ -2094,6 +2129,7 @@ validate_dnskey_dsset_done(dns_validator_t *val, isc_result_t result) { | ||
| 333 | + switch (result) { | ||
| 334 | + case ISC_R_CANCELED: | ||
| 335 | + case ISC_R_SHUTTINGDOWN: | ||
| 336 | ++ case ISC_R_QUOTA: | ||
| 337 | + /* Abort, abort, abort! */ | ||
| 338 | + break; | ||
| 339 | + case ISC_R_SUCCESS: | ||
| 340 | +@@ -2133,7 +2169,6 @@ validate_dnskey_dsset(dns_validator_t *val) { | ||
| 341 | + isc_result_t result; | ||
| 342 | + dns_rdata_ds_t ds; | ||
| 343 | + | ||
| 344 | +- dns_rdata_reset(&dsrdata); | ||
| 345 | + dns_rdataset_current(val->dsset, &dsrdata); | ||
| 346 | + result = dns_rdata_tostruct(&dsrdata, &ds, NULL); | ||
| 347 | + RUNTIME_CHECK(result == ISC_R_SUCCESS); | ||
| 348 | +@@ -2160,7 +2195,31 @@ validate_dnskey_dsset(dns_validator_t *val) { | ||
| 349 | + return DNS_R_BADALG; | ||
| 350 | + } | ||
| 351 | + | ||
| 352 | +- val->validation_attempts++; | ||
| 353 | ++ val->matchds_attempts++; | ||
| 354 | ++ | ||
| 355 | ++ /* | ||
| 356 | ++ * Matching one DS against the DNSKEY RRset derives a key tag for | ||
| 357 | ++ * every DNSKEY, so each DS that reaches this point costs one key-tag | ||
| 358 | ++ * computation per key. Bound the accumulated DS-by-DNSKEY work at | ||
| 359 | ++ * DS_DNSKEY_COMBINATIONS_PER_VALIDATION key tags per allowed | ||
| 360 | ++ * validation and stop once it is exceeded, so a flood of mismatched | ||
| 361 | ++ * (or matching but unsigned) DS records cannot force unbounded | ||
| 362 | ++ * matching. Ignored DS (unsupported digest or algorithm) return | ||
| 363 | ++ * above without being counted. A trust-anchor dsset is locally | ||
| 364 | ++ * configured, not attacker supplied, and its rdataset has no count | ||
| 365 | ++ * method. | ||
| 366 | ++ */ | ||
| 367 | ++ if (val->nvalidations != NULL && val->dsset != &val->fdsset) { | ||
| 368 | ++ size_t keycount = dns_rdataset_count(val->rdataset); | ||
| 369 | ++ | ||
| 370 | ++ if ((size_t)val->matchds_attempts * keycount > | ||
| 371 | ++ (size_t)isc_counter_getlimit(val->nvalidations) * | ||
| 372 | ++ DS_DNSKEY_COMBINATIONS_PER_VALIDATION) | ||
| 373 | ++ { | ||
| 374 | ++ val->attributes |= VALATTR_MAXVALIDATIONS; | ||
| 375 | ++ return ISC_R_QUOTA; | ||
| 376 | ++ } | ||
| 377 | ++ } | ||
| 378 | + | ||
| 379 | + /* | ||
| 380 | + * Find the DNSKEY matching the DS... | ||
| 381 | +@@ -2168,15 +2227,35 @@ validate_dnskey_dsset(dns_validator_t *val) { | ||
| 382 | + result = dns_dnssec_matchdskey(val->name, &dsrdata, val->rdataset, | ||
| 383 | + &keyrdata); | ||
| 384 | + if (result != ISC_R_SUCCESS) { | ||
| 385 | ++ val->validation_attempts++; | ||
| 386 | + validator_log(val, ISC_LOG_DEBUG(3), "no DNSKEY matching DS"); | ||
| 387 | ++ /* | ||
| 388 | ++ * A DS that matches no DNSKEY is wasted key-tag matching | ||
| 389 | ++ * work; count it against the validation quota so a flood of | ||
| 390 | ++ * mismatched DS records cannot force unbounded matching. | ||
| 391 | ++ */ | ||
| 392 | ++ consume_validation(val); | ||
| 393 | ++ if (over_max_validations(val)) { | ||
| 394 | ++ return ISC_R_QUOTA; | ||
| 395 | ++ } | ||
| 396 | + return DNS_R_NOKEYMATCH; | ||
| 397 | + } | ||
| 398 | + | ||
| 399 | ++ val->validation_attempts++; | ||
| 400 | ++ | ||
| 401 | + /* | ||
| 402 | + * ... and check that it signed the DNSKEY RRset. | ||
| 403 | + */ | ||
| 404 | + result = check_signer(val, &keyrdata, ds.key_tag, ds.algorithm); | ||
| 405 | +- if (result != ISC_R_SUCCESS) { | ||
| 406 | ++ switch (result) { | ||
| 407 | ++ case ISC_R_SUCCESS: | ||
| 408 | ++ break; | ||
| 409 | ++ case ISC_R_CANCELED: | ||
| 410 | ++ case ISC_R_SHUTTINGDOWN: | ||
| 411 | ++ case ISC_R_QUOTA: | ||
| 412 | ++ /* Abort, abort, abort */ | ||
| 413 | ++ return result; | ||
| 414 | ++ default: | ||
| 415 | + validator_log(val, ISC_LOG_DEBUG(3), | ||
| 416 | + "no RRSIG matching DS key"); | ||
| 417 | + | ||
| 418 | +@@ -2221,14 +2300,21 @@ validate_dnskey_dsset_next_done(void *arg) { | ||
| 419 | + switch (result) { | ||
| 420 | + case ISC_R_CANCELED: | ||
| 421 | + case ISC_R_SHUTTINGDOWN: | ||
| 422 | ++ case ISC_R_QUOTA: | ||
| 423 | + /* Abort, abort, abort! */ | ||
| 424 | + break; | ||
| 425 | + case ISC_R_SUCCESS: | ||
| 426 | + case ISC_R_NOMORE: | ||
| 427 | +- /* We are done */ | ||
| 428 | + break; | ||
| 429 | + default: | ||
| 430 | +- /* Continue validation until we have success or no more data */ | ||
| 431 | ++ /* | ||
| 432 | ++ * A DS that matched no DNSKEY, or matched one that did not | ||
| 433 | ++ * sign the RRset (for example a standby KSK), is not a | ||
| 434 | ++ * validation failure: mismatched DS records are already | ||
| 435 | ++ * charged against the quota in validate_dnskey_dsset() and | ||
| 436 | ++ * signature failures inside verify(). Continue until we | ||
| 437 | ++ * have success or no more data. | ||
| 438 | ++ */ | ||
| 439 | + (void)validate_helper_run(val, validate_dnskey_dsset_next); | ||
| 440 | + return; | ||
| 441 | + } | ||
| 442 | +@@ -2244,25 +2330,18 @@ validate_dnskey_dsset_next_done(void *arg) { | ||
| 443 | + | ||
| 444 | + static void | ||
| 445 | + validate_dnskey_dsset_first(dns_validator_t *val) { | ||
| 446 | +- isc_result_t result; | ||
| 447 | +- | ||
| 448 | + if (CANCELED(val) || CANCELING(val)) { | ||
| 449 | +- result = ISC_R_CANCELED; | ||
| 450 | ++ val->result = ISC_R_CANCELED; | ||
| 451 | + } else { | ||
| 452 | +- result = dns_rdataset_first(val->dsset); | ||
| 453 | ++ val->result = dns_rdataset_first(val->dsset); | ||
| 454 | + } | ||
| 455 | + | ||
| 456 | +- if (result == ISC_R_SUCCESS) { | ||
| 457 | ++ if (val->result == ISC_R_SUCCESS) { | ||
| 458 | + /* continue async run */ | ||
| 459 | +- result = validate_dnskey_dsset(val); | ||
| 460 | +- if (result != ISC_R_SUCCESS) { | ||
| 461 | +- (void)validate_helper_run(val, | ||
| 462 | +- validate_dnskey_dsset_next); | ||
| 463 | +- return; | ||
| 464 | +- } | ||
| 465 | ++ val->result = validate_dnskey_dsset(val); | ||
| 466 | + } | ||
| 467 | + | ||
| 468 | +- validate_dnskey_dsset_done(val, result); | ||
| 469 | ++ (void)validate_async_run(val, validate_dnskey_dsset_next_done); | ||
| 470 | + } | ||
| 471 | + | ||
| 472 | + static void | ||
| @@ -29,7 +29,7 @@ Summary: The Berkeley Internet Name Domain (BIND) DNS (Domain Name System) serv | |||
| 29 | Name: bind | 29 | Name: bind |
| 30 | License: MPLv2.0 | 30 | License: MPLv2.0 |
| 31 | Version: 9.20.21 | 31 | Version: 9.20.21 |
| 32 | -Release: 4 | 32 | +Release: 5 |
| 33 | Epoch: 32 | 33 | Epoch: 32 |
| 34 | Url: https://www.isc.org/downloads/bind/ | 34 | Url: https://www.isc.org/downloads/bind/ |
| 35 | # | 35 | # |
| @@ -94,6 +94,7 @@ Patch6030:backport-CVE-2026-13204-01.patch | |||
| 94 | Patch6031:backport-CVE-2026-13204-02.patch | 94 | Patch6031:backport-CVE-2026-13204-02.patch |
| 95 | Patch6032:backport-CVE-2026-13204-03.patch | 95 | Patch6032:backport-CVE-2026-13204-03.patch |
| 96 | Patch6033:backport-CVE-2026-11622.patch | 96 | Patch6033:backport-CVE-2026-11622.patch |
| 97 | +Patch6034:backport-CVE-2026-19668-01.patch | ||
| 97 | # Common patches | 98 | # Common patches |
| 98 | 99 | ||
| 99 | %{?systemd_ordering} | 100 | %{?systemd_ordering} |
| @@ -923,6 +924,12 @@ fi; | |||
| 923 | %endif | 924 | %endif |
| 924 | 925 | ||
| 925 | %changelog | 926 | %changelog |
| 927 | +* Thu Sep 17 2026 infra_team <zhaiwenjie1@huawei.com> - 32:9.20.21-5 | ||
| 928 | +- Type:CVE | ||
| 929 | +- CVE:CVE-2026-19668 | ||
| 930 | +- SUG:NA | ||
| 931 | +- DESC:fix CVE-2026-19668 | ||
| 932 | + | ||
| 926 | * Sat Aug 01 2026 tangce <tangce1@h-partners.com> - 32:9.20.21-4 | 933 | * Sat Aug 01 2026 tangce <tangce1@h-partners.com> - 32:9.20.21-4 |
| 927 | - Type:CVE | 934 | - Type:CVE |
| 928 | - CVE:CVE-2026-10822,CVE-2026-11331,CVE-2026-12617,CVE-2026-13204,CVE-2026-11622 | 935 | - CVE:CVE-2026-10822,CVE-2026-11331,CVE-2026-12617,CVE-2026-13204,CVE-2026-11622 |