[CVE-2025-40775] sec: usr: Prevent assertion when processing TSIG algorithm

DNS messages that included a Transaction Signature (TSIG) containing an
invalid value in the algorithm field caused :iscman:`named` to crash
with an assertion failure. This has been fixed.  :cve:`2025-40775`

See isc-projects/bind9#5300

Merge branch '5300-confidential-tsig-unknown-alg' into 'v9.21.8-release'

See merge request isc-private/bind9!793
This commit is contained in:
Michał Kępień 2025-05-08 22:45:48 +02:00
commit 1665e05438
No known key found for this signature in database
5 changed files with 80 additions and 53 deletions

View file

@ -285,18 +285,16 @@ struct dns_message {
ISC_LIST(dns_rdata_t) freerdata;
ISC_LIST(dns_rdatalist_t) freerdatalist;
dns_rcode_t tsigstatus;
dns_rcode_t querytsigstatus;
dns_name_t *tsigname; /* Owner name of TSIG, if any
* */
dns_rcode_t tsigstatus;
dns_rcode_t querytsigstatus;
dns_name_t *tsigname; /* Owner name of TSIG, if any */
dns_rdataset_t *querytsig;
dns_tsigkey_t *tsigkey;
dst_context_t *tsigctx;
int sigstart;
int timeadjust;
dns_name_t *sig0name; /* Owner name of SIG0, if any
* */
dns_name_t *sig0name; /* Owner name of SIG0, if any */
dst_key_t *sig0key;
dns_rcode_t sig0status;
isc_region_t query;

View file

@ -78,12 +78,14 @@ struct dns_tsigkeyring {
struct dns_tsigkey {
/* Unlocked */
unsigned int magic; /*%< Magic number. */
isc_mem_t *mctx;
dst_key_t *key; /*%< Key */
dns_fixedname_t fn;
dns_name_t *name; /*%< Key name */
const dns_name_t *algorithm; /*%< Algorithm name */
unsigned int magic; /*%< Magic number. */
isc_mem_t *mctx;
dst_key_t *key; /*%< Key */
dns_fixedname_t fn;
dns_name_t *name; /*%< Key name */
dst_algorithm_t alg; /*< Algorithm */
dns_name_t algname; /*< Algorithm name, only used if
algorithm is DST_ALG_UNKNOWN */
dns_name_t *creator; /*%< name that created secret */
bool generated : 1; /*%< key was auto-generated */
bool restored : 1; /*%< key was restored at startup */
@ -234,6 +236,16 @@ dns_tsigkey_find(dns_tsigkey_t **tsigkeyp, const dns_name_t *name,
*\li #ISC_R_NOTFOUND
*/
const dns_name_t *
dns_tsigkey_algorithm(dns_tsigkey_t *tkey);
/*%<
* Returns the key algorithm associated with a tsigkey object.
*
* Note that when a tsigkey object is created with algorithm
* DST_ALG_UNKNOWN, the unknown algorithm's name must be cloned
* into tsigkey->algname.
*/
void
dns_tsigkeyring_create(isc_mem_t *mctx, dns_tsigkeyring_t **ringp);
/*%<

View file

@ -633,12 +633,11 @@ msgreset(dns_message_t *msg, bool everything) {
static unsigned int
spacefortsig(dns_tsigkey_t *key, int otherlen) {
isc_region_t r1, r2;
unsigned int x;
isc_result_t result;
isc_region_t r1 = { 0 }, r2 = { 0 };
unsigned int x = 0;
/*
* The space required for an TSIG record is:
* The space required for a TSIG record is:
*
* n1 bytes for the name
* 2 bytes for the type
@ -659,11 +658,11 @@ spacefortsig(dns_tsigkey_t *key, int otherlen) {
*/
dns_name_toregion(key->name, &r1);
dns_name_toregion(key->algorithm, &r2);
if (key->key == NULL) {
x = 0;
} else {
result = dst_key_sigsize(key->key, &x);
if (key->alg != DST_ALG_UNKNOWN) {
dns_name_toregion(dns_tsigkey_algorithm(key), &r2);
}
if (key->key != NULL) {
isc_result_t result = dst_key_sigsize(key->key, &x);
if (result != ISC_R_SUCCESS) {
x = 0;
}

View file

@ -189,28 +189,6 @@ adjust_lru(dns_tsigkey_t *tkey) {
}
}
static const dns_name_t *
namefromalg(dst_algorithm_t alg) {
switch (alg) {
case DST_ALG_HMACMD5:
return dns_tsig_hmacmd5_name;
case DST_ALG_HMACSHA1:
return dns_tsig_hmacsha1_name;
case DST_ALG_HMACSHA224:
return dns_tsig_hmacsha224_name;
case DST_ALG_HMACSHA256:
return dns_tsig_hmacsha256_name;
case DST_ALG_HMACSHA384:
return dns_tsig_hmacsha384_name;
case DST_ALG_HMACSHA512:
return dns_tsig_hmacsha512_name;
case DST_ALG_GSSAPI:
return dns_tsig_gssapi_name;
default:
return NULL;
}
}
isc_result_t
dns_tsigkey_createfromkey(const dns_name_t *name, dst_algorithm_t algorithm,
dst_key_t *dstkey, bool generated, bool restored,
@ -230,6 +208,8 @@ dns_tsigkey_createfromkey(const dns_name_t *name, dst_algorithm_t algorithm,
.restored = restored,
.inception = inception,
.expire = expire,
.alg = algorithm,
.algname = DNS_NAME_INITEMPTY,
.link = ISC_LINK_INITIALIZER,
};
@ -247,8 +227,6 @@ dns_tsigkey_createfromkey(const dns_name_t *name, dst_algorithm_t algorithm,
goto cleanup_name;
}
tkey->algorithm = namefromalg(algorithm);
if (creator != NULL) {
tkey->creator = isc_mem_get(mctx, sizeof(dns_name_t));
dns_name_init(tkey->creator);
@ -432,7 +410,8 @@ dump_key(dns_tsigkey_t *tkey, FILE *fp) {
dns_name_format(tkey->name, namestr, sizeof(namestr));
dns_name_format(tkey->creator, creatorstr, sizeof(creatorstr));
dns_name_format(tkey->algorithm, algorithmstr, sizeof(algorithmstr));
dns_name_format(dns_tsigkey_algorithm(tkey), algorithmstr,
sizeof(algorithmstr));
result = dst_key_dump(tkey->key, tkey->mctx, &buffer, &length);
if (result == ISC_R_SUCCESS) {
fprintf(fp, "%s %s %u %u %s %.*s\n", namestr, creatorstr,
@ -605,7 +584,7 @@ dns_tsig_sign(dns_message_t *msg) {
};
dns_name_init(&tsig.algorithm);
dns_name_clone(key->algorithm, &tsig.algorithm);
dns_name_clone(dns_tsigkey_algorithm(key), &tsig.algorithm);
isc_buffer_init(&databuf, data, sizeof(data));
@ -962,12 +941,17 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg,
}
if (result != ISC_R_SUCCESS) {
msg->tsigstatus = dns_tsigerror_badkey;
result = dns_tsigkey_create(
keyname, dns__tsig_algfromname(&tsig.algorithm),
NULL, 0, mctx, &msg->tsigkey);
alg = dns__tsig_algfromname(&tsig.algorithm);
result = dns_tsigkey_create(keyname, alg, NULL, 0, mctx,
&msg->tsigkey);
if (result != ISC_R_SUCCESS) {
return result;
}
if (alg == DST_ALG_UNKNOWN) {
dns_name_clone(&tsig.algorithm,
&msg->tsigkey->algname);
}
tsig_log(msg->tsigkey, 2, "unknown key");
return DNS_R_TSIGVERIFYFAILURE;
}
@ -1091,7 +1075,7 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg,
/*
* Digest the key algorithm.
*/
dns_name_toregion(tsigkey->algorithm, &r);
dns_name_toregion(dns_tsigkey_algorithm(tsigkey), &r);
result = dst_context_adddata(ctx, &r);
if (result != ISC_R_SUCCESS) {
goto cleanup_context;
@ -1539,7 +1523,8 @@ again:
RWUNLOCK(&ring->lock, locktype);
return result;
}
if (algorithm != NULL && !dns_name_equal(key->algorithm, algorithm)) {
if (algorithm != NULL && key->alg != dns__tsig_algfromname(algorithm)) {
RWUNLOCK(&ring->lock, locktype);
return ISC_R_NOTFOUND;
}
@ -1565,6 +1550,39 @@ again:
return ISC_R_SUCCESS;
}
const dns_name_t *
dns_tsigkey_algorithm(dns_tsigkey_t *tkey) {
REQUIRE(VALID_TSIGKEY(tkey));
switch (tkey->alg) {
case DST_ALG_HMACMD5:
return dns_tsig_hmacmd5_name;
case DST_ALG_HMACSHA1:
return dns_tsig_hmacsha1_name;
case DST_ALG_HMACSHA224:
return dns_tsig_hmacsha224_name;
case DST_ALG_HMACSHA256:
return dns_tsig_hmacsha256_name;
case DST_ALG_HMACSHA384:
return dns_tsig_hmacsha384_name;
case DST_ALG_HMACSHA512:
return dns_tsig_hmacsha512_name;
case DST_ALG_GSSAPI:
return dns_tsig_gssapi_name;
case DST_ALG_UNKNOWN:
/*
* If the tsigkey object was created with an
* unknown algorithm, then we cloned
* the algorithm name here.
*/
return &tkey->algname;
default:
UNREACHABLE();
}
}
void
dns_tsigkeyring_create(isc_mem_t *mctx, dns_tsigkeyring_t **ringp) {
dns_tsigkeyring_t *ring = NULL;

View file

@ -96,7 +96,7 @@ add_tsig(dst_context_t *tsigctx, dns_tsigkey_t *key, isc_buffer_t *target,
tsig.common.rdtype = dns_rdatatype_tsig;
ISC_LINK_INIT(&tsig.common, link);
dns_name_init(&tsig.algorithm);
dns_name_clone(key->algorithm, &tsig.algorithm);
dns_name_clone(dns_tsigkey_algorithm(key), &tsig.algorithm);
tsig.timesigned = now;
tsig.fudge = DNS_TSIG_FUDGE;