Skip to content

Commit c7ab4f5

Browse files
Ankit Kumarmeta-codesync[bot]
authored andcommitted
Plumb peer cert through to ThriftConnContext
Summary: Snapshot the peer certificate and negotiated protocol into `PeerSecurityInfo` when the fizz handshake completes and carry it to `ThriftConnContext`, so it survives a StopTLS downgrade that leaves the transport unable to report either. Reviewed By: robertroeser Differential Revision: D116839723 fbshipit-source-id: 0b99cd44589187990d20ceb91cd3c41f0d299d0d
1 parent 559eb32 commit c7ab4f5

28 files changed

Lines changed: 248 additions & 43 deletions

third-party/thrift/src/thrift/lib/cpp2/fast_thrift/bench/tcp/TcpServer.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,9 @@ class TcpConnectionFactory {
141141

142142
TcpConnection getConnection(
143143
folly::AsyncTransport::UniquePtr socket,
144-
const folly::SocketAddress& /*clientAddr*/) {
144+
const folly::SocketAddress& /*clientAddr*/,
145+
const std::shared_ptr<const connection::PeerSecurityInfo>&
146+
/*peerSecurity*/) {
145147
return build_(std::move(socket));
146148
}
147149

third-party/thrift/src/thrift/lib/cpp2/fast_thrift/connection/ConnectionFactory.h

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717
#pragma once
1818

1919
#include <functional>
20+
#include <memory>
2021
#include <type_traits>
2122
#include <utility>
2223

2324
#include <folly/SocketAddress.h>
2425
#include <folly/io/async/AsyncTransport.h>
26+
#include <thrift/lib/cpp2/fast_thrift/connection/common/Messages.h>
2527

2628
namespace apache::thrift::fast_thrift::connection {
2729

@@ -52,25 +54,30 @@ concept Connection = requires(C& c, std::function<void()> cb) {
5254
};
5355

5456
/**
55-
* ConnectionFactory — anything that, given a ready transport and the peer
56-
* address observed when the socket was accepted, produces a Connection. The
57-
* returned type is up to the factory; the connection layer doesn't care, as
58-
* long as it satisfies the Connection concept.
57+
* ConnectionFactory — anything that, given a ready transport plus what was
58+
* observed about the peer when the socket was accepted and secured, produces a
59+
* Connection. The returned type is up to the factory; the connection layer
60+
* doesn't care, as long as it satisfies the Connection concept.
5961
*
60-
* clientAddr is passed rather than re-derived from the transport: the
61-
* transport can no longer report a peer once that peer has gone away.
62+
* clientAddr and peerSecurity are passed rather than re-derived from the
63+
* transport because the transport can no longer report either: the peer
64+
* address is gone once the peer is, and a StopTLS downgrade replaces the
65+
* secured transport with a plaintext one. peerSecurity is null on a connection
66+
* that negotiated no security.
6267
*/
6368
template <typename F>
6469
concept ConnectionFactory =
6570
requires(
6671
F& f,
6772
folly::AsyncTransport::UniquePtr socket,
68-
const folly::SocketAddress& clientAddr) {
69-
{ f.getConnection(std::move(socket), clientAddr) };
73+
const folly::SocketAddress& clientAddr,
74+
const std::shared_ptr<const PeerSecurityInfo>& peerSecurity) {
75+
{ f.getConnection(std::move(socket), clientAddr, peerSecurity) };
7076
} &&
7177
Connection<std::decay_t<decltype(std::declval<F&>().getConnection(
7278
std::declval<folly::AsyncTransport::UniquePtr>(),
73-
std::declval<const folly::SocketAddress&>()))>>;
79+
std::declval<const folly::SocketAddress&>(),
80+
std::declval<const std::shared_ptr<const PeerSecurityInfo>&>()))>>;
7481

7582
/**
7683
* The connection type produced by a given factory.
@@ -79,6 +86,7 @@ template <ConnectionFactory F>
7986
using FactoryConnectionType =
8087
std::decay_t<decltype(std::declval<F&>().getConnection(
8188
std::declval<folly::AsyncTransport::UniquePtr>(),
82-
std::declval<const folly::SocketAddress&>()))>;
89+
std::declval<const folly::SocketAddress&>(),
90+
std::declval<const std::shared_ptr<const PeerSecurityInfo>&>()))>;
8391

8492
} // namespace apache::thrift::fast_thrift::connection

third-party/thrift/src/thrift/lib/cpp2/fast_thrift/connection/common/Messages.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,28 @@
1616

1717
#pragma once
1818

19+
#include <memory>
20+
#include <string>
21+
1922
#include <folly/SocketAddress.h>
2023
#include <folly/io/async/AsyncTransport.h>
2124

2225
namespace apache::thrift::fast_thrift::connection {
2326

27+
// What the peer proved about itself during the security handshake.
28+
//
29+
// Captured once, when it is negotiated, and carried alongside the transport
30+
// rather than re-read from it later: a StopTLS downgrade replaces the
31+
// transport with a plaintext one that can report neither. Null on a connection
32+
// that negotiated no security.
33+
//
34+
// Held behind a shared_ptr so the messages carrying it stay inside the
35+
// pipeline's inline-message budget; the allocation is once per handshake.
36+
struct PeerSecurityInfo {
37+
std::shared_ptr<const folly::AsyncTransportCertificate> peerCertificate;
38+
std::string securityProtocol;
39+
};
40+
2441
// Single message type that flows through the acceptance pipeline. Each
2542
// accepted socket enters the pipeline as one ConnectionMessage at the head;
2643
// each handler may upgrade the transport in-place (e.g. plain AsyncSocket →
@@ -30,6 +47,7 @@ namespace apache::thrift::fast_thrift::connection {
3047
struct ConnectionMessage {
3148
folly::AsyncTransport::UniquePtr transport;
3249
folly::SocketAddress clientAddr;
50+
std::shared_ptr<const PeerSecurityInfo> peerSecurity;
3351
};
3452

3553
} // namespace apache::thrift::fast_thrift::connection

third-party/thrift/src/thrift/lib/cpp2/fast_thrift/connection/endpoint/ConnectionListener.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ class ConnectionListener : public folly::DelayedDestruction,
137137
ConnectionMessage msg{
138138
.transport = std::move(transport),
139139
.clientAddr = clientAddr,
140+
.peerSecurity = nullptr,
140141
};
141142
auto result =
142143
pipeline_->fireRead(channel_pipeline::erase_and_box(std::move(msg)));

third-party/thrift/src/thrift/lib/cpp2/fast_thrift/connection/handler/ConnectionBuilderHandler.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ class ConnectionBuilderHandler {
5555
channel_pipeline::Result onRead(
5656
Context& ctx, channel_pipeline::TypeErasedBox&& msg) noexcept {
5757
auto ready = msg.take<ConnectionMessage>();
58-
auto conn =
59-
factory_.getConnection(std::move(ready.transport), ready.clientAddr);
58+
auto conn = factory_.getConnection(
59+
std::move(ready.transport), ready.clientAddr, ready.peerSecurity);
6060
return ctx.fireRead(channel_pipeline::erase_and_box(std::move(conn)));
6161
}
6262

third-party/thrift/src/thrift/lib/cpp2/fast_thrift/connection/handler/ConnectionTLSHandler.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,12 @@ class ConnectionTLSHandler {
146146
this,
147147
[](void* self,
148148
folly::AsyncTransport::UniquePtr transport,
149-
folly::SocketAddress clientAddr) noexcept {
149+
folly::SocketAddress clientAddr,
150+
std::shared_ptr<const PeerSecurityInfo> peerSecurity) noexcept {
150151
return static_cast<ConnectionTLSHandler*>(self)->onResolved(
151-
std::move(transport), std::move(clientAddr));
152+
std::move(transport),
153+
std::move(clientAddr),
154+
std::move(peerSecurity));
152155
},
153156
[](void* self, folly::exception_wrapper&& e) noexcept {
154157
static_cast<ConnectionTLSHandler*>(self)->onInnerException(
@@ -181,6 +184,7 @@ class ConnectionTLSHandler {
181184
.clientAddr = std::move(incoming.clientAddr),
182185
.tlsParams = nullptr,
183186
.extension = nullptr,
187+
.peerSecurity = nullptr,
184188
};
185189
return tail_.submit(std::move(request));
186190
}
@@ -217,13 +221,15 @@ class ConnectionTLSHandler {
217221
// here and is fired onto the outer pipeline as a ConnectionMessage.
218222
channel_pipeline::Result onResolved(
219223
folly::AsyncTransport::UniquePtr transport,
220-
folly::SocketAddress clientAddr) noexcept {
224+
folly::SocketAddress clientAddr,
225+
std::shared_ptr<const PeerSecurityInfo> peerSecurity) noexcept {
221226
if (FOLLY_UNLIKELY(!outerCtx_)) {
222227
return channel_pipeline::Result::Success;
223228
}
224229
ConnectionMessage out{
225230
.transport = std::move(transport),
226231
.clientAddr = std::move(clientAddr),
232+
.peerSecurity = std::move(peerSecurity),
227233
};
228234
return outerCtx_->fireRead(channel_pipeline::erase_and_box(std::move(out)));
229235
}

third-party/thrift/src/thrift/lib/cpp2/fast_thrift/connection/security/bench/TLSPipelineBench.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ struct Harness {
214214
conn::ConnectionMessage msg{
215215
.transport = folly::AsyncTransport::UniquePtr(serverSock.release()),
216216
.clientAddr = folly::SocketAddress{"127.0.0.1", 0},
217+
.peerSecurity = nullptr,
217218
};
218219
(void)pipeline->fireRead(channel_pipeline::erase_and_box(std::move(msg)));
219220

third-party/thrift/src/thrift/lib/cpp2/fast_thrift/connection/security/common/Messages.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include <folly/SocketAddress.h>
2222
#include <folly/io/async/AsyncTransport.h>
23+
#include <thrift/lib/cpp2/fast_thrift/connection/common/Messages.h>
2324
#include <thrift/lib/cpp2/fast_thrift/security/FizzServerContextBuilder.h>
2425
#include <thrift/lib/cpp2/security/extensions/ThriftParametersServerExtension.h>
2526

@@ -41,21 +42,28 @@ namespace apache::thrift::fast_thrift::connection::security {
4142
//
4243
// `extension` is null until the fizz handshake completes, then read by
4344
// StopTLSV1Handler to decide whether to downgrade.
45+
//
46+
// `peerSecurity` is null until the handshake completes, and every stage after
47+
// it must carry it forward: it is the only surviving record of what the peer
48+
// proved once StopTLS has swapped the fizz transport for a plaintext one.
4449
struct TLSRequestMessage {
4550
folly::AsyncTransport::UniquePtr transport;
4651
folly::SocketAddress clientAddr;
4752
std::shared_ptr<const apache::thrift::fast_thrift::security::TLSParams>
4853
tlsParams;
4954
std::shared_ptr<apache::thrift::ThriftParametersServerExtension> extension;
55+
std::shared_ptr<const PeerSecurityInfo> peerSecurity;
5056
};
5157

5258
// Inbound (read/return path). TLSFinalizer collapses a resolved
5359
// TLSRequestMessage down to this at the head; the stages pass it through
5460
// untouched to the tail adapter, which hands the resolved transport off.
55-
// Carries only what handoff needs — no negotiation state.
61+
// Carries only what handoff needs — the negotiated peer identity, but no
62+
// negotiation state.
5663
struct TLSResponseMessage {
5764
folly::AsyncTransport::UniquePtr transport;
5865
folly::SocketAddress clientAddr;
66+
std::shared_ptr<const PeerSecurityInfo> peerSecurity;
5967
};
6068

6169
} // namespace apache::thrift::fast_thrift::connection::security

third-party/thrift/src/thrift/lib/cpp2/fast_thrift/connection/security/handler/FizzHandshakeHandler.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,11 +194,18 @@ class FizzHandshakeHandler {
194194
if (FOLLY_UNLIKELY(!ctx_)) {
195195
return;
196196
}
197+
// Snapshot what the peer proved while the fizz session is still the
198+
// transport: a StopTLS downgrade downstream leaves nothing to read it from.
199+
auto peerSecurity =
200+
std::make_shared<const PeerSecurityInfo>(PeerSecurityInfo{
201+
.peerCertificate = fizzServer->getState().clientCert(),
202+
.securityProtocol = fizzServer->getSecurityProtocol()});
197203
TLSRequestMessage upgraded{
198204
.transport = folly::AsyncTransport::UniquePtr(fizzServer.release()),
199205
.clientAddr = clientAddr,
200206
.tlsParams = std::move(tlsParams),
201207
.extension = std::move(extension),
208+
.peerSecurity = std::move(peerSecurity),
202209
};
203210
auto result =
204211
ctx_->fireWrite(channel_pipeline::erase_and_box(std::move(upgraded)));

third-party/thrift/src/thrift/lib/cpp2/fast_thrift/connection/security/handler/StopTLSV1Handler.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,19 @@ class StopTLSV1Handler {
122122
auto stopTLSTimeout = incoming.tlsParams
123123
? incoming.tlsParams->handshakeTimeout
124124
: std::nullopt;
125+
// The downgrade hands back a plaintext transport that can no longer report
126+
// the peer, so the handshake's record of it has to ride the callback.
127+
auto peerSecurity = std::move(incoming.peerSecurity);
125128

126129
util::StopTLSHelper::UniquePtr helper(new util::StopTLSHelper(
127130
std::move(fizzServer),
128131
stopTLSTimeout,
129132
[this](util::StopTLSHelper* h) noexcept { inFlight_.erase(h); },
130-
[this, clientAddr, extension](
133+
[this, clientAddr, extension, peerSecurity](
131134
folly::AsyncTransport::UniquePtr plaintext,
132135
const folly::exception_wrapper& ex) noexcept {
133-
onStopTLSComplete(std::move(plaintext), ex, clientAddr, extension);
136+
onStopTLSComplete(
137+
std::move(plaintext), ex, clientAddr, extension, peerSecurity);
134138
}));
135139
auto* raw = helper.get();
136140
inFlight_.emplace(raw, std::move(helper));
@@ -177,7 +181,8 @@ class StopTLSV1Handler {
177181
const folly::exception_wrapper& ex,
178182
const folly::SocketAddress& clientAddr,
179183
std::shared_ptr<apache::thrift::ThriftParametersServerExtension>
180-
extension) noexcept {
184+
extension,
185+
std::shared_ptr<const PeerSecurityInfo> peerSecurity) noexcept {
181186
if (ex || !plaintext) {
182187
XLOG(DBG3) << "StopTLS V1 failed for " << clientAddr.describe() << ": "
183188
<< (ex ? ex.what().toStdString() : std::string("null"));
@@ -191,6 +196,7 @@ class StopTLSV1Handler {
191196
.clientAddr = clientAddr,
192197
.tlsParams = nullptr,
193198
.extension = std::move(extension),
199+
.peerSecurity = std::move(peerSecurity),
194200
};
195201
auto result =
196202
ctx_->fireWrite(channel_pipeline::erase_and_box(std::move(downgraded)));

0 commit comments

Comments
 (0)