diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index ced874e2ba..39b679ab04 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -646,8 +646,10 @@ static int dnssec_rrset_verify_sig( if (!ctx) return -ENOMEM; + /* If the signature algorithm is supported by systemd-resolved but disabled by host policy, + * also return -EOPNOTSUPP. */ if (EVP_DigestInit_ex(ctx, md_algorithm, NULL) <= 0) - return -EIO; + return -EOPNOTSUPP; if (EVP_DigestUpdate(ctx, sig_data, sig_size) <= 0) return -EIO; @@ -912,9 +914,6 @@ int dnssec_verify_rrset_search( DNS_ANSWER_FOREACH_FLAGS(dnskey, flags, validated_dnskeys) { DnssecResult one_result; - if ((flags & DNS_ANSWER_AUTHENTICATED) == 0) - continue; - /* Is this a DNSKEY RR that matches they key of our RRSIG? */ r = dnssec_rrsig_match_dnskey(rrsig, dnskey, false); if (r < 0) @@ -922,6 +921,14 @@ int dnssec_verify_rrset_search( if (r == 0) continue; + if ((flags & DNS_ANSWER_AUTHENTICATED) == 0) { + /* An unauthenticated DNSKEY in validated_dnskeys is a key we are not able to + * authenticate, but might still be valid. Record this as an unsupported + * algorithm so we can still at least report an insecure answer. */ + found_unsupported_algorithm = true; + continue; + } + /* Take the time here, if it isn't set yet, so * that we do all validations with the same * time. */ @@ -1092,8 +1099,10 @@ int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, if (!ctx) return -ENOMEM; + /* If the digest is supported by systemd-resolved but disabled by host policy, also return -EOPNOTSUPP + */ if (EVP_DigestInit_ex(ctx, md_algorithm, NULL) <= 0) - return -EIO; + return -EOPNOTSUPP; if (EVP_DigestUpdate(ctx, wire_format, encoded_length) <= 0) return -EIO; @@ -1121,6 +1130,7 @@ int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) { DnsResourceRecord *ds; DnsAnswerFlags flags; + bool found_unsupported_algorithm = false; int r; assert(dnskey); @@ -1145,14 +1155,21 @@ int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *vali continue; r = dnssec_verify_dnskey_by_ds(dnskey, ds, false); - if (IN_SET(r, -EKEYREJECTED, -EOPNOTSUPP)) - continue; /* The DNSKEY is revoked or otherwise invalid, or we don't support the digest algorithm */ + if (r == -EKEYREJECTED) + continue; /* The DNSKEY is revoked or otherwise invalid. */ + if (r == -EOPNOTSUPP) { + found_unsupported_algorithm = true; + continue; + } if (r < 0) return r; if (r > 0) return 1; } + if (found_unsupported_algorithm) + return -EOPNOTSUPP; + return 0; } @@ -1201,7 +1218,7 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) { return -ENOMEM; if (EVP_DigestInit_ex(ctx, algorithm, NULL) <= 0) - return -EIO; + return -EOPNOTSUPP; r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true); if (r < 0) @@ -1218,7 +1235,7 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) { for (unsigned k = 0; k < nsec3->nsec3.iterations; k++) { if (EVP_DigestInit_ex(ctx, algorithm, NULL) <= 0) - return -EIO; + return -EOPNOTSUPP; if (EVP_DigestUpdate(ctx, result, hash_size) <= 0) return -EIO; if (EVP_DigestUpdate(ctx, nsec3->nsec3.salt, nsec3->nsec3.salt_size) <= 0) diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index d4a5dd8e17..1a786ccf27 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -2836,13 +2836,18 @@ static int dns_transaction_validate_dnskey_by_ds(DnsTransaction *t) { DNS_ANSWER_FOREACH_ITEM(item, t->answer) { r = dnssec_verify_dnskey_by_ds_search(item->rr, t->validated_keys); - if (r < 0) + if (r < 0 && r != -EOPNOTSUPP) return r; if (r == 0) continue; - /* If so, the DNSKEY is validated too. */ - r = dns_answer_add_extend(&t->validated_keys, item->rr, item->ifindex, item->flags|DNS_ANSWER_AUTHENTICATED, item->rrsig); + /* If so, the DNSKEY is validated too, but only mark it authenticated if the DS verification + * succeeded with a known algorithm. */ + if (r == -EOPNOTSUPP) + r = dns_answer_add_extend(&t->validated_keys, item->rr, item->ifindex, item->flags, NULL); + else + r = dns_answer_add_extend(&t->validated_keys, item->rr, item->ifindex, item->flags|DNS_ANSWER_AUTHENTICATED, item->rrsig); + if (r < 0) return r; } @@ -3259,6 +3264,12 @@ static int dns_transaction_copy_validated(DnsTransaction *t) { if (DNS_TRANSACTION_IS_LIVE(dt->state)) continue; + /* Some of the validated keys may not be authenticated, but are still useful to report + * insecure answers when the domain is signed only by unsupported algorithms. */ + r = dns_answer_extend(&t->validated_keys, dt->validated_keys); + if (r < 0) + return r; + if (!FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED)) continue; @@ -3478,6 +3489,23 @@ static int dnssec_validate_records( /* https://datatracker.ietf.org/doc/html/rfc6840#section-5.2 */ if (result == DNSSEC_UNSUPPORTED_ALGORITHM) { + if (rr->key->type == DNS_TYPE_DNSKEY) { + /* This is a DNSKEY we cannot authenticate, but it might still be the best + * offer from the resolver. Add it to the validated keys in case it's the + * best we can find, but do not mark it as authenticated. + */ + + r = dns_answer_copy_by_key(&t->validated_keys, t->answer, rr->key, 0, NULL); + if (r < 0) + return r; + + /* Some of the DNSKEYs we just added might already have been revoked, + * remove them again in that case. */ + r = dns_transaction_invalidate_revoked_keys(t); + if (r < 0) + return r; + } + r = dns_answer_move_by_key(validated, &t->answer, rr->key, 0, NULL); if (r < 0) return r;