wangle/ssl/SSLContextManager.h (116 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <folly/SharedMutex.h>
#include <folly/io/async/EventBase.h>
#include <folly/io/async/SSLContext.h>
#include <glog/logging.h>
#include <wangle/acceptor/SSLContextSelectionMisc.h>
#include <wangle/ssl/PasswordInFileFactory.h>
#include <wangle/ssl/SSLContextConfig.h>
#include <wangle/ssl/SSLSessionCacheManager.h>
#include <wangle/ssl/TLSTicketKeySeeds.h>
#include <list>
#include <memory>
#include <vector>
namespace folly {
class SocketAddress;
class SSLContext;
} // namespace folly
namespace wangle {
class ClientHelloExtStats;
struct SSLCacheOptions;
class SSLStats;
class TLSTicketKeyManager;
struct TLSTicketKeySeeds;
class ServerSSLContext;
// SSLContextManager represents all of the different server side
// SSL configurations behind a VIP.
//
// One VIP can serve multiple, independent TLS configurations by
// selecting a configuration by the ServerNameIndication extension
// on the initial ClientHello.
//
// There is 1 SSLContextManager per acceptor.
class SSLContextManager {
private:
// SslContexts is an internal, private structure used for
// storing all of the different TLS configurations behind
// an acceptor.
//
// 1 Acceptor : 1 SSLContextManager
// 1 SSLContextManager : N SSLContexts.
//
// Declaration is in SSLContextManager.cpp
class SslContexts;
public:
/**
* Provide ability to perform explicit client certificate
* verification
* If this callback is used, it becomes the responsibility
* of the callback to ensure that any verification is done (e.g. via
* SSL_CTX_set_verify(...). We remove the verification option
* on the SSLContext wrapper so that no other layer decides to set its own
* verification callback overriding the callbacks behavior. E.g.
* AsyncSSLSocket's verify callback wiping out any existing cb set.
*/
struct ClientCertVerifyCallback {
// no-op. Should be overridden if actual
// verification is required. This should assign the callback functions
// to the context, without altering the callback itself.
virtual void attachSSLContext(
const std::shared_ptr<folly::SSLContext>& sslCtx) const = 0;
virtual ~ClientCertVerifyCallback() {}
};
explicit SSLContextManager(
const std::string& vipName,
bool strict,
SSLStats* stats);
virtual ~SSLContextManager();
/**
* Add a new X509 to SSLContextManager. The details of a X509
* is passed as a SSLContextConfig object.
*
* @param ctxConfig Details of a X509, its private key, password, etc.
* @param cacheOptions Options for how to do session caching.
* @param ticketSeeds If non-null, the initial ticket key seeds to use.
* @param vipAddress Which VIP are the X509(s) used for? It is only for
* for user friendly log message
* @param externalCache Optional external provider for the session cache;
* may be null
*/
void addSSLContextConfig(
const SSLContextConfig& ctxConfig,
const SSLCacheOptions& cacheOptions,
const TLSTicketKeySeeds* ticketSeeds,
const folly::SocketAddress& vipAddress,
const std::shared_ptr<SSLCacheProvider>& externalCache);
/**
* Resets SSLContextManager with new X509s
*
* @param ctxConfigs Details of a X509s, private key, password, etc.
* @param cacheOptions Options for how to do session caching.
* @param ticketSeeds If non-null, and ticketHandler_ is a
* TLSTicketKeyManager, the initial ticket key seeds to
* use.
* @param vipAddress Which VIP are the X509(s) used for? It is only for
* for user friendly log message
* @param externalCache Optional external provider for the session cache;
* may be null
*/
void resetSSLContextConfigs(
const std::vector<SSLContextConfig>& ctxConfig,
const SSLCacheOptions& cacheOptions,
const TLSTicketKeySeeds* ticketSeeds,
const folly::SocketAddress& vipAddress,
const std::shared_ptr<SSLCacheProvider>& externalCache);
/**
* Remove SSLContextConfig of the given key. Note that to remove the context
* for wildcard domain, call either
* removeSSLContextConfigByDomainName("*.example.com") or
* removeSSLContextConfig(SSLContextKey(".example.com")).
*/
void removeSSLContextConfigByDomainName(const std::string& domainName);
void removeSSLContextConfig(const SSLContextKey& key);
/**
* Clears all ssl contexts
*/
void clear();
/**
* Get the default SSL_CTX for a VIP
*/
std::shared_ptr<folly::SSLContext> getDefaultSSLCtx() const;
/**
* Search first by exact domain, then by one level up
*/
std::shared_ptr<folly::SSLContext> getSSLCtx(const SSLContextKey& key) const;
/**
* Search by the _one_ level up subdomain
*/
std::shared_ptr<folly::SSLContext> getSSLCtxBySuffix(
const SSLContextKey& key) const;
/**
* Search by the full-string domain name
*/
std::shared_ptr<folly::SSLContext> getSSLCtxByExactDomain(
const SSLContextKey& key) const;
/**
* Updates ticket keys in ticket handler if using TLSTicketKeyManager,
* otherwise a no-op.
*/
void reloadTLSTicketKeys(
const std::vector<std::string>& oldSeeds,
const std::vector<std::string>& currentSeeds,
const std::vector<std::string>& newSeeds);
void setSSLStats(SSLStats* stats) {
stats_ = stats;
}
/**
* SSLContextManager only collects SNI stats now
*/
void setClientHelloExtStats(ClientHelloExtStats* stats);
/*
* Please read class header before setting this callback, it may have
* unintended conseqeunces, as you are now fully responsible for any
* verification.
*/
void setClientVerifyCallback(std::unique_ptr<ClientCertVerifyCallback> cb) {
clientCertVerifyCallback_ = std::move(cb);
}
void setPasswordFactory(std::shared_ptr<PasswordInFileFactory> factory) {
passwordFactory_ = std::move(factory);
}
protected:
// Return value indicates if any certificates were loaded. If not, the cert
// manager skips this context config. Allows for contexts using certs only
// supported by TLS 1.3/Fizz. Note: This is different than an error
// occurring. If an error occurs, it ought to throw in here. Returning false
// means the context is empty *due to a policy decision*.
virtual bool loadCertKeyPairsInSSLContext(
const std::shared_ptr<folly::SSLContext>&,
const SSLContextConfig&,
std::string& commonName) const;
virtual bool loadCertKeyPairsInSSLContextExternal(
const std::shared_ptr<folly::SSLContext>&,
const SSLContextConfig&,
std::string& /* commonName */) const {
LOG(FATAL) << "Unsupported in base SSLContextManager";
// unreachable
return false;
}
virtual void overrideConfiguration(
const std::shared_ptr<folly::SSLContext>&,
const SSLContextConfig&) const {}
std::string vipName_;
SSLStats* stats_{nullptr};
/**
* Insert a SSLContext by domain name.
*/
void insertSSLCtxByDomainName(
const std::string& dn,
std::shared_ptr<folly::SSLContext> sslCtx,
CertCrypto certCrypto = CertCrypto::BEST_AVAILABLE,
bool defaultFallback = false);
void loadCertsFromFiles(
const std::shared_ptr<folly::SSLContext>& sslCtx,
const SSLContextConfig::CertificateInfo& cert) const;
/**
* Helper used to verify that all certificates installed for a single
* `folly::SSLContext` convey the same identities (with the possibility of
* different SubjectPublicKeyInfos)
*/
void verifyCertNames(
const std::shared_ptr<folly::SSLContext>& sslCtx,
const std::string& description,
std::string& groupIdentity,
std::unique_ptr<std::list<std::string>>& subjectAltName,
const std::string& lastCertPath,
bool firstCert) const;
void addServerContext(std::shared_ptr<ServerSSLContext> sslCtx);
private:
SSLContextManager(const SSLContextManager&) = delete;
std::shared_ptr<SslContexts> contexts_;
ClientHelloExtStats* clientHelloTLSExtStats_{nullptr};
bool strict_{true};
std::unique_ptr<ClientCertVerifyCallback> clientCertVerifyCallback_{nullptr};
std::shared_ptr<ServerSSLContext> defaultCtx_;
std::shared_ptr<PasswordInFileFactory> passwordFactory_{nullptr};
};
} // namespace wangle