summaryrefslogtreecommitdiff
path: root/Libraries/LibTLS
diff options
context:
space:
mode:
Diffstat (limited to 'Libraries/LibTLS')
-rw-r--r--Libraries/LibTLS/Certificate.h16
-rw-r--r--Libraries/LibTLS/ClientHandshake.cpp9
-rw-r--r--Libraries/LibTLS/TLSv12.cpp109
-rw-r--r--Libraries/LibTLS/TLSv12.h4
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;