Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ssl: add support for SNI. #1984

Merged
merged 53 commits into from
Nov 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
76709a0
ssl: add support for SNI.
PiotrSikora Nov 1, 2017
d04afe2
Merge commit '908ddde3adb0634c966d1170319b8c1f571dab95' into piotrsik…
PiotrSikora Nov 7, 2017
0f984ce
review: skip context update in case of one filter chain.
PiotrSikora Nov 3, 2017
47e3df7
review: use ASSERT().
PiotrSikora Nov 3, 2017
227589b
review: fix comment.
PiotrSikora Nov 4, 2017
75abf26
review: use shared lock.
PiotrSikora Nov 7, 2017
0fecd6b
review: add tests.
PiotrSikora Nov 1, 2017
68541bf
review: add docs.
PiotrSikora Nov 7, 2017
33142cc
review: unordered_map find instead of direct access.
PiotrSikora Nov 7, 2017
ca9eb0b
reivew: fix format.
PiotrSikora Nov 7, 2017
7d1b1af
review: fix non-debug build.
PiotrSikora Nov 7, 2017
fc9705f
review: fix non-debug tests.
PiotrSikora Nov 7, 2017
a2f657c
review: fix typo.
PiotrSikora Nov 7, 2017
0dc9b85
review: fix stack-use-after-scope.
PiotrSikora Nov 7, 2017
348e6e8
review: describe algorithm in findSslServerContext().
PiotrSikora Nov 7, 2017
7ce1bbb
review: reject configurations with mixed use of Session Ticket Keys.
PiotrSikora Nov 7, 2017
1733d92
review: fix non-debug tests (again...).
PiotrSikora Nov 7, 2017
909ada7
review: proper use of unordered_map.
PiotrSikora Nov 7, 2017
1dbe7af
review: expand comment on why Session Ticket Keys can't be mixed.
PiotrSikora Nov 7, 2017
00bf76d
review: don't init std::string.
PiotrSikora Nov 9, 2017
5c6e262
review: skip context update early on.
PiotrSikora Nov 9, 2017
ce8ccc6
review: use ctx_.get()
PiotrSikora Nov 9, 2017
0a25690
review: erase(ctx).
PiotrSikora Nov 9, 2017
c29cd5b
review: improve skip_context_update.
PiotrSikora Nov 9, 2017
37e083d
review: add TODO about making it lockless.
PiotrSikora Nov 9, 2017
7f18d00
review: add comment about skip_context_update.
PiotrSikora Nov 9, 2017
80be681
review: rename updateConnection to updateConnectionContext.
PiotrSikora Nov 9, 2017
12e1260
review: add comment about "magic" 5.
PiotrSikora Nov 9, 2017
3f21609
review: fix lock in iterateContexts().
PiotrSikora Nov 9, 2017
c11713e
review: constify few functions.
PiotrSikora Nov 9, 2017
8dc1702
review: constify iterateContexts and use shared lock there.
PiotrSikora Nov 12, 2017
fa77d69
review: rename sslContext() to defaultSslContext().
PiotrSikora Nov 12, 2017
eb3ff75
review: remove invalid domain length restrictions.
PiotrSikora Nov 12, 2017
afc3b62
review: const bool skip_context_update.
PiotrSikora Nov 14, 2017
cd854a2
review: use uint32_t instead of size_t.
PiotrSikora Nov 14, 2017
aa5098b
review: add TODO to upstream SNI parsing to BoringSSL.
PiotrSikora Nov 14, 2017
da1dbaa
review: throw exception for configs with mixed use of STK.
PiotrSikora Nov 14, 2017
4ec1786
review: add ContextManagerImpl::isWildcardServerName().
PiotrSikora Nov 14, 2017
33d27e7
review: remove "x".
PiotrSikora Nov 15, 2017
757abeb
review: make sure all filter chains are equal.
PiotrSikora Nov 15, 2017
5a2fc2a
Merge remote-tracking branch 'origin/master' into piotrsikora/sni
PiotrSikora Nov 15, 2017
ea6660a
review: fixup bad merge.
PiotrSikora Nov 15, 2017
dd3cf76
review: update data-plane-api (for docs).
PiotrSikora Nov 16, 2017
f0a48d4
review: add TODO for refactoring with RouteMatcher::findVirtualHost().
PiotrSikora Nov 16, 2017
ddbaf41
review: add and use RepeatedPtrUtil::hash().
PiotrSikora Nov 16, 2017
6901dc0
reivew: fix format.
PiotrSikora Nov 16, 2017
f519595
review: fix typo.
PiotrSikora Nov 20, 2017
a43e74c
review: constify vars.
PiotrSikora Nov 20, 2017
5700d0c
review: move function prototype before vars.
PiotrSikora Nov 20, 2017
57ed156
review: use Optional<uint64_t>.
PiotrSikora Nov 20, 2017
5520293
review: add tests for listener manager.
PiotrSikora Nov 28, 2017
4ac9fdf
Merge remote-tracking branch 'origin/master' into piotrsikora/sni.
PiotrSikora Nov 28, 2017
122f450
review: remove factory variables.
PiotrSikora Nov 28, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions include/envoy/server/listener_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ class Listener {
virtual Network::ListenSocket& socket() PURE;

/**
* @return Ssl::ServerContext* the SSL context
* @return Ssl::ServerContext* the default SSL context.
*/
virtual Ssl::ServerContext* sslContext() PURE;
virtual Ssl::ServerContext* defaultSslContext() PURE;

/**
* @return bool whether to use the PROXY Protocol V1
Expand Down
6 changes: 3 additions & 3 deletions include/envoy/ssl/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@ class Context {
/**
* @return the number of days in this context until the next certificate will expire
*/
virtual size_t daysUntilFirstCertExpires() PURE;
virtual size_t daysUntilFirstCertExpires() const PURE;

/**
* @return a string of ca certificate path, certificate serial number and days until certificate
* expiration
*/
virtual std::string getCaCertInformation() PURE;
virtual std::string getCaCertInformation() const PURE;

/**
* @return a string of cert chain certificate path, certificate serial number and days until
* certificate expiration
*/
virtual std::string getCertChainInformation() PURE;
virtual std::string getCertChainInformation() const PURE;
};

class ClientContext : public virtual Context {};
Expand Down
19 changes: 15 additions & 4 deletions include/envoy/ssl/context_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,30 @@ class ContextManager {

/**
* Builds a ServerContext from a ServerContextConfig.
* The skip_context_update parameter is used for fast-path (avoiding lock & context lookup)
* on listeners with a single filter chain and no SNI restrictions.
*/
virtual ServerContextPtr createSslServerContext(Stats::Scope& scope,
ServerContextConfig& config) PURE;
virtual ServerContextPtr createSslServerContext(const std::string& listener_name,
const std::vector<std::string>& server_names,
Stats::Scope& scope, ServerContextConfig& config,
bool skip_context_update) PURE;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document the meaning/effect of skip_context_update

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


/**
* Find ServerContext for a given listener and server_name.
* @return ServerContext or nullptr in case there is no match.
*/
virtual ServerContext* findSslServerContext(const std::string& listener_name,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be out of scope for this change, but should the ServerContextImpl has a reference to it's ConnectionHandler, then this function could be on ConnectionHandler and not need a listener_name param? This feels awkward because the ServerContext already exists under a listener/ConnectionHandler, so needing a separate listener lookup is strange.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, agreed. This is related to my comments about making this lockless and adding TODO. I'm willing live with this to get this shipped, but ultimately don't like the global namespace for this. Really we want listener -> set of contexts -> global context manager. Will let the two of you sort it out.

Copy link
Contributor Author

@PiotrSikora PiotrSikora Nov 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes and no. While this indeed could be done much better for this specific use case, we're going to add SDS shortly and then it's going to get a bit more complex.

Personally, I'd defer any optimizations and/or refactoring until SDS is committed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ok with that.

const std::string& server_name) const PURE;

/**
* @return the number of days until the next certificate being managed will expire.
*/
virtual size_t daysUntilFirstCertExpires() PURE;
virtual size_t daysUntilFirstCertExpires() const PURE;

/**
* Iterate through all currently allocated contexts.
*/
virtual void iterateContexts(std::function<void(Context&)> callback) PURE;
virtual void iterateContexts(std::function<void(const Context&)> callback) PURE;
};

} // namespace Ssl
Expand Down
19 changes: 19 additions & 0 deletions source/common/protobuf/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,25 @@ class RepeatedPtrUtil {
}) +
"]";
}

// Based on MessageUtil::hash() defined below.
template <class ProtoType>
static std::size_t hash(const google::protobuf::RepeatedPtrField<ProtoType>& source) {
// Use Protobuf::io::CodedOutputStream to force deterministic serialization, so that the same
// message doesn't hash to different values.
ProtobufTypes::String text;
{
// For memory safety, the StringOutputStream needs to be destroyed before
// we read the string.
Protobuf::io::StringOutputStream string_stream(&text);
Protobuf::io::CodedOutputStream coded_stream(&string_stream);
coded_stream.SetSerializationDeterministic(true);
for (const auto& message : source) {
message.SerializeToCodedStream(&coded_stream);
}
}
return HashUtil::xxHash64(text);
}
};

class MessageUtil {
Expand Down
1 change: 1 addition & 0 deletions source/common/ssl/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ envoy_cc_library(
"//include/envoy/stats:stats_interface",
"//include/envoy/stats:stats_macros",
"//source/common/common:assert_lib",
"//source/common/common:empty_string",
"//source/common/common:hex_lib",
],
)
104 changes: 90 additions & 14 deletions source/common/ssl/context_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ int ContextImpl::sslContextIndex() {
}

ContextImpl::ContextImpl(ContextManagerImpl& parent, Stats::Scope& scope, ContextConfig& config)
: parent_(parent), ctx_(SSL_CTX_new(TLS_method())), scope_(scope),
stats_(generateStats(scope)) {
: parent_(parent), ctx_(SSL_CTX_new(TLS_method())), scope_(scope), stats_(generateStats(scope)),
ecdh_curves_(config.ecdhCurves()) {
RELEASE_ASSERT(ctx_);

int rc = SSL_CTX_set_ex_data(ctx_.get(), sslContextIndex(), this);
Expand All @@ -40,8 +40,8 @@ ContextImpl::ContextImpl(ContextManagerImpl& parent, Stats::Scope& scope, Contex
fmt::format("Failed to initialize cipher suites {}", config.cipherSuites()));
}

if (!SSL_CTX_set1_curves_list(ctx_.get(), config.ecdhCurves().c_str())) {
throw EnvoyException(fmt::format("Failed to initialize ECDH curves {}", config.ecdhCurves()));
if (!SSL_CTX_set1_curves_list(ctx_.get(), ecdh_curves_.c_str())) {
throw EnvoyException(fmt::format("Failed to initialize ECDH curves {}", ecdh_curves_));
}

int verify_mode = SSL_VERIFY_NONE;
Expand Down Expand Up @@ -266,7 +266,7 @@ SslStats ContextImpl::generateStats(Stats::Scope& store) {
POOL_HISTOGRAM_PREFIX(store, prefix))};
}

size_t ContextImpl::daysUntilFirstCertExpires() {
size_t ContextImpl::daysUntilFirstCertExpires() const {
int daysUntilExpiration = getDaysUntilExpiration(ca_cert_.get());
daysUntilExpiration =
std::min<int>(getDaysUntilExpiration(cert_chain_.get()), daysUntilExpiration);
Expand All @@ -276,7 +276,7 @@ size_t ContextImpl::daysUntilFirstCertExpires() {
return daysUntilExpiration;
}

int32_t ContextImpl::getDaysUntilExpiration(const X509* cert) {
int32_t ContextImpl::getDaysUntilExpiration(const X509* cert) const {
if (cert == nullptr) {
return std::numeric_limits<int>::max();
}
Expand All @@ -287,7 +287,7 @@ int32_t ContextImpl::getDaysUntilExpiration(const X509* cert) {
return 0;
}

std::string ContextImpl::getCaCertInformation() {
std::string ContextImpl::getCaCertInformation() const {
if (ca_cert_ == nullptr) {
return "";
}
Expand All @@ -296,7 +296,7 @@ std::string ContextImpl::getCaCertInformation() {
getDaysUntilExpiration(ca_cert_.get()));
}

std::string ContextImpl::getCertChainInformation() {
std::string ContextImpl::getCertChainInformation() const {
if (cert_chain_ == nullptr) {
return "";
}
Expand All @@ -305,9 +305,9 @@ std::string ContextImpl::getCertChainInformation() {
getDaysUntilExpiration(cert_chain_.get()));
}

std::string ContextImpl::getSerialNumber(X509* cert) {
std::string ContextImpl::getSerialNumber(const X509* cert) {
ASSERT(cert);
ASN1_INTEGER* serial_number = X509_get_serialNumber(cert);
ASN1_INTEGER* serial_number = X509_get_serialNumber(const_cast<X509*>(cert));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

openssl sigh :)

BIGNUM num_bn;
BN_init(&num_bn);
ASN1_INTEGER_to_BN(serial_number, &num_bn);
Expand Down Expand Up @@ -355,10 +355,20 @@ bssl::UniquePtr<SSL> ClientContextImpl::newSsl() const {
return ssl_con;
}

ServerContextImpl::ServerContextImpl(ContextManagerImpl& parent, Stats::Scope& scope,
ServerContextConfig& config, Runtime::Loader& runtime)
: ContextImpl(parent, scope, config), runtime_(runtime),
ServerContextImpl::ServerContextImpl(ContextManagerImpl& parent, const std::string& listener_name,
const std::vector<std::string>& server_names,
Stats::Scope& scope, ServerContextConfig& config,
bool skip_context_update, Runtime::Loader& runtime)
: ContextImpl(parent, scope, config), listener_name_(listener_name),
server_names_(server_names), skip_context_update_(skip_context_update), runtime_(runtime),
session_ticket_keys_(config.sessionTicketKeys()) {
SSL_CTX_set_select_certificate_cb(
ctx_.get(), [](const SSL_CLIENT_HELLO* client_hello) -> ssl_select_cert_result_t {
ContextImpl* context_impl = static_cast<ContextImpl*>(
SSL_CTX_get_ex_data(SSL_get_SSL_CTX(client_hello->ssl), sslContextIndex()));
return dynamic_cast<ServerContextImpl*>(context_impl)->processClientHello(client_hello);
});

if (!config.caCertFile().empty()) {
bssl::UniquePtr<STACK_OF(X509_NAME)> list(SSL_load_client_CA_file(config.caCertFile().c_str()));
if (nullptr == list) {
Expand Down Expand Up @@ -404,7 +414,7 @@ ServerContextImpl::ServerContextImpl(ContextManagerImpl& parent, Stats::Scope& s
int rc = EVP_DigestInit(&md, EVP_sha256());
RELEASE_ASSERT(rc == 1);

// Hash the CommonName/SANs in the server certificate. This makes sure that
// Hash the CommonName/SANs of the server certificate. This makes sure that
// sessions can only be resumed to a certificate for the same name, but allows
// resuming to unique certs in the case that different Envoy instances each have
// their own certs.
Expand Down Expand Up @@ -469,12 +479,78 @@ ServerContextImpl::ServerContextImpl(ContextManagerImpl& parent, Stats::Scope& s
RELEASE_ASSERT(rc == 1);
}

// Hash configured SNIs for this context, so that sessions cannot be resumed across different
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't totally follow this. Should this be "so that sessions cannot be resumed across listeners with different configurations?" What is the downside of allowing resumption if the SNI cert matches?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, except s/listeners/filter chains/.

Imagine certificate for *.example.com that's used on different filter chains (e.g. www.example.com and admin.example.com). There is no reason sessions on those logically separate "virtual hosts" should be resumable simply because they use the same wildcard certificate.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think maybe we are using different lingo or I'm just confused. I think by far the most common use of SNI is going to be a single filter chain with multiple certificates, where host/authority header allows the virtual host in the route table to be selected. Very few people would regularly configure a full HTTP connection manager per SNI host I think. AFAICT the below check basically says that we don't allow resumption unless the filter chain has all the same server names that it had before. This seems wrong to me?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, but that's not what the data-plane-api allows you to do, really.

Per current (and frozen) specification, sni_domains are part of the FilterChainMatch, which is used to select FilterChain. Each FilterChain contains a single DownstreamTlsContext, therefore multiple DownstreamTlsContext require multiple filter chains, and there is no other SNI to TLS certificate mapping.

cc @htuch

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I see. I think I totally missed that part of how we did the protos. I'm concerned users will find that burdensome since it will potentially require a different route/RDS table for each sever name. I now understand why you are calling this a hack w/o full filter chain matching support.

My concern here is that once this code goes out we are actually providing the behavior that I originally thought people would want (multiple server names -> 1 connection manager). Can we really take it away once we give it to them? At minimum we would have to put it through the full deprecation cycle.

This is a tough one. I'm wondering if it's worth it for me to help out and try to get the multiple filter chains working?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PiotrSikora I talked offline with @htuch and he had the idea that to help with back-compat, we should check that all the filter chains are identical and throw if they aren't. Then in the future we can relax this. I think that will make me OK with this.

I also now think I understand why the multiple filter chain stuff is complicated. You basically have to complete the handshake before you do any filter chain processing/selection. I can have a look at all of this offline and come back with some implementation notes for the future if that would be useful.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, I was planning to use multiple filter chains heavily. That decision isn't set in stone, but it's I was planning to model my configuration.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ggreenway we are going to implement multiple filter chains, it's just a matter of when. It's pretty urgent that we get base SNI support merged so I think this approach is fine to start with as long as we future proof it. Then I will likely look at the multiple filter chain aspect after this merges.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sgtm

// filter chains, even when using the same server certificate.
for (const auto& name : server_names_) {
rc = EVP_DigestUpdate(&md, name.data(), name.size());
RELEASE_ASSERT(rc == 1);
}

rc = EVP_DigestFinal(&md, session_context_buf, &session_context_len);
RELEASE_ASSERT(rc == 1);
rc = SSL_CTX_set_session_id_context(ctx_.get(), session_context_buf, session_context_len);
RELEASE_ASSERT(rc == 1);
}

ssl_select_cert_result_t
ServerContextImpl::processClientHello(const SSL_CLIENT_HELLO* client_hello) {
if (skip_context_update_) {
return ssl_select_cert_success;
}

std::string server_name;
const uint8_t* data;
size_t len;

if (SSL_early_callback_ctx_extension_get(client_hello, TLSEXT_TYPE_server_name, &data, &len)) {
// Based on BoringSSL's ext_sni_parse_clienthello().
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason we can't use the boring ssl function here? Is it worth putting in a TODO here to replace with a boringssl helper? cc @davidben

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few reasons:

  1. ext_sni_parse_clienthello() isn't exported and early callback (which we need to be able to set different protocol versions across different filter chains) is called so early that SNI isn't parsed yet and it's not available via SSL_set_tlsext_host_name().
  2. I'm going to need a few more changes for ClientHello sniffing (for SNI-based routing without TLS termination #1843), so I'm going to try to upstream everything that's needed once we have a complete picture.
  3. We're using chromium-stable branch of BoringSSL, so anything we add to BoringSSL wouldn't be available to use for the next month or two anyway.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK SGTM. Maybe just add a TODO to circle back and upstream.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// Match on empty SNI instead of rejecting connection in case we cannot process the extension.
// TODO(PiotrSikora): figure out if we can upstream this to BoringSSL.
CBS extension;
CBS_init(&extension, data, len);
CBS server_name_list, host_name;
uint8_t name_type;
if (CBS_get_u16_length_prefixed(&extension, &server_name_list) &&
CBS_get_u8(&server_name_list, &name_type) &&
CBS_get_u16_length_prefixed(&server_name_list, &host_name) &&
CBS_len(&server_name_list) == 0 && CBS_len(&extension) == 0 &&
name_type == TLSEXT_NAMETYPE_host_name && CBS_len(&host_name) != 0 &&
CBS_len(&host_name) <= TLSEXT_MAXLEN_host_name && !CBS_contains_zero_byte(&host_name)) {
server_name.assign(reinterpret_cast<const char*>(CBS_data(&host_name)), CBS_len(&host_name));
}
}

ServerContext* new_ctx = parent_.findSslServerContext(listener_name_, server_name);

// Reject connection if we didn't find a match.
if (new_ctx == nullptr) {
stats_.fail_no_sni_match_.inc();
return ssl_select_cert_error;
}

// Update context if it changed.
if (new_ctx != this) {
ServerContextImpl* new_impl = dynamic_cast<ServerContextImpl*>(new_ctx);
new_impl->updateConnectionContext(client_hello->ssl);
}

return ssl_select_cert_success;
}

void ServerContextImpl::updateConnectionContext(SSL* ssl) {
ASSERT(ctx_);

SSL_set_SSL_CTX(ssl, ctx_.get());
ASSERT(SSL_CTX_get_ex_data(ctx_.get(), sslContextIndex()) == this);

// Update SSL-level settings and parameters that are inherited from SSL_CTX during SSL_new().
SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ctx_.get()), SSL_CTX_get_verify_callback(ctx_.get()));

int rc = SSL_set1_curves_list(ssl, ecdh_curves_.c_str());
ASSERT(rc == 1);
UNREFERENCED_PARAMETER(rc);
}

int ServerContextImpl::sessionTicketProcess(SSL*, uint8_t* key_name, uint8_t* iv,
EVP_CIPHER_CTX* ctx, HMAC_CTX* hmac_ctx, int encrypt) {
const EVP_MD* hmac = EVP_sha256();
Expand Down
34 changes: 22 additions & 12 deletions source/common/ssl/context_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ namespace Ssl {
COUNTER(handshake) \
COUNTER(session_reused) \
COUNTER(no_certificate) \
COUNTER(fail_no_sni_match) \
COUNTER(fail_verify_no_cert) \
COUNTER(fail_verify_error) \
COUNTER(fail_verify_san) \
Expand All @@ -42,8 +43,6 @@ struct SslStats {

class ContextImpl : public virtual Context {
public:
~ContextImpl() { parent_.releaseContext(this); }

virtual bssl::UniquePtr<SSL> newSsl() const;

/**
Expand Down Expand Up @@ -72,9 +71,9 @@ class ContextImpl : public virtual Context {
SslStats& stats() { return stats_; }

// Ssl::Context
size_t daysUntilFirstCertExpires() override;
std::string getCaCertInformation() override;
std::string getCertChainInformation() override;
size_t daysUntilFirstCertExpires() const override;
std::string getCaCertInformation() const override;
std::string getCertChainInformation() const override;

protected:
ContextImpl(ContextManagerImpl& parent, Stats::Scope& scope, ContextConfig& config);
Expand Down Expand Up @@ -102,11 +101,11 @@ class ContextImpl : public virtual Context {

std::vector<uint8_t> parseAlpnProtocols(const std::string& alpn_protocols);
static SslStats generateStats(Stats::Scope& scope);
int32_t getDaysUntilExpiration(const X509* cert);
int32_t getDaysUntilExpiration(const X509* cert) const;
bssl::UniquePtr<X509> loadCert(const std::string& cert_file);
static std::string getSerialNumber(X509* cert);
std::string getCaFileName() { return ca_file_path_; };
std::string getCertChainFileName() { return cert_chain_file_path_; };
static std::string getSerialNumber(const X509* cert);
std::string getCaFileName() const { return ca_file_path_; };
std::string getCertChainFileName() const { return cert_chain_file_path_; };

ContextManagerImpl& parent_;
bssl::UniquePtr<SSL_CTX> ctx_;
Expand All @@ -119,11 +118,13 @@ class ContextImpl : public virtual Context {
bssl::UniquePtr<X509> cert_chain_;
std::string ca_file_path_;
std::string cert_chain_file_path_;
const std::string ecdh_curves_;
};

class ClientContextImpl : public ContextImpl, public ClientContext {
public:
ClientContextImpl(ContextManagerImpl& parent, Stats::Scope& scope, ClientContextConfig& config);
~ClientContextImpl() { parent_.releaseClientContext(this); }

bssl::UniquePtr<SSL> newSsl() const override;

Expand All @@ -133,19 +134,28 @@ class ClientContextImpl : public ContextImpl, public ClientContext {

class ServerContextImpl : public ContextImpl, public ServerContext {
public:
ServerContextImpl(ContextManagerImpl& parent, Stats::Scope& scope, ServerContextConfig& config,
ServerContextImpl(ContextManagerImpl& parent, const std::string& listener_name,
const std::vector<std::string>& server_names, Stats::Scope& scope,
ServerContextConfig& config, bool skip_context_update,
Runtime::Loader& runtime);
~ServerContextImpl() { parent_.releaseServerContext(this, listener_name_, server_names_); }

private:
ssl_select_cert_result_t processClientHello(const SSL_CLIENT_HELLO* client_hello);
void updateConnectionContext(SSL* ssl);

int alpnSelectCallback(const unsigned char** out, unsigned char* outlen, const unsigned char* in,
unsigned int inlen);
int sessionTicketProcess(SSL* ssl, uint8_t* key_name, uint8_t* iv, EVP_CIPHER_CTX* ctx,
HMAC_CTX* hmac_ctx, int encrypt);

const std::string listener_name_;
const std::vector<std::string> server_names_;
const bool skip_context_update_;
Runtime::Loader& runtime_;
std::vector<uint8_t> parsed_alt_alpn_protocols_;
const std::vector<ServerContextConfig::SessionTicketKey> session_ticket_keys_;
};

} // Ssl
} // Envoy
} // namespace Ssl
} // namespace Envoy
Loading