proxygen
FizzServerCommand.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018-present, Facebook, Inc.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree.
7  */
8 
17 #include <fizz/util/Parse.h>
18 
21 
22 #include <string>
23 #include <vector>
24 
25 using namespace fizz::server;
26 using namespace folly;
27 
28 namespace fizz {
29 namespace tool {
30 namespace {
31 
32 void printUsage() {
33  // clang-format off
34  std::cerr
35  << "Usage: s_server args\n"
36  << "\n"
37  << "Supported arguments:\n"
38  << " -accept port (set port to accept connections on. Default: 8443)\n"
39  << " -ciphers c1,... (comma-separated custom list of ciphers to use in order of preference)\n"
40  << " -cert cert (PEM format server certificate. Default: none, generates a self-signed cert)\n"
41  << " -key key (PEM format private key for server certificate. Default: none)\n"
42  << " -pass password (private key password. Default: none)\n"
43  << " -requestcert (request an optional client certificate from clients. Default: false)\n"
44  << " -requirecert (require a client certificate from clients. Default: false)\n"
45  << " -capaths d1:... (colon-separated paths to directories of CA certs used for verification)\n"
46  << " -cafile file (path to bundle of CA certs used for verification)\n"
47  << " -early (enables sending early data during resumption. Default: false)\n"
48  << " -alpn alpn1,... (comma-separated list of ALPNs to support. Default: none)\n"
49  << " -fallback (enables falling back to OpenSSL for pre-1.3 connections. Default: false)\n"
50  << " -loop (don't exit after client disconnect. Default: false)\n"
51  << " -quiet (hide informational logging. Default: false)\n";
52  // clang-format on
53 }
54 
55 class FizzServerAcceptor : AsyncServerSocket::AcceptCallback {
56  public:
57  explicit FizzServerAcceptor(
58  uint16_t port,
59  std::shared_ptr<FizzServerContext> serverCtx,
60  bool loop,
61  EventBase* evb,
62  std::shared_ptr<SSLContext> sslCtx);
63  void connectionAccepted(
64  int fd,
65  const SocketAddress& clientAddr) noexcept override;
66 
67  void acceptError(const std::exception& ex) noexcept override;
68  void done();
69 
70  private:
71  bool loop_{false};
72  EventBase* evb_{nullptr};
73  std::shared_ptr<FizzServerContext> ctx_;
74  std::shared_ptr<SSLContext> sslCtx_;
76  std::unique_ptr<AsyncFizzServer::HandshakeCallback> cb_;
77  std::unique_ptr<TerminalInputHandler> inputHandler_;
78 };
79 
80 class FizzExampleServer : public AsyncFizzServer::HandshakeCallback,
83  public InputHandlerCallback {
84  public:
85  explicit FizzExampleServer(
86  std::shared_ptr<AsyncFizzServer> transport,
87  FizzServerAcceptor* acceptor,
88  std::shared_ptr<SSLContext> sslCtx)
89  : transport_(transport), acceptor_(acceptor), sslCtx_(sslCtx) {}
90  void fizzHandshakeSuccess(AsyncFizzServer* server) noexcept override {
91  server->setReadCB(this);
92  connected_ = true;
93  printHandshakeSuccess();
94  }
95 
96  void fizzHandshakeError(
97  AsyncFizzServer* /*server*/,
98  exception_wrapper ex) noexcept override {
99  LOG(ERROR) << "Handshake error: " << ex.what();
100  finish();
101  }
102 
103  void fizzHandshakeAttemptFallback(
104  std::unique_ptr<IOBuf> clientHello) override {
105  CHECK(transport_);
106  LOG(INFO) << "Fallback attempt";
107  auto socket = transport_->getUnderlyingTransport<AsyncSocket>();
108  auto evb = socket->getEventBase();
109  auto fd = socket->detachFd();
110  transport_.reset();
111  sslSocket_ =
113  sslSocket_->setPreReceivedData(std::move(clientHello));
114  sslSocket_->sslAccept(this);
115  }
116 
117  void handshakeSuc(AsyncSSLSocket* sock) noexcept override {
118  LOG(INFO) << "Fallback SSL Handshake success";
119  sock->setReadCB(this);
120  connected_ = true;
121  printFallbackSuccess();
122  }
123 
124  void handshakeErr(
125  AsyncSSLSocket* /*sock*/,
126  const AsyncSocketException& ex) noexcept override {
127  LOG(ERROR) << "Fallback SSL Handshake error: " << ex.what();
128  finish();
129  }
130 
131  void getReadBuffer(void** bufReturn, size_t* lenReturn) override {
132  *bufReturn = readBuf_.data();
133  *lenReturn = readBuf_.size();
134  }
135 
136  void readDataAvailable(size_t len) noexcept override {
137  std::cout << std::string(readBuf_.data(), len);
138  }
139 
140  bool isBufferMovable() noexcept override {
141  return true;
142  }
143 
144  void readBufferAvailable(std::unique_ptr<IOBuf> buf) noexcept override {
145  std::cout << StringPiece(buf->coalesce()).str();
146  }
147 
148  void readEOF() noexcept override {
149  LOG(INFO) << "EOF";
150  finish();
151  }
152 
153  void readErr(const AsyncSocketException& ex) noexcept override {
154  LOG(ERROR) << "Read error: " << ex.what();
155  finish();
156  }
157 
158  bool connected() const override {
159  return connected_;
160  }
161 
162  void write(std::unique_ptr<IOBuf> msg) override {
163  if (transport_) {
164  transport_->writeChain(nullptr, std::move(msg));
165  } else if (sslSocket_) {
166  sslSocket_->writeChain(nullptr, std::move(msg));
167  }
168  }
169 
170  void close() override {
171  finish();
172  }
173 
174  private:
175  void printHandshakeSuccess() {
176  auto& state = transport_->getState();
177  auto serverCert = state.serverCert();
178  auto clientCert = state.clientCert();
179  LOG(INFO) << "Handshake succeeded.";
180  LOG(INFO) << " TLS Version: " << toString(*state.version());
181  LOG(INFO) << " Cipher Suite: " << toString(*state.cipher());
182  LOG(INFO) << " Named Group: "
183  << (state.group() ? toString(*state.group()) : "(none)");
184  LOG(INFO) << " Signature Scheme: "
185  << (state.sigScheme() ? toString(*state.sigScheme()) : "(none)");
186  LOG(INFO) << " PSK: " << toString(*state.pskType());
187  LOG(INFO) << " PSK Mode: "
188  << (state.pskMode() ? toString(*state.pskMode()) : "(none)");
189  LOG(INFO) << " Key Exchange Type: " << toString(*state.keyExchangeType());
190  LOG(INFO) << " Early: " << toString(*state.earlyDataType());
191  LOG(INFO) << " Server identity: "
192  << (serverCert ? serverCert->getIdentity() : "(none)");
193  LOG(INFO) << " Client Identity: "
194  << (clientCert ? clientCert->getIdentity() : "(none)");
195  LOG(INFO) << " ALPN: " << state.alpn().value_or("(none)");
196  }
197 
198  void printFallbackSuccess() {
199  auto serverCert = sslSocket_->getSelfCertificate();
200  auto clientCert = sslSocket_->getPeerCertificate();
201  auto ssl = sslSocket_->getSSL();
202  LOG(INFO) << "Handshake succeeded.";
203  LOG(INFO) << " TLS Version: " << SSL_get_version(ssl);
204  LOG(INFO) << " Cipher: " << sslSocket_->getNegotiatedCipherName();
205  LOG(INFO) << " Signature Algorithm: "
206  << sslSocket_->getSSLCertSigAlgName();
207  LOG(INFO) << " Server identity: "
208  << (serverCert ? serverCert->getIdentity() : "(none)");
209  LOG(INFO) << " Client Identity: "
210  << (clientCert ? clientCert->getIdentity() : "(none)");
211  }
212 
213  void finish() {
214  if (transport_ || sslSocket_) {
215  transport_.reset();
216  sslSocket_.reset();
217  acceptor_->done();
218  }
219  }
220 
221  std::shared_ptr<AsyncFizzServer> transport_;
223  FizzServerAcceptor* acceptor_;
224  std::shared_ptr<SSLContext> sslCtx_;
225  std::array<char, 8192> readBuf_;
226  bool connected_{false};
227 };
228 
229 FizzServerAcceptor::FizzServerAcceptor(
230  uint16_t port,
231  std::shared_ptr<FizzServerContext> serverCtx,
232  bool loop,
233  EventBase* evb,
234  std::shared_ptr<SSLContext> sslCtx)
235  : loop_(loop), evb_(evb), ctx_(serverCtx), sslCtx_(sslCtx) {
237  socket_->bind(port);
238  socket_->listen(100);
239  socket_->addAcceptCallback(this, evb_);
240  socket_->startAccepting();
241  LOG(INFO) << "Started listening on " << socket_->getAddress();
242 }
243 
244 void FizzServerAcceptor::connectionAccepted(
245  int fd,
246  const SocketAddress& clientAddr) noexcept {
247  LOG(INFO) << "Connection accepted from " << clientAddr;
248  auto sock = new AsyncSocket(evb_, fd);
249  std::shared_ptr<AsyncFizzServer> transport = AsyncFizzServer::UniquePtr(
251  socket_->pauseAccepting();
252  auto serverCb = std::make_unique<FizzExampleServer>(transport, this, sslCtx_);
253  inputHandler_ = std::make_unique<TerminalInputHandler>(evb_, serverCb.get());
254  cb_ = std::move(serverCb);
255  transport->accept(cb_.get());
256 }
257 
258 void FizzServerAcceptor::acceptError(const std::exception& ex) noexcept {
259  LOG(ERROR) << "Failed to accept connection: " << ex.what();
260  if (!loop_) {
262  }
263 }
264 
265 void FizzServerAcceptor::done() {
266  cb_.reset();
267  inputHandler_.reset();
268  if (loop_) {
269  socket_->startAccepting();
270  } else {
271  socket_.reset();
272  }
273 }
274 
275 } // namespace
276 
277 int fizzServerCommand(const std::vector<std::string>& args) {
278  uint16_t port = 8443;
280  std::string certPath;
281  std::string keyPath;
282  std::string keyPass;
283  ClientAuthMode clientAuthMode = ClientAuthMode::None;
284  std::string caPaths;
285  std::string caFile;
286  bool early = false;
287  std::vector<std::string> alpns;
288  bool loop = false;
289  bool fallback = false;
290 
291  // clang-format off
292  FizzArgHandlerMap handlers = {
293  {"-accept", {true, [&port](const std::string& arg) {
294  port = portFromString(arg, true);
295  }}},
296  {"-ciphers", {true, [&ciphers](const std::string& arg) {
297  std::vector<CipherSuite> newCiphers;
298  auto remainder = arg;
299  try {
300  for (auto commaPos = remainder.find(',');
301  commaPos != std::string::npos;
302  commaPos = remainder.find(',')) {
303  auto cipher = parse<CipherSuite>(remainder.substr(0, commaPos));
304  remainder = remainder.substr(commaPos+1);
305  newCiphers.push_back(cipher);
306  }
307 
308  newCiphers.push_back(parse<CipherSuite>(remainder));
309  ciphers = std::move(newCiphers);
310  }
311  catch (const std::exception& e) {
312  LOG(ERROR) << "Error parsing cipher suites: " << e.what();
313  }
314  }}},
315  {"-cert", {true, [&certPath](const std::string& arg) { certPath = arg; }}},
316  {"-key", {true, [&keyPath](const std::string& arg) { keyPath = arg; }}},
317  {"-pass", {true, [&keyPass](const std::string& arg) { keyPass = arg; }}},
318  {"-requestcert", {false, [&clientAuthMode](const std::string&) {
319  clientAuthMode = ClientAuthMode::Optional;
320  }}},
321  {"-requirecert", {false, [&clientAuthMode](const std::string&) {
322  clientAuthMode = ClientAuthMode::Required;
323  }}},
324  {"-capaths", {true, [&caPaths](const std::string& arg) { caPaths = arg; }}},
325  {"-cafile", {true, [&caFile](const std::string& arg) { caFile = arg; }}},
326  {"-early", {false, [&early](const std::string&) { early = true; }}},
327  {"-alpn", {true, [&alpns](const std::string& arg) {
328  alpns.clear();
329  auto remainder = arg;
330  for (auto commaPos = remainder.find(',');
331  commaPos != std::string::npos;
332  commaPos = remainder.find(',')) {
333  alpns.push_back(remainder.substr(0, commaPos));
334  remainder = remainder.substr(commaPos+1);
335  }
336 
337  alpns.push_back(remainder);
338  }}},
339  {"-loop", {false, [&loop](const std::string&) { loop = true; }}},
340  {"-quiet", {false, [](const std::string&) {
341  FLAGS_minloglevel = google::GLOG_ERROR;
342  }}},
343  {"-fallback", {false, [&fallback](const std::string&) {
344  fallback = true;
345  }}}
346  };
347  // clang-format on
348 
349  try {
350  if (parseArguments(args, handlers, printUsage)) {
351  // Parsing failed, return
352  return 1;
353  }
354  } catch (const std::exception& e) {
355  LOG(ERROR) << "Error: " << e.what();
356  return 1;
357  }
358 
359  // Sanity check input.
360  if (certPath.empty() != keyPath.empty()) {
361  LOG(ERROR) << "-cert and -key are both required when specified";
362  return 1;
363  }
364 
365  EventBase evb;
366  std::shared_ptr<const CertificateVerifier> verifier;
367 
368  if (clientAuthMode != ClientAuthMode::None) {
369  // Initialize CA store first, if given.
371  if (!caPaths.empty() || !caFile.empty()) {
372  storePtr.reset(X509_STORE_new());
373  auto caFilePtr = caFile.empty() ? nullptr : caFile.c_str();
374  auto caPathPtr = caPaths.empty() ? nullptr : caPaths.c_str();
375 
376  if (X509_STORE_load_locations(storePtr.get(), caFilePtr, caPathPtr) ==
377  0) {
378  LOG(ERROR) << "Failed to load CA certificates";
379  return 1;
380  }
381  }
382 
383  verifier = std::make_shared<const DefaultCertificateVerifier>(
385  }
386 
387  auto serverContext = std::make_shared<FizzServerContext>();
388  if (ciphers) {
389  serverContext->setSupportedCiphers({*ciphers});
390  }
391  serverContext->setClientAuthMode(clientAuthMode);
392  serverContext->setClientCertVerifier(verifier);
393 
394  auto ticketCipher = std::make_shared<AeadTicketCipher<
397  HkdfImpl<Sha256>>>();
398  auto ticketSeed = RandomGenerator<32>().generateRandom();
399  ticketCipher->setTicketSecrets({{range(ticketSeed)}});
400  serverContext->setTicketCipher(ticketCipher);
401 
402  auto certManager = std::make_unique<CertManager>();
403  if (!certPath.empty()) {
404  std::string certData;
405  std::string keyData;
406  if (!readFile(certPath.c_str(), certData)) {
407  LOG(ERROR) << "Failed to read certificate";
408  return 1;
409  } else if (!readFile(keyPath.c_str(), keyData)) {
410  LOG(ERROR) << "Failed to read private key";
411  return 1;
412  }
413  std::unique_ptr<SelfCert> cert;
414  if (!keyPass.empty()) {
415  cert = CertUtils::makeSelfCert(certData, keyData, keyPass);
416  } else {
417  cert = CertUtils::makeSelfCert(certData, keyData);
418  }
419  certManager->addCert(std::move(cert), true);
420  } else {
421  auto certData = fizz::test::createCert("fizz-self-signed", false, nullptr);
422  std::vector<folly::ssl::X509UniquePtr> certChain;
423  certChain.push_back(std::move(certData.cert));
424  auto cert = std::make_unique<SelfCertImpl<KeyType::P256>>(
425  std::move(certData.key), std::move(certChain));
426  certManager->addCert(std::move(cert), true);
427  }
428  serverContext->setCertManager(std::move(certManager));
429 
430  if (early) {
431  serverContext->setEarlyDataSettings(
432  true,
433  {std::chrono::seconds(-10), std::chrono::seconds(10)},
434  std::make_shared<SlidingBloomReplayCache>(240, 140000, 0.0005, &evb));
435  }
436 
437  std::shared_ptr<SSLContext> sslContext;
438  if (fallback) {
439  if (certPath.empty()) {
440  LOG(ERROR) << "Fallback mode requires explicit certificates";
441  return 1;
442  }
443  sslContext = std::make_shared<SSLContext>();
444  sslContext->loadCertKeyPairFromFiles(certPath.c_str(), keyPath.c_str());
445  SSL_CTX_set_ecdh_auto(sslContext->getSSLCtx(), 1);
446  }
447  serverContext->setVersionFallbackEnabled(fallback);
448 
449  if (!alpns.empty()) {
450  serverContext->setSupportedAlpns(std::move(alpns));
451  }
452 
453  serverContext->setSupportedVersions(
455  FizzServerAcceptor acceptor(port, serverContext, loop, &evb, sslContext);
456  evb.loop();
457  return 0;
458 }
459 
460 } // namespace tool
461 } // namespace fizz
std::unique_ptr< TerminalInputHandler > inputHandler_
folly::fbstring what() const
folly::StringPiece toString(StateEnum state)
Definition: State.cpp:16
int fizzServerCommand(const std::vector< std::string > &args)
bool readFile(int fd, Container &out, size_t num_bytes=std::numeric_limits< size_t >::max())
Definition: FileUtil.h:125
void write(const T &in, folly::io::Appender &appender)
Definition: Types-inl.h:112
static std::unique_ptr< SelfCert > makeSelfCert(std::string certData, std::string keyData, const std::vector< std::shared_ptr< CertificateCompressor >> &compressors={})
std::array< char, 8192 > readBuf_
std::shared_ptr< AsyncFizzServer > transport_
ByteRange coalesce()
Definition: IOBuf.h:1095
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
requires E e noexcept(noexcept(s.error(std::move(e))))
bool connected_
int parseArguments(std::vector< std::string > argv, FizzArgHandlerMap handlers, std::function< void()> usageFunc)
std::shared_ptr< FizzServerContext > ctx_
CipherSuite cipher
void setReadCB(ReadCallback *callback) override
Gen range(Value begin, Value end)
Definition: Base.h:467
void setReadCB(ReadCallback *callback) override
std::unique_ptr< AsyncSSLSocket, Destructor > UniquePtr
FizzServerAcceptor * acceptor_
std::unique_ptr< AsyncFizzServer::HandshakeCallback > cb_
AsyncSSLSocket::UniquePtr sslSocket_
void terminateLoopSoon()
Definition: EventBase.cpp:493
AsyncServerSocket::UniquePtr socket_
uint16_t portFromString(const std::string &portStr, bool serverSide)
std::unique_ptr< X509_STORE, X509StoreDeleter > X509StoreUniquePtr
Definition: Actions.h:16
NetworkSocket socket(int af, int type, int protocol)
Definition: NetOps.cpp:412
std::shared_ptr< const Cert > serverCert
bool loop_
std::unique_ptr< AsyncServerSocket, Destructor > UniquePtr
EventBase * evb_
void loop(int iters)
std::unique_ptr< AsyncFizzServerT, folly::DelayedDestruction::Destructor > UniquePtr
CertAndKey createCert(std::string cn, bool ca, CertAndKey *issuer)
Definition: Utilities.h:35
const char * string
Definition: Conv.cpp:212
std::shared_ptr< const Cert > clientCert
std::map< std::string, FizzCommandArgHandlerInfo > FizzArgHandlerMap
AsyncFizzServerT< ServerStateMachine > AsyncFizzServer
Range< const char * > StringPiece
int close(NetworkSocket s)
Definition: NetOps.cpp:90
std::unique_ptr< AsyncSocket, Destructor > UniquePtr
Definition: AsyncSocket.h:83
state
Definition: http_parser.c:272
std::shared_ptr< SSLContext > sslCtx_