2 * Copyright (c) Meta Platforms, Inc. and affiliates.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <fizz/server/AsyncFizzServer.h>
18 #include <folly/io/async/AsyncSocket.h>
19 #include <folly/net/NetworkSocket.h>
20 #include <thrift/lib/cpp/async/TAsyncSSLSocket.h>
21 #include <thrift/lib/cpp2/security/AsyncStopTLS.h>
22 #include <thrift/lib/cpp2/security/FizzPeeker.h>
23 #include <thrift/lib/cpp2/security/SSLUtil.h>
24 #include <thrift/lib/cpp2/security/extensions/ThriftParametersContext.h>
25 #include <thrift/lib/cpp2/security/extensions/ThriftParametersServerExtension.h>
26 #include <thrift/lib/thrift/gen-cpp2/RpcMetadata_types.h>
27 #include <wangle/acceptor/FizzAcceptorHandshakeHelper.h>
28 #include <wangle/acceptor/SSLAcceptorHandshakeHelper.h>
35 class ThriftFizzAcceptorHandshakeHelper
;
38 * ThriftFizzAcceptorHandshakeHelper represents a single asynchronous Fizz
39 * handshake. It has Thrift specific functionality such as including
40 * a Thrift extension in the handshake and managing StopTLS negotiations.
42 * IMPLEMENTATION NOTES:
43 * To fulfill the AcceptorHandshakeHelper contract as documented in wangle,
44 * we must ensure that we always send either a `connectionReady()` or
45 * `connectionError()` during the lifetime of this helper object.
47 * `dropConnection()` is inherited from the parent, which will close the
48 * underlying socket. To fulfill our promises to the Handshake Manager, we
49 * just need to ensure that at any time while this object lives, if we close
50 * the underlying socket, this will result in some error being propagated.
52 * If the socket is closed:
53 * * During the initial TLS handshake, this results in a fizzHandshakeErr
54 * firing, which will trigger a connectionError().
55 * * If we are performing StopTLS, and we receive a `dropConnection()` after
56 * the initial TLS handshake but before the peer close_notify arrives, then
57 * we rely on `AsyncStopTLS` to receive a `readErr()` which will fire
58 * `stopTLSError()` which will fire `connectionError()`
60 class ThriftFizzAcceptorHandshakeHelper
61 : public wangle::FizzAcceptorHandshakeHelper
,
62 private AsyncStopTLS::Callback
{
64 ThriftFizzAcceptorHandshakeHelper(
65 std::shared_ptr
<const fizz::server::FizzServerContext
> context
,
66 const folly::SocketAddress
& clientAddr
,
67 std::chrono::steady_clock::time_point acceptTime
,
68 wangle::TransportInfo
& tinfo
,
69 wangle::FizzHandshakeOptions
&& options
,
70 const std::shared_ptr
<apache::thrift::ThriftParametersContext
>&
71 thriftParametersContext
)
72 : wangle::FizzAcceptorHandshakeHelper::FizzAcceptorHandshakeHelper(
73 context
, clientAddr
, acceptTime
, tinfo
, std::move(options
)),
74 thriftParametersContext_(thriftParametersContext
) {}
77 folly::AsyncSSLSocket::UniquePtr sock
,
78 wangle::AcceptorHandshakeHelper::Callback
* callback
) noexcept override
{
80 sslContext_
= sock
->getSSLContext();
82 if (thriftParametersContext_
) {
84 std::make_shared
<apache::thrift::ThriftParametersServerExtension
>(
85 thriftParametersContext_
);
87 transport_
= createFizzServer(std::move(sock
), context_
, thriftExtension_
);
88 transport_
->accept(this);
92 // AsyncFizzServer::HandshakeCallback API
93 void fizzHandshakeSuccess(
94 fizz::server::AsyncFizzServer
* transport
) noexcept override
{
95 VLOG(3) << "Fizz handshake success";
97 tinfo_
.acceptTime
= acceptTime_
;
99 tinfo_
.sslVersion
= 0x0304;
100 tinfo_
.securityType
= transport
->getSecurityProtocol();
101 tinfo_
.sslSetupTime
= std::chrono::duration_cast
<std::chrono::milliseconds
>(
102 std::chrono::steady_clock::now() - acceptTime_
);
104 auto* handshakeLogging
= transport
->getState().handshakeLogging();
105 if (handshakeLogging
&& handshakeLogging
->clientSni
) {
106 tinfo_
.sslServerName
=
107 std::make_shared
<std::string
>(*handshakeLogging
->clientSni
);
110 auto appProto
= transport
->getApplicationProtocol();
112 if (loggingCallback_
) {
113 loggingCallback_
->logFizzHandshakeSuccess(*transport
, tinfo_
);
116 if (thriftExtension_
&& thriftExtension_
->getNegotiatedStopTLS()) {
117 VLOG(5) << "Beginning StopTLS negotiation";
118 stopTLSAsyncFrame_
.reset(new AsyncStopTLS(*this));
120 // We are running as part of a wangle::ManagedConnection. The timeout
121 // is managed by wangle; wangle will close() the underlying transport
122 // (which will trigger an error) when its own timer elapses.
123 stopTLSAsyncFrame_
->start(
124 transport
, AsyncStopTLS::Role::Server
, std::chrono::milliseconds(0));
126 callback_
->connectionReady(
127 std::move(transport_
),
129 SecureTransportType::TLS
,
130 wangle::SSLErrorEnum::NO_ERROR
);
134 // Invoked by AsyncStopTLS when StopTLS downgrade completes successfully
135 void stopTLSSuccess(std::unique_ptr
<folly::IOBuf
> endOfData
) override
{
136 auto appProto
= transport_
->getApplicationProtocol();
137 auto plaintextTransport
= moveToPlaintext(transport_
.get());
138 // The server initiates the close, which means the client will be the first
139 // to successfully terminate tls and return the socket back to the caller.
140 // What this means for us is we clearly don't know if our fizz transport
141 // will only read the close notify and not additionally read any data the
142 // application decided to send when it got back the socket. Fizz already
143 // exposes any post close notify data and we shove it back into the socket
145 plaintextTransport
->setPreReceivedData(std::move(endOfData
));
146 plaintextTransport
->cacheAddresses();
147 // kill the fizz socket unique ptr
149 callback_
->connectionReady(
150 std::move(plaintextTransport
),
152 SecureTransportType::TLS
,
153 wangle::SSLErrorEnum::NO_ERROR
);
156 // Invoked by AsyncStopTLS when StopTLS downgrade was interrupted or did
157 // not finish successfully.
158 void stopTLSError(const folly::exception_wrapper
& ew
) override
{
159 callback_
->connectionError(transport_
.get(), ew
, sslError_
);
162 std::shared_ptr
<apache::thrift::ThriftParametersContext
>
163 thriftParametersContext_
;
164 std::shared_ptr
<apache::thrift::ThriftParametersServerExtension
>
166 AsyncStopTLS::UniquePtr stopTLSAsyncFrame_
;
170 wangle::AcceptorHandshakeHelper::UniquePtr
FizzPeeker::getHelper(
171 const std::vector
<uint8_t>& /* bytes */,
172 const folly::SocketAddress
& clientAddr
,
173 std::chrono::steady_clock::time_point acceptTime
,
174 wangle::TransportInfo
& tinfo
) {
178 auto optionsCopy
= options_
;
179 return wangle::AcceptorHandshakeHelper::UniquePtr(
180 new ThriftFizzAcceptorHandshakeHelper(
185 std::move(optionsCopy
),
186 thriftParametersContext_
));
189 } // namespace thrift
190 } // namespace apache