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

Generalize SNI support #6870

Merged
merged 2 commits into from
Jan 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions iocore/net/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ libinknet_a_SOURCES = \
SSLUtils.cc \
OCSPStapling.cc \
TLSSessionResumptionSupport.cc \
TLSSNISupport.cc \
UDPIOEvent.cc \
UnixConnection.cc \
UnixNet.cc \
Expand Down
31 changes: 17 additions & 14 deletions iocore/net/P_SNIActionPerformer.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class ActionItem
std::optional<std::vector<std::string>> _fqdn_wildcard_captured_groups;
};

virtual int SNIAction(Continuation *cont, const Context &ctx) const = 0;
virtual int SNIAction(TLSSNISupport *snis, const Context &ctx) const = 0;

/**
This method tests whether this action would have been triggered by a
Expand All @@ -72,9 +72,9 @@ class ControlH2 : public ActionItem
~ControlH2() override {}

int
SNIAction(Continuation *cont, const Context &ctx) const override
SNIAction(TLSSNISupport *snis, const Context &ctx) const override
{
auto ssl_vc = dynamic_cast<SSLNetVConnection *>(cont);
auto ssl_vc = dynamic_cast<SSLNetVConnection *>(snis);
if (ssl_vc) {
if (!enable_h2) {
ssl_vc->disableProtocol(TS_ALPN_PROTOCOL_INDEX_HTTP_2_0);
Expand All @@ -100,10 +100,10 @@ class TunnelDestination : public ActionItem
~TunnelDestination() override {}

int
SNIAction(Continuation *cont, const Context &ctx) const override
SNIAction(TLSSNISupport *snis, const Context &ctx) const override
{
// Set the netvc option?
SSLNetVConnection *ssl_netvc = dynamic_cast<SSLNetVConnection *>(cont);
SSLNetVConnection *ssl_netvc = dynamic_cast<SSLNetVConnection *>(snis);
if (ssl_netvc) {
// If needed, we will try to amend the tunnel destination.
if (ctx._fqdn_wildcard_captured_groups && need_fix) {
Expand All @@ -113,6 +113,9 @@ class TunnelDestination : public ActionItem
} else {
ssl_netvc->set_tunnel_destination(destination, tunnel_decrypt, tls_upstream);
}
if (ssl_netvc->has_tunnel_destination() && !ssl_netvc->decrypt_tunnel()) {
ssl_netvc->attributes = HttpProxyPort::TRANSPORT_BLIND_TUNNEL;
}
}
return SSL_TLSEXT_ERR_OK;
}
Expand Down Expand Up @@ -201,9 +204,9 @@ class VerifyClient : public ActionItem
VerifyClient(const char *param, std::string_view file, std::string_view dir) : VerifyClient(atoi(param), file, dir) {}
~VerifyClient() override;
int
SNIAction(Continuation *cont, const Context &ctx) const override
SNIAction(TLSSNISupport *snis, const Context &ctx) const override
{
auto ssl_vc = dynamic_cast<SSLNetVConnection *>(cont);
auto ssl_vc = dynamic_cast<SSLNetVConnection *>(snis);
Debug("ssl_sni", "action verify param %d", this->mode);
setClientCertLevel(ssl_vc->ssl, this->mode);
ssl_vc->set_ca_cert_file(ca_file, ca_dir);
Expand Down Expand Up @@ -232,7 +235,7 @@ class HostSniPolicy : public ActionItem
HostSniPolicy(uint8_t param) : policy(param) {}
~HostSniPolicy() override {}
int
SNIAction(Continuation *cont, const Context &ctx) const override
SNIAction(TLSSNISupport *snis, const Context &ctx) const override
{
// On action this doesn't do anything
return SSL_TLSEXT_ERR_OK;
Expand Down Expand Up @@ -261,14 +264,14 @@ class TLSValidProtocols : public ActionItem
TLSValidProtocols() : protocol_mask(max_mask) {}
TLSValidProtocols(unsigned long protocols) : unset(false), protocol_mask(protocols) {}
int
SNIAction(Continuation *cont, const Context & /* ctx */) const override
SNIAction(TLSSNISupport *snis, const Context & /* ctx */) const override
{
if (!unset) {
auto ssl_vc = dynamic_cast<SSLNetVConnection *>(cont);
auto ssl_vc = dynamic_cast<SSLNetVConnection *>(snis);
Copy link
Member

Choose a reason for hiding this comment

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

Looking through this, I realize there is no value to doing the dynamic_cast here as opposed to a static_cast since we don't bother to check the return value. That is an issue that predates this PR, so I'll put up a separate PR for that.

Debug("ssl_sni", "TLSValidProtocol param 0%x", static_cast<unsigned int>(this->protocol_mask));
ssl_vc->protocol_mask_set = true;
ssl_vc->protocol_mask = protocol_mask;
ssl_vc->set_valid_tls_protocols(protocol_mask, TLSValidProtocols::max_mask);
}

return SSL_TLSEXT_ERR_OK;
}
};
Expand Down Expand Up @@ -301,14 +304,14 @@ class SNI_IpAllow : public ActionItem
} // end function SNI_IpAllow

int
SNIAction(Continuation *cont, const Context &ctx) const override
SNIAction(TLSSNISupport *snis, const Context &ctx) const override
{
// i.e, ip filtering is not required
if (ip_map.count() == 0) {
return SSL_TLSEXT_ERR_OK;
}

auto ssl_vc = dynamic_cast<SSLNetVConnection *>(cont);
auto ssl_vc = dynamic_cast<SSLNetVConnection *>(snis);
auto ip = ssl_vc->get_remote_endpoint();

// check the allowed ips
Expand Down
17 changes: 12 additions & 5 deletions iocore/net/P_SSLNetVConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include "P_UnixNet.h"
#include "P_ALPNSupport.h"
#include "TLSSessionResumptionSupport.h"
#include "TLSSNISupport.h"
#include "P_SSLUtils.h"
#include "P_SSLConfig.h"

Expand Down Expand Up @@ -93,7 +94,7 @@ enum SSLHandshakeStatus { SSL_HANDSHAKE_ONGOING, SSL_HANDSHAKE_DONE, SSL_HANDSHA
// A VConnection for a network socket.
//
//////////////////////////////////////////////////////////////////
class SSLNetVConnection : public UnixNetVConnection, public ALPNSupport, public TLSSessionResumptionSupport
class SSLNetVConnection : public UnixNetVConnection, public ALPNSupport, public TLSSessionResumptionSupport, public TLSSNISupport
{
typedef UnixNetVConnection super; ///< Parent type.

Expand Down Expand Up @@ -384,11 +385,9 @@ class SSLNetVConnection : public UnixNetVConnection, public ALPNSupport, public
const char *
get_server_name() const override
{
return _serverName.get() ? _serverName.get() : "";
return _get_sni_server_name() ? _get_sni_server_name() : "";
}

void set_server_name(std::string_view name);

bool
support_sni() const override
{
Expand Down Expand Up @@ -473,13 +472,22 @@ class SSLNetVConnection : public UnixNetVConnection, public ALPNSupport, public
return _ca_cert_dir.get();
}

void
set_valid_tls_protocols(unsigned long proto_mask, unsigned long max_mask)
{
SSL_set_options(this->ssl, proto_mask);
SSL_clear_options(this->ssl, max_mask & ~proto_mask);
}

protected:
const IpEndpoint &
_getLocalEndpoint() override
{
return local_addr;
}

void _fire_ssl_servername_event() override;

private:
std::string_view map_tls_protocol_to_tag(const char *proto_string) const;
bool update_rbio(bool move_to_socket);
Expand Down Expand Up @@ -526,7 +534,6 @@ class SSLNetVConnection : public UnixNetVConnection, public ALPNSupport, public
X509_STORE_CTX *verify_cert = nullptr;

// Null-terminated string, or nullptr if there is no SNI server name.
std::unique_ptr<char[]> _serverName;
std::unique_ptr<char[]> _ca_cert_file;
std::unique_ptr<char[]> _ca_cert_dir;

Expand Down
13 changes: 5 additions & 8 deletions iocore/net/SSLNetVConnection.cc
Original file line number Diff line number Diff line change
Expand Up @@ -215,13 +215,15 @@ SSLNetVConnection::_bindSSLObject()
{
SSLNetVCAttach(this->ssl, this);
TLSSessionResumptionSupport::bind(this->ssl, this);
TLSSNISupport::bind(this->ssl, this);
}

void
SSLNetVConnection::_unbindSSLObject()
{
SSLNetVCDetach(this->ssl);
TLSSessionResumptionSupport::unbind(this->ssl);
TLSSNISupport::unbind(this->ssl);
}

static void
Expand Down Expand Up @@ -928,7 +930,6 @@ SSLNetVConnection::do_io_close(int lerrno)
void
SSLNetVConnection::clear()
{
_serverName.reset();
_ca_cert_file.reset();
_ca_cert_dir.reset();

Expand All @@ -938,6 +939,7 @@ SSLNetVConnection::clear()
}
ALPNSupport::clear();
TLSSessionResumptionSupport::clear();
TLSSNISupport::_clear();

sslHandshakeStatus = SSL_HANDSHAKE_ONGOING;
sslHandshakeBeginTime = 0;
Expand Down Expand Up @@ -1918,14 +1920,9 @@ SSLNetVConnection::protocol_contains(std::string_view prefix) const
}

void
SSLNetVConnection::set_server_name(std::string_view name)
SSLNetVConnection::_fire_ssl_servername_event()
{
if (name.size()) {
char *n = new char[name.size() + 1];
std::memcpy(n, name.data(), name.size());
n[name.size()] = '\0';
_serverName.reset(n);
}
this->callHooks(TS_EVENT_SSL_SERVERNAME);
}

void
Expand Down
121 changes: 32 additions & 89 deletions iocore/net/SSLUtils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -347,76 +347,31 @@ ssl_verify_client_callback(int preverify_ok, X509_STORE_CTX *ctx)
return preverify_ok;
}

static int
PerformAction(Continuation *cont, const char *servername)
{
SNIConfig::scoped_config params;
if (const auto &actions = params->get(servername); !actions.first) {
Debug("ssl_sni", "%s not available in the map", servername);
} else {
for (auto &&item : *actions.first) {
auto ret = item->SNIAction(cont, actions.second);
if (ret != SSL_TLSEXT_ERR_OK) {
return ret;
}
}
}
return SSL_TLSEXT_ERR_OK;
}

#if TS_USE_HELLO_CB
// Pausable callback
static int
ssl_client_hello_callback(SSL *s, int *al, void *arg)
{
SSLNetVConnection *netvc = SSLNetVCAccess(s);
const char *servername = nullptr;
const unsigned char *p;
size_t remaining, len;

if (!netvc || netvc->ssl != s) {
Debug("ssl.error", "ssl_client_hello_callback call back on stale netvc");
TLSSNISupport *snis = TLSSNISupport::getInstance(s);
if (snis) {
snis->on_client_hello(s, al, arg);
int ret = snis->perform_sni_action();
if (ret != SSL_TLSEXT_ERR_OK) {
return SSL_CLIENT_HELLO_ERROR;
}
} else {
// This error suggests either of these:
// 1) Call back on unsupported netvc -- Don't register callback unnecessarily
// 2) Call back on stale netvc
Debug("ssl.error", "ssl_client_hello_callback was called unexpectedly");
return SSL_CLIENT_HELLO_ERROR;
}

// Parse the server name if the get extension call succeeds and there are more than 2 bytes to parse
if (SSL_client_hello_get0_ext(s, TLSEXT_TYPE_server_name, &p, &remaining) && remaining > 2) {
// Parse to get to the name, originally from test/handshake_helper.c in openssl tree
/* Extract the length of the supplied list of names. */
len = *(p++) << 8;
len += *(p++);
if (len + 2 == remaining) {
remaining = len;
/*
* The list in practice only has a single element, so we only consider
* the first one.
*/
if (*p++ == TLSEXT_NAMETYPE_host_name) {
remaining--;
/* Now we can finally pull out the byte array with the actual hostname. */
if (remaining > 2) {
len = *(p++) << 8;
len += *(p++);
if (len + 2 <= remaining) {
servername = reinterpret_cast<const char *>(p);
}
}
}
}
}
if (servername) {
netvc->set_server_name(std::string_view(servername, len));
}
int ret = PerformAction(netvc, netvc->get_server_name());
if (ret != SSL_TLSEXT_ERR_OK) {
SSLNetVConnection *netvc = SSLNetVCAccess(s);
if (!netvc || netvc->ssl != s) {
Debug("ssl.error", "ssl_client_hello_callback call back on stale netvc");
return SSL_CLIENT_HELLO_ERROR;
}
if (netvc->has_tunnel_destination() && !netvc->decrypt_tunnel()) {
netvc->attributes = HttpProxyPort::TRANSPORT_BLIND_TUNNEL;
}
if (netvc->protocol_mask_set) {
setTLSValidProtocols(s, netvc->protocol_mask, TLSValidProtocols::max_mask);
}

bool reenabled = netvc->callHooks(TS_EVENT_SSL_CLIENT_HELLO);

Expand Down Expand Up @@ -473,32 +428,26 @@ ssl_cert_callback(SSL *ssl, void * /*arg*/)
* Cannot stop this callback. Always reeneabled
*/
static int
ssl_servername_callback(SSL *ssl, int * /* ad */, void * /*arg*/)
ssl_servername_callback(SSL *ssl, int *al, void *arg)
{
SSLNetVConnection *netvc = SSLNetVCAccess(ssl);

if (!netvc || netvc->ssl != ssl) {
Debug("ssl.error", "ssl_servername_callback call back on stale netvc");
return SSL_TLSEXT_ERR_ALERT_FATAL;
}

netvc->callHooks(TS_EVENT_SSL_SERVERNAME);

const char *name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (name) {
netvc->set_server_name(name);
}

TLSSNISupport *snis = TLSSNISupport::getInstance(ssl);
if (snis) {
snis->on_servername(ssl, al, arg);
#if !TS_USE_HELLO_CB
// Only call the SNI actions here if not already performed in the HELLO_CB
int ret = PerformAction(netvc, netvc->get_server_name());
if (ret != SSL_TLSEXT_ERR_OK) {
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
// Only call the SNI actions here if not already performed in the HELLO_CB
int ret = snis->perform_sni_action();
if (ret != SSL_TLSEXT_ERR_OK) {
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
#endif
if (netvc->has_tunnel_destination() && !netvc->decrypt_tunnel()) {
netvc->attributes = HttpProxyPort::TRANSPORT_BLIND_TUNNEL;
} else {
// This error suggests either of these:
// 1) Call back on unsupported netvc -- Don't register callback unnecessarily
// 2) Call back on stale netvc
Debug("ssl.error", "ssl_servername_callback was called unexpectedly");
return SSL_TLSEXT_ERR_ALERT_FATAL;
}

return SSL_TLSEXT_ERR_OK;
}

Expand Down Expand Up @@ -912,6 +861,7 @@ SSLInitializeLibrary()
ssl_vc_index = SSL_get_ex_new_index(0, (void *)"NetVC index", nullptr, nullptr, nullptr);

TLSSessionResumptionSupport::initialize();
TLSSNISupport::initialize();

open_ssl_initialized = true;
}
Expand Down Expand Up @@ -1124,13 +1074,6 @@ SSLMultiCertConfigLoader::_set_handshake_callbacks(SSL_CTX *ctx)
#endif
}

void
setTLSValidProtocols(SSL *ssl, unsigned long proto_mask, unsigned long max_mask)
{
SSL_set_options(ssl, proto_mask);
SSL_clear_options(ssl, max_mask & ~proto_mask);
}

void
setClientCertLevel(SSL *ssl, uint8_t certLevel)
{
Expand Down
Loading