Re-sync with internal repository
[hiphop-php.git] / third-party / thrift / src / thrift / lib / cpp2 / security / FizzPeeker.cpp
blob9d57b8f2821096847dd4fbc026a0b38646ced726
1 /*
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>
30 namespace apache {
31 namespace thrift {
33 namespace {
35 class ThriftFizzAcceptorHandshakeHelper;
37 /**
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 {
63 public:
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) {}
76 void start(
77 folly::AsyncSSLSocket::UniquePtr sock,
78 wangle::AcceptorHandshakeHelper::Callback* callback) noexcept override {
79 callback_ = callback;
80 sslContext_ = sock->getSSLContext();
82 if (thriftParametersContext_) {
83 thriftExtension_ =
84 std::make_shared<apache::thrift::ThriftParametersServerExtension>(
85 thriftParametersContext_);
87 transport_ = createFizzServer(std::move(sock), context_, thriftExtension_);
88 transport_->accept(this);
91 private:
92 // AsyncFizzServer::HandshakeCallback API
93 void fizzHandshakeSuccess(
94 fizz::server::AsyncFizzServer* transport) noexcept override {
95 VLOG(3) << "Fizz handshake success";
97 tinfo_.acceptTime = acceptTime_;
98 tinfo_.secure = true;
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));
125 } else {
126 callback_->connectionReady(
127 std::move(transport_),
128 std::move(appProto),
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
144 // here.
145 plaintextTransport->setPreReceivedData(std::move(endOfData));
146 plaintextTransport->cacheAddresses();
147 // kill the fizz socket unique ptr
148 transport_.reset();
149 callback_->connectionReady(
150 std::move(plaintextTransport),
151 std::move(appProto),
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>
165 thriftExtension_;
166 AsyncStopTLS::UniquePtr stopTLSAsyncFrame_;
168 } // namespace
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) {
175 if (!context_) {
176 return nullptr;
178 auto optionsCopy = options_;
179 return wangle::AcceptorHandshakeHelper::UniquePtr(
180 new ThriftFizzAcceptorHandshakeHelper(
181 context_,
182 clientAddr,
183 acceptTime,
184 tinfo,
185 std::move(optionsCopy),
186 thriftParametersContext_));
189 } // namespace thrift
190 } // namespace apache