From 8c3b3a40620d4247e2a4349c511ded09e24876d8 Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Tue, 17 Dec 2024 14:37:30 +0100 Subject: [PATCH] wip --- Libraries/LibCore/Promise.h | 38 +++++++++++++- Libraries/LibDNS/Resolver.h | 102 +++++++++++++++++++++++++++++------- 2 files changed, 121 insertions(+), 19 deletions(-) diff --git a/Libraries/LibCore/Promise.h b/Libraries/LibCore/Promise.h index 8db457e2d9b..cb2a8c34959 100644 --- a/Libraries/LibCore/Promise.h +++ b/Libraries/LibCore/Promise.h @@ -24,6 +24,42 @@ public: Function(Result&)> on_resolution; Function on_rejection; + static NonnullRefPtr after(Vector>&& promises) + { + auto promise = Promise::construct(); + struct Resolved : RefCounted { + explicit Resolved(size_t n) + : needed(n) + { + } + + size_t count { 0 }; + size_t needed { 0 }; + }; + + auto resolved = make_ref_counted(promises.size()); + auto weak_promise = promise->template make_weak_ptr(); + for (auto p : promises) { + p->when_resolved([weak_promise, resolved](Result&) -> ErrorOr { + if (weak_promise->is_rejected()) + return {}; + + if (++resolved->count == resolved->needed) + weak_promise->resolve({}); + return {}; + }); + + p->when_rejected([weak_promise, resolved](ErrorType& error) { + ++resolved->count; + weak_promise->reject(move(error)); + }); + + promise->add_child(*p); + } + + return promise; + } + void resolve(Result&& result) { m_result_or_rejection = move(result); @@ -82,7 +118,7 @@ public: template F> Promise& when_resolved(F handler) { - return when_resolved([handler = move(handler)](Result& result) -> ErrorOr { + return when_resolved([handler = move(handler)](Result& result) mutable -> ErrorOr { handler(result); return {}; }); diff --git a/Libraries/LibDNS/Resolver.h b/Libraries/LibDNS/Resolver.h index 2ec6c7c498d..7d2d6ab18f1 100644 --- a/Libraries/LibDNS/Resolver.h +++ b/Libraries/LibDNS/Resolver.h @@ -17,10 +17,15 @@ #include #include #include +#include +#include #include #include #include +#undef DNS_DEBUG +#define DNS_DEBUG 1 + namespace DNS { class Resolver; @@ -528,7 +533,7 @@ private: auto result = lookup->result.strong_ref(); if (result->is_dnssec_validated()) - return validate_dnssec(move(message), *lookup); + return validate_dnssec(move(message), *lookup, *result); for (auto& record : message.answers) result->add_record(move(record)); @@ -543,25 +548,25 @@ private: } } - ErrorOr validate_dnssec(Messages::Message message, PendingLookup& lookup) + ErrorOr validate_dnssec(Messages::Message message, PendingLookup& lookup, NonnullRefPtr result) { struct RecordAndRRSIG { - Messages::ResourceRecord* record { nullptr }; - Messages::Records::RRSIG* rrsig { nullptr }; + Optional record; + Messages::Records::RRSIG rrsig; }; HashMap records_with_rrsigs; for (auto& record : message.answers) { if (record.type == Messages::ResourceType::RRSIG) { auto& rrsig = record.record.get(); if (auto found = records_with_rrsigs.get(rrsig.type_covered); found.has_value()) - found->rrsig = &rrsig; + found->rrsig = move(rrsig); else - records_with_rrsigs.set(rrsig.type_covered, { nullptr, &rrsig }); + records_with_rrsigs.set(rrsig.type_covered, { {}, move(rrsig) }); } else { if (auto found = records_with_rrsigs.get(record.type); found.has_value()) - found->record = &record; + found->record = move(record); else - records_with_rrsigs.set(record.type, { &record, nullptr }); + records_with_rrsigs.set(record.type, { move(record), {} }); } } @@ -570,35 +575,58 @@ private: return {}; } - auto name = lookup.result->name(); + auto name = result->name(); - Core::deferred_invoke([this, lookup, name, records_with_rrsigs = move(records_with_rrsigs)] { + Core::deferred_invoke([this, lookup, name, records_with_rrsigs = move(records_with_rrsigs), result = move(result)] mutable { dbgln("DNS: Resolving DNSKEY for {}", name.to_string()); this->lookup(lookup.name, Messages::Class::IN, Array { Messages::ResourceType::DNSKEY }.span(), { .validate_dnssec_locally = false }) - ->when_resolved([=](NonnullRefPtr& dnskey_lookup_result) { + ->when_resolved([=, this, records_with_rrsigs = move(records_with_rrsigs)](NonnullRefPtr& dnskey_lookup_result) mutable { dbgln("DNSKEY for {}:", name.to_string()); for (auto& record : dnskey_lookup_result->records()) dbgln("DNSKEY: {}", record.to_string()); + dbgln("DNS: Validating {} RRSIGs for {}", records_with_rrsigs.size(), name.to_string()); + Vector>> promises; + for (auto& [type, pair] : records_with_rrsigs) { - if (!pair.record) + if (!pair.record.has_value()) continue; auto& record = *pair.record; - auto& rrsig = *pair.rrsig; + auto& rrsig = pair.rrsig; dbgln("Validating RRSIG for {} with DNSKEY", record.to_string()); - auto dnskey = [&] { + auto dnskey = [&] -> Optional { for (auto& dnskey_record : dnskey_lookup_result->records()) { - if (dnskey_record.type == Messages::ResourceType::DNSKEY) - return dnskey_record.record.get(); + if (auto r = dnskey_record.record.get_pointer()) + return *r; } - return Messages::Records::DNSKEY {}; + return {}; }(); - dbgln("DNSKEY: {}", dnskey.to_string()); + if (!dnskey.has_value()) { + dbgln("DNS: No DNSKEY found for RRSIG validation of {}", record.to_string()); + continue; + } + + dbgln("DNSKEY: {}", dnskey->to_string()); dbgln("RRSIG: {}", rrsig.to_string()); + + promises.append(validate_record_with_rrsig(record, rrsig, *dnskey, result)); } + + auto promise = Core::Promise::after(move(promises)) + ->when_resolved([result, lookup](Empty) { + result->set_dnssec_validated(true); + result->finished_request(); + lookup.promise->resolve(result); + }) + .when_rejected([result, lookup](Error& error) { + result->finished_request(); + lookup.promise->reject(move(error)); + }).map>([result](Empty&) { return result; }); + + lookup.promise = move(promise); }) .when_rejected([=](auto& error) { dbgln("Failed to resolve DNSKEY for {}: {}", name.to_string(), error); @@ -609,6 +637,44 @@ private: return {}; } + NonnullRefPtr> validate_record_with_rrsig(Messages::ResourceRecord const& record, Messages::Records::RRSIG const& rrsig, Messages::Records::DNSKEY const& dnskey, NonnullRefPtr result) + { + dbgln("Validating RRSIG {} for RR: {} with DNSKEY: {}", rrsig.to_string(), record.to_string(), dnskey.to_string()); + auto promise = Core::Promise::construct(); + + switch (dnskey.algorithm) { + case Messages::DNSSEC::Algorithm::RSAMD5: { + Crypto::PK::RSA rsa { dnskey.public_key }; + break; + } + case Messages::DNSSEC::Algorithm::DSA: + break; + case Messages::DNSSEC::Algorithm::RSASHA1: + break; + case Messages::DNSSEC::Algorithm::RSASHA256: + break; + case Messages::DNSSEC::Algorithm::RSASHA512: + break; + case Messages::DNSSEC::Algorithm::ECDSAP256SHA256: { + auto key = MUST(Crypto::PK::EC::parse_ec_key(dnskey.public_key, false, {})); + Crypto::PK::EC ec { key }; + break; + } + case Messages::DNSSEC::Algorithm::ECDSAP384SHA384: + break; + case Messages::DNSSEC::Algorithm::RSASHA1NSEC3SHA1: + case Messages::DNSSEC::Algorithm::ED25519: + case Messages::DNSSEC::Algorithm::Unknown: + dbgln("DNS: Unsupported algorithm for DNSSEC validation: {}", to_string(dnskey.algorithm)); + promise->reject(Error::from_string_literal("Unsupported algorithm for DNSSEC validation")); + return promise; + } + + result->add_record(record); + promise->resolve({}); + return promise; + } + bool has_connection(bool attempt_restart = true) { auto result = m_socket.with_read_locked(