Skip to content

Commit

Permalink
CVE-2015-4663: Support verifying certificates on ssl/tls streams, ena…
Browse files Browse the repository at this point in the history
…ble by default

Summary: - thread through the StreamContext so it's actually possible to change
   SSLSocket settings - code to enable it was unreachable
 - affects both raw sockets and file_get_contents etc
 - CuRL was unaffected
 - enable verify_peer by default (behavior change in PHP 5.6.9)
 - use the system certificate store if none is specified

With thanks to @

Reviewed By: @siyengar

Differential Revision: D2171039
  • Loading branch information
fredemmott authored and hhvm-bot committed Jun 29, 2015
1 parent 8a57588 commit e282a45
Show file tree
Hide file tree
Showing 8 changed files with 54 additions and 17 deletions.
7 changes: 4 additions & 3 deletions hphp/runtime/base/http-client.cpp
Expand Up @@ -136,7 +136,7 @@ int HttpClient::request(const char* verb,
curl_easy_setopt(cp, CURLOPT_DNS_USE_GLOBAL_CACHE, 0); // for thread-safe
curl_easy_setopt(cp, CURLOPT_DNS_CACHE_TIMEOUT, 120);
curl_easy_setopt(cp, CURLOPT_NOSIGNAL, 1); // for multithreading mode
curl_easy_setopt(cp, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(cp, CURLOPT_SSL_VERIFYPEER, 1);
curl_easy_setopt(cp, CURLOPT_SSL_CTX_FUNCTION, curl_tls_workarounds_cb);

/*
Expand Down Expand Up @@ -214,8 +214,9 @@ int HttpClient::request(const char* verb,

if (m_stream_context_options[s_ssl].isArray()) {
const Array ssl = m_stream_context_options[s_ssl].toArray();
if (ssl[s_verify_peer].toBoolean()) {
curl_easy_setopt(cp, CURLOPT_SSL_VERIFYPEER, 1);
if (ssl.exists(s_verify_peer)) {
curl_easy_setopt(cp, CURLOPT_SSL_VERIFYPEER,
ssl[s_verify_peer].toBoolean());
}
if (ssl.exists(s_capath)) {
curl_easy_setopt(cp, CURLOPT_CAPATH,
Expand Down
1 change: 1 addition & 0 deletions hphp/runtime/base/http-stream-wrapper.cpp
Expand Up @@ -126,6 +126,7 @@ HttpStreamWrapper::open(const String& filename,
auto file = makeSmartPtr<UrlFile>(method.data(), headers,
post_data, max_redirs,
timeout, ignore_errors);
file->setStreamContext(context);
file->setProxy(proxy_host, proxy_port, proxy_user, proxy_pass);
bool ret = file->open(filename, mode);
if (!ret) {
Expand Down
31 changes: 26 additions & 5 deletions hphp/runtime/base/ssl-socket.cpp
Expand Up @@ -128,6 +128,10 @@ SSL *SSLSocket::createSSL(SSL_CTX *ctx) {
cafile.data(), capath.data());
return nullptr;
}
} else if (m_data->m_client && !SSL_CTX_set_default_verify_paths(ctx)) {
raise_warning(
"Unable to set default verify locations and no CA settings specified");
return nullptr;
}

int64_t depth = m_context[s_verify_depth].toInt64();
Expand Down Expand Up @@ -200,11 +204,19 @@ SSLSocket::SSLSocket()
m_data(static_cast<SSLSocketData*>(getSocketData()))
{}

SSLSocket::SSLSocket(int sockfd, int type, const char *address /* = NULL */,
int port /* = 0 */)
StaticString s_ssl("ssl");

SSLSocket::SSLSocket(int sockfd, int type, const SmartPtr<StreamContext>& ctx,
const char *address /* = NULL */, int port /* = 0 */)
: Socket(std::make_shared<SSLSocketData>(), sockfd, type, address, port),
m_data(static_cast<SSLSocketData*>(getSocketData()))
{}
{
if (!ctx) {
return;
}
this->setStreamContext(ctx);
this->m_context = ctx->getOptions()[s_ssl].toArray();
}

SSLSocket::~SSLSocket() { }

Expand Down Expand Up @@ -322,7 +334,8 @@ bool SSLSocket::handleError(int64_t nr_bytes, bool is_init) {
///////////////////////////////////////////////////////////////////////////////

SmartPtr<SSLSocket> SSLSocket::Create(
int fd, int domain, const HostURL &hosturl, double timeout) {
int fd, int domain, const HostURL &hosturl, double timeout,
const SmartPtr<StreamContext>& ctx) {
CryptoMethod method;
const std::string scheme = hosturl.getScheme();

Expand All @@ -339,7 +352,7 @@ SmartPtr<SSLSocket> SSLSocket::Create(
}

auto sock = makeSmartPtr<SSLSocket>(
fd, domain, hosturl.getHost().c_str(), hosturl.getPort());
fd, domain, ctx, hosturl.getHost().c_str(), hosturl.getPort());

sock->m_data->m_method = method;
sock->m_data->m_connect_timeout = timeout;
Expand Down Expand Up @@ -400,6 +413,10 @@ bool SSLSocket::setupCrypto(SSLSocket *session /* = NULL */) {
return false;
}

if (!m_context.exists(s_verify_peer)) {
m_context.add(s_verify_peer, true);
}

/* need to do slightly different things, based on client/server method,
* so lets remember which method was selected */
#if OPENSSL_VERSION_NUMBER < 0x00909000L
Expand Down Expand Up @@ -524,6 +541,10 @@ bool SSLSocket::applyVerificationPolicy(X509 *peer) {
} else if (name_len != (int)strlen(buf)) {
raise_warning("Peer certificate CN=`%.*s' is malformed", name_len, buf);
return false;
} else if (name_len == sizeof(buf)) {
raise_warning("Peer certificate CN=`%.*s' is too long to verify",
name_len, buf);
return false;
}

bool match = (strcmp(cnmatch.c_str(), buf) == 0);
Expand Down
6 changes: 4 additions & 2 deletions hphp/runtime/base/ssl-socket.h
Expand Up @@ -48,10 +48,12 @@ struct SSLSocket : Socket {

static int GetSSLExDataIndex();
static SmartPtr<SSLSocket> Create(int fd, int domain, const HostURL &hosturl,
double timeout);
double timeout,
const SmartPtr<StreamContext>& ctx);

SSLSocket();
SSLSocket(int sockfd, int type, const char *address = nullptr, int port = 0);
SSLSocket(int sockfd, int type, const SmartPtr<StreamContext>& ctx,
const char *address = nullptr, int port = 0);
virtual ~SSLSocket();
DECLARE_RESOURCE_ALLOCATION(SSLSocket);

Expand Down
4 changes: 4 additions & 0 deletions hphp/runtime/base/url-file.cpp
Expand Up @@ -79,6 +79,10 @@ bool UrlFile::open(const String& input_url, const String& mode) {
return false;
}
HttpClient http(m_timeout, m_maxRedirect);
auto ctx = this->getStreamContext();
if (ctx) {
http.setStreamContextOptions(ctx->getOptions());
}
m_response.clear();

if (!m_proxyHost.empty()) {
Expand Down
17 changes: 12 additions & 5 deletions hphp/runtime/ext/sockets/ext_sockets.cpp
Expand Up @@ -405,6 +405,7 @@ static int connect_with_timeout(int fd, struct sockaddr *sa_ptr,
}

static Variant new_socket_connect(const HostURL &hosturl, double timeout,
const SmartPtr<StreamContext>& streamctx,
Variant &errnum, Variant &errstr) {
int domain = AF_UNSPEC;
int type = SOCK_STREAM;
Expand Down Expand Up @@ -474,7 +475,7 @@ static Variant new_socket_connect(const HostURL &hosturl, double timeout,
fd = -1;
}

sslsock = SSLSocket::Create(fd, domain, hosturl, timeout);
sslsock = SSLSocket::Create(fd, domain, hosturl, timeout, streamctx);
if (sslsock) {
sock = sslsock;
} else {
Expand Down Expand Up @@ -1341,7 +1342,8 @@ thread_local std::unordered_map<
}

Variant sockopen_impl(const HostURL &hosturl, VRefParam errnum,
VRefParam errstr, double timeout, bool persistent) {
VRefParam errstr, double timeout, bool persistent,
const Variant& context) {
errnum = 0;
errstr = empty_string();
std::string key;
Expand All @@ -1368,7 +1370,12 @@ Variant sockopen_impl(const HostURL &hosturl, VRefParam errnum,
m_reqInjectionData.getSocketDefaultTimeout();
}

auto socket = new_socket_connect(hosturl, timeout, errnum, errstr);

SmartPtr<StreamContext> streamctx;
if (context.isResource()) {
streamctx = cast<StreamContext>(context.toResource());
}
auto socket = new_socket_connect(hosturl, timeout, streamctx, errnum, errstr);
if (!socket.isResource()) {
return false;
}
Expand All @@ -1389,7 +1396,7 @@ Variant HHVM_FUNCTION(fsockopen,
VRefParam errstr /* = null */,
double timeout /* = -1.0 */) {
HostURL hosturl(static_cast<const std::string>(hostname), port);
return sockopen_impl(hosturl, errnum, errstr, timeout, false);
return sockopen_impl(hosturl, errnum, errstr, timeout, false, null_variant);
}

Variant HHVM_FUNCTION(pfsockopen,
Expand All @@ -1399,7 +1406,7 @@ Variant HHVM_FUNCTION(pfsockopen,
VRefParam errstr /* = null */,
double timeout /* = -1.0 */) {
HostURL hosturl(static_cast<const std::string>(hostname), port);
return sockopen_impl(hosturl, errnum, errstr, timeout, true);
return sockopen_impl(hosturl, errnum, errstr, timeout, true, null_variant);
}

String ipaddr_convert(struct sockaddr *addr, int addrlen) {
Expand Down
3 changes: 2 additions & 1 deletion hphp/runtime/ext/sockets/ext_sockets.h
Expand Up @@ -158,7 +158,8 @@ Variant socket_server_impl(const HostURL &hosturl,
VRefParam errnum = uninit_null(),
VRefParam errstr = uninit_null());
Variant sockopen_impl(const HostURL &hosturl, VRefParam errnum,
VRefParam errstr, double timeout, bool persistent);
VRefParam errstr, double timeout, bool persistent,
const Variant& context);

///////////////////////////////////////////////////////////////////////////////
}
Expand Down
2 changes: 1 addition & 1 deletion hphp/runtime/ext/stream/ext_stream.cpp
Expand Up @@ -682,7 +682,7 @@ Variant HHVM_FUNCTION(stream_socket_client,
int flags /* = 0 */,
const Variant& context /* = null_variant */) {
HostURL hosturl(static_cast<const std::string>(remote_socket));
return sockopen_impl(hosturl, errnum, errstr, timeout, false);
return sockopen_impl(hosturl, errnum, errstr, timeout, false, context);
}

Variant HHVM_FUNCTION(stream_socket_get_name,
Expand Down

0 comments on commit e282a45

Please sign in to comment.