Add mTLS support to masque_client PiperOrigin-RevId: 813310519
diff --git a/quiche/quic/masque/masque_client.cc b/quiche/quic/masque/masque_client.cc index da6611e..af14227 100644 --- a/quiche/quic/masque/masque_client.cc +++ b/quiche/quic/masque/masque_client.cc
@@ -11,6 +11,7 @@ #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" +#include "quiche/quic/core/crypto/client_proof_source.h" #include "quiche/quic/core/crypto/proof_verifier.h" #include "quiche/quic/core/io/quic_event_loop.h" #include "quiche/quic/core/quic_connection.h" @@ -91,7 +92,8 @@ // static std::unique_ptr<MasqueClient> MasqueClient::Create( const std::string& uri_template, MasqueMode masque_mode, - QuicEventLoop* event_loop, std::unique_ptr<ProofVerifier> proof_verifier) { + QuicEventLoop* event_loop, std::unique_ptr<ProofVerifier> proof_verifier, + std::unique_ptr<ClientProofSource> proof_source) { QuicUrl url(uri_template); std::string host = url.host(); if (host.empty()) { @@ -118,6 +120,9 @@ QUIC_LOG(ERROR) << "Failed to create masque_client"; return nullptr; } + if (proof_source != nullptr) { + masque_client->crypto_config()->set_proof_source(std::move(proof_source)); + } if (!masque_client->Prepare(kDefaultMaxPacketSizeForTunnels)) { QUIC_LOG(ERROR) << "Failed to prepare MASQUE client to " << host << ":" << port;
diff --git a/quiche/quic/masque/masque_client.h b/quiche/quic/masque/masque_client.h index c0f5b76..5b8e0c1 100644 --- a/quiche/quic/masque/masque_client.h +++ b/quiche/quic/masque/masque_client.h
@@ -8,6 +8,7 @@ #include <memory> #include <string> +#include "quiche/quic/core/crypto/client_proof_source.h" #include "quiche/quic/core/crypto/proof_verifier.h" #include "quiche/quic/core/io/quic_event_loop.h" #include "quiche/quic/core/quic_connection.h" @@ -32,7 +33,8 @@ // lookup. static std::unique_ptr<MasqueClient> Create( const std::string& uri_template, MasqueMode masque_mode, - QuicEventLoop* event_loop, std::unique_ptr<ProofVerifier> proof_verifier); + QuicEventLoop* event_loop, std::unique_ptr<ProofVerifier> proof_verifier, + std::unique_ptr<ClientProofSource> proof_source); // From QuicClient. std::unique_ptr<QuicSession> CreateQuicClientSession(
diff --git a/quiche/quic/masque/masque_client_bin.cc b/quiche/quic/masque/masque_client_bin.cc index 0bd8eec..c7a4b3d 100644 --- a/quiche/quic/masque/masque_client_bin.cc +++ b/quiche/quic/masque/masque_client_bin.cc
@@ -9,6 +9,7 @@ #include <cstddef> #include <cstdint> +#include <fstream> #include <iostream> #include <memory> #include <string> @@ -21,6 +22,8 @@ #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" #include "openssl/curve25519.h" +#include "quiche/quic/core/crypto/certificate_view.h" +#include "quiche/quic/core/crypto/client_proof_source.h" #include "quiche/quic/core/crypto/proof_verifier.h" #include "quiche/quic/core/http/quic_spdy_client_stream.h" #include "quiche/quic/core/io/quic_default_event_loop.h" @@ -42,6 +45,7 @@ #include "quiche/common/platform/api/quiche_command_line_flags.h" #include "quiche/common/platform/api/quiche_googleurl.h" #include "quiche/common/platform/api/quiche_logging.h" +#include "quiche/common/platform/api/quiche_reference_counted.h" #include "quiche/common/platform/api/quiche_system_event_loop.h" DEFINE_QUICHE_COMMAND_LINE_FLAG( @@ -89,6 +93,14 @@ "If set to true, no URLs need to be specified and instead a TAP device " "is brought up for a MASQUE CONNECT-ETHERNET session."); +DEFINE_QUICHE_COMMAND_LINE_FLAG( + std::string, client_cert_file, "", + "Path to the PEM-encoded client certificate chain."); + +DEFINE_QUICHE_COMMAND_LINE_FLAG( + std::string, client_cert_key_file, "", + "Path to the PEM/PKCS8-encoded client certificate private key."); + namespace quic { namespace { @@ -246,6 +258,43 @@ int fd_ = -1; }; +std::unique_ptr<ClientProofSource> CreateClientProofSource( + const std::string& client_cert_file, + const std::string& client_cert_key_file) { + if (client_cert_file.empty() || client_cert_key_file.empty()) { + std::cerr << "Both client cert and client cert key need to be set." + << std::endl; + return nullptr; + } + std::ifstream cert_stream(client_cert_file, std::ios::binary); + std::vector<std::string> certs = + CertificateView::LoadPemFromStream(&cert_stream); + if (certs.empty()) { + std::cerr << "Failed to load client certs." << std::endl; + return nullptr; + } + + std::ifstream key_stream(client_cert_key_file, std::ios::binary); + std::unique_ptr<CertificatePrivateKey> private_key = + CertificatePrivateKey::LoadPemFromStream(&key_stream); + if (private_key == nullptr) { + std::cerr << "Failed to load client cert key." << std::endl; + return nullptr; + } + + auto proof_source = std::make_unique<DefaultClientProofSource>(); + if (!proof_source->AddCertAndKey( + {"*"}, + quiche::QuicheReferenceCountedPointer<ClientProofSource::Chain>( + new ClientProofSource::Chain(certs)), + std::move(*private_key))) { + std::cerr << "Failed to add client cert and key." << std::endl; + return nullptr; + } + + return proof_source; +} + int RunMasqueClient(int argc, char* argv[]) { const char* usage = "Usage: masque_client [options] <proxy-url> <urls>..\n" @@ -384,6 +433,10 @@ } const bool dns_on_client = quiche::GetQuicheCommandLineFlag(FLAGS_dns_on_client); + std::string client_cert_file = + quiche::GetQuicheCommandLineFlag(FLAGS_client_cert_file); + std::string client_cert_key_file = + quiche::GetQuicheCommandLineFlag(FLAGS_client_cert_key_file); std::unique_ptr<QuicEventLoop> event_loop = GetDefaultEventLoop()->Create(QuicDefaultClock::Get()); @@ -411,15 +464,26 @@ if (masque_clients.empty()) { std::string host = uri_template.substr(parsed_uri_template.host.begin, parsed_uri_template.host.len); + std::unique_ptr<ProofVerifier> proof_verifier; if (disable_certificate_verification) { proof_verifier = std::make_unique<FakeProofVerifier>(); } else { proof_verifier = CreateDefaultProofVerifier(host); } - masque_client = - MasqueClient::Create(uri_template, masque_mode, event_loop.get(), - std::move(proof_verifier)); + + std::unique_ptr<ClientProofSource> proof_source; + if (!client_cert_file.empty() || !client_cert_key_file.empty()) { + proof_source = + CreateClientProofSource(client_cert_file, client_cert_key_file); + if (proof_source == nullptr) { + return 1; + } + } + + masque_client = MasqueClient::Create( + uri_template, masque_mode, event_loop.get(), + std::move(proof_verifier), std::move(proof_source)); } else { masque_client = tools::CreateAndConnectMasqueEncapsulatedClient(
diff --git a/quiche/quic/tools/quic_toy_client.cc b/quiche/quic/tools/quic_toy_client.cc index 1857025..4d366f7 100644 --- a/quiche/quic/tools/quic_toy_client.cc +++ b/quiche/quic/tools/quic_toy_client.cc
@@ -235,11 +235,14 @@ } auto proof_source = std::make_unique<DefaultClientProofSource>(); - proof_source->AddCertAndKey( - {"*"}, - quiche::QuicheReferenceCountedPointer<ClientProofSource::Chain>( - new ClientProofSource::Chain(certs)), - std::move(*private_key)); + if (!proof_source->AddCertAndKey( + {"*"}, + quiche::QuicheReferenceCountedPointer<ClientProofSource::Chain>( + new ClientProofSource::Chain(certs)), + std::move(*private_key))) { + std::cerr << "Failed to add client cert and key." << std::endl; + return nullptr; + } return proof_source; }