diff options
Diffstat (limited to 'Libraries/LibTLS')
-rw-r--r-- | Libraries/LibTLS/Certificate.h | 16 | ||||
-rw-r--r-- | Libraries/LibTLS/ClientHandshake.cpp | 9 | ||||
-rw-r--r-- | Libraries/LibTLS/TLSv12.cpp | 109 | ||||
-rw-r--r-- | Libraries/LibTLS/TLSv12.h | 4 |
4 files changed, 135 insertions, 3 deletions
diff --git a/Libraries/LibTLS/Certificate.h b/Libraries/LibTLS/Certificate.h index e5f67942a9..2f2de40d87 100644 --- a/Libraries/LibTLS/Certificate.h +++ b/Libraries/LibTLS/Certificate.h @@ -28,6 +28,7 @@ #include <AK/ByteBuffer.h> #include <AK/Forward.h> +#include <AK/Singleton.h> #include <AK/Types.h> #include <LibCrypto/BigInt/UnsignedBigInteger.h> #include <LibCrypto/PK/RSA.h> @@ -77,6 +78,21 @@ struct Certificate { bool is_valid() const; }; +class DefaultRootCACertificates { +public: + DefaultRootCACertificates(); + + const Vector<Certificate>& certificates() const { return m_ca_certificates; } + + static DefaultRootCACertificates& the() { return s_the; } + +private: + static AK::Singleton<DefaultRootCACertificates> s_the; + + Vector<Certificate> m_ca_certificates; +}; + } using TLS::Certificate; +using TLS::DefaultRootCACertificates; diff --git a/Libraries/LibTLS/ClientHandshake.cpp b/Libraries/LibTLS/ClientHandshake.cpp index 214fce237a..6fb48bf565 100644 --- a/Libraries/LibTLS/ClientHandshake.cpp +++ b/Libraries/LibTLS/ClientHandshake.cpp @@ -274,7 +274,14 @@ void TLSv12::build_random(PacketBuilder& builder) m_context.premaster_key = ByteBuffer::copy(random_bytes, bytes); - const auto& certificate = m_context.certificates[0]; + const auto& certificate_option = verify_chain_and_get_matching_certificate(m_context.SNI); // if the SNI is empty, we'll make a special case and match *a* leaf certificate. + if (!certificate_option.has_value()) { + dbg() << "certificate verification failed :("; + alert(AlertLevel::Critical, AlertDescription::BadCertificate); + return; + } + + auto& certificate = m_context.certificates[certificate_option.value()]; #ifdef TLS_DEBUG dbg() << "PreMaster secret"; print_buffer(m_context.premaster_key); diff --git a/Libraries/LibTLS/TLSv12.cpp b/Libraries/LibTLS/TLSv12.cpp index fd78dd74ad..cf823f58c8 100644 --- a/Libraries/LibTLS/TLSv12.cpp +++ b/Libraries/LibTLS/TLSv12.cpp @@ -25,6 +25,7 @@ */ #include <AK/Endian.h> +#include <LibCore/ConfigFile.h> #include <LibCore/DateTime.h> #include <LibCore/Timer.h> #include <LibCrypto/ASN1/DER.h> @@ -498,8 +499,10 @@ ssize_t TLSv12::handle_certificate(const ByteBuffer& buffer) auto certificate = parse_asn1(buffer.slice_view(res_cert, certificate_size_specific), false); if (certificate.has_value()) { - m_context.certificates.append(certificate.value()); - valid_certificate = true; + if (certificate.value().is_valid()) { + m_context.certificates.append(certificate.value()); + valid_certificate = true; + } } res_cert += certificate_size_specific; } while (remaining > 0); @@ -705,6 +708,94 @@ void TLSv12::try_disambiguate_error() const } } +void TLSv12::set_root_certificates(Vector<Certificate> certificates) +{ + if (!m_context.root_ceritificates.is_empty()) + dbg() << "TLS warn: resetting root certificates!"; + + for (auto& cert : certificates) { + if (!cert.is_valid()) + dbg() << "Certificate for " << cert.subject << " by " << cert.issuer_subject << " is invalid, things may or may not work!"; + // FIXME: Figure out what we should do when our root certs are invalid. + } + m_context.root_ceritificates = move(certificates); +} + +bool Context::verify_chain() const +{ + const Vector<Certificate>* local_chain = nullptr; + if (is_server) { + dbg() << "Unsupported: Server mode"; + TODO(); + } else { + local_chain = &certificates; + } + + // FIXME: Actually verify the signature, instead of just checking the name. + HashMap<String, String> chain; + HashTable<String> roots; + // First, walk the root certs. + for (auto& cert : root_ceritificates) { + roots.set(cert.subject); + chain.set(cert.subject, cert.issuer_subject); + } + + // Then, walk the local certs. + for (auto& cert : *local_chain) { + auto& issuer_unique_name = cert.issuer_unit.is_empty() ? cert.issuer_subject : cert.issuer_unit; + chain.set(cert.subject, issuer_unique_name); + } + + // Then verify the chain. + for (auto& it : chain) { + if (it.key == it.value) { // Allow self-signed certificates. + if (!roots.contains(it.key)) + dbg() << "Self-signed warning: Certificate for " << it.key << " is self-signed"; + continue; + } + + auto ref = chain.get(it.value); + if (!ref.has_value()) { + dbg() << "Certificate for " << it.key << " is not signed by anyone we trust (" << it.value << ")"; + return false; + } + + if (ref.value() == it.key) // Allow (but warn about) mutually recursively signed cert A <-> B. + dbg() << "Co-dependency warning: Certificate for " << ref.value() << " is issued by " << it.key << ", which itself is issued by " << ref.value(); + } + + return true; +} + +static bool wildcard_matches(const StringView& host, const StringView& subject) +{ + if (host.matches(subject)) + return true; + + if (subject.starts_with("*.")) + return wildcard_matches(host, subject.substring_view(2)); + + return false; +} + +Optional<size_t> TLSv12::verify_chain_and_get_matching_certificate(const StringView& host) const +{ + if (m_context.certificates.is_empty() || !m_context.verify_chain()) + return {}; + + if (host.is_empty()) + return 0; + + for (size_t i = 0; i < m_context.certificates.size(); ++i) { + auto& cert = m_context.certificates[i]; + // FIXME: Also check against SAN (oid 2.5.29.17). + if (wildcard_matches(host, cert.subject)) + return i; + } + + return {}; +} + TLSv12::TLSv12(Core::Object* parent, Version version) : Core::Socket(Core::Socket::Type::TCP, parent) { @@ -751,4 +842,18 @@ bool TLSv12::add_client_key(const ByteBuffer& certificate_pem_buffer, const Byte return add_client_key(certificate); } +AK::Singleton<DefaultRootCACertificates> DefaultRootCACertificates::s_the; +DefaultRootCACertificates::DefaultRootCACertificates() +{ + // FIXME: This might not be the best format, find a better way to represent CA certificates. + auto config = Core::ConfigFile::get_for_system("ca_certs"); + for (auto& entity : config->groups()) { + Certificate cert; + cert.subject = entity; + cert.issuer_subject = config->read_entry(entity, "issuer_subject", entity); + cert.country = config->read_entry(entity, "country"); + m_ca_certificates.append(move(cert)); + } +} + } diff --git a/Libraries/LibTLS/TLSv12.h b/Libraries/LibTLS/TLSv12.h index 08abdc0832..3e2788dd61 100644 --- a/Libraries/LibTLS/TLSv12.h +++ b/Libraries/LibTLS/TLSv12.h @@ -279,6 +279,8 @@ public: bool load_certificates(const ByteBuffer& pem_buffer); bool load_private_key(const ByteBuffer& pem_buffer); + void set_root_certificates(Vector<Certificate>); + bool add_client_key(const ByteBuffer& certificate_pem_buffer, const ByteBuffer& key_pem_buffer); bool add_client_key(Certificate certificate) { @@ -429,6 +431,8 @@ private: bool compute_master_secret(size_t length); + Optional<size_t> verify_chain_and_get_matching_certificate(const StringView& host) const; + void try_disambiguate_error() const; Context m_context; |