Commit 4700a31c authored by Anirudh Ramachandran's avatar Anirudh Ramachandran Committed by Facebook Github Bot 1

SSL cleanup: moving some OpenSSL definitions to new dir folly/io/async/ssl

Summary:SSLContext and AsyncSSLSocket are growing with a lot of code that is
OpenSSL-specific, and it may be good to refactor some of these before it gets
out of hand.

This is also useful to reduce complexity as we some additional features such as ServerHello parsing and TLS Cached Info (D2936570)

Main changes:
 * Created a subdirectory folly/io/async/ssl to refactor code from folly/io/async. We may want to consider moving this out of folly/io/async
 * Moved OpenSSLPtrTypes.h to folly/io/async/ssl/OpenSSLPtrTypes.h
 * Moved 'OpenSSLUtils' from SSLContext to separate file OpenSSLUtils.{h,cpp}
 * Moved TLSExtensions and ClientHelloInfo from AsyncSSLSocket to TLSDefinitions.h

Reviewed By: siyengar

Differential Revision: D2978707

fb-gh-sync-id: a21f02947aeffccc447da2124a91cc99315df1c7
shipit-source-id: a21f02947aeffccc447da2124a91cc99315df1c7
parent e756d07c
...@@ -229,7 +229,9 @@ nobase_follyinclude_HEADERS = \ ...@@ -229,7 +229,9 @@ nobase_follyinclude_HEADERS = \
io/async/EventUtil.h \ io/async/EventUtil.h \
io/async/NotificationQueue.h \ io/async/NotificationQueue.h \
io/async/HHWheelTimer.h \ io/async/HHWheelTimer.h \
io/async/OpenSSLPtrTypes.h \ io/async/ssl/OpenSSLPtrTypes.h \
io/async/ssl/OpenSSLUtils.h \
io/async/ssl/TLSDefinitions.h \
io/async/Request.h \ io/async/Request.h \
io/async/SSLContext.h \ io/async/SSLContext.h \
io/async/ScopedEventBaseThread.h \ io/async/ScopedEventBaseThread.h \
...@@ -397,6 +399,7 @@ libfolly_la_SOURCES = \ ...@@ -397,6 +399,7 @@ libfolly_la_SOURCES = \
io/async/HHWheelTimer.cpp \ io/async/HHWheelTimer.cpp \
io/async/test/SocketPair.cpp \ io/async/test/SocketPair.cpp \
io/async/test/TimeUtil.cpp \ io/async/test/TimeUtil.cpp \
io/async/ssl/OpenSSLUtils.cpp \
json.cpp \ json.cpp \
detail/MemoryIdler.cpp \ detail/MemoryIdler.cpp \
MacAddress.cpp \ MacAddress.cpp \
......
...@@ -1634,7 +1634,7 @@ int AsyncSSLSocket::sslVerifyCallback(int preverifyOk, ...@@ -1634,7 +1634,7 @@ int AsyncSSLSocket::sslVerifyCallback(int preverifyOk,
void AsyncSSLSocket::enableClientHelloParsing() { void AsyncSSLSocket::enableClientHelloParsing() {
parseClientHello_ = true; parseClientHello_ = true;
clientHelloInfo_.reset(new ClientHelloInfo()); clientHelloInfo_.reset(new ssl::ClientHelloInfo());
} }
void AsyncSSLSocket::resetClientHelloParsing(SSL *ssl) { void AsyncSSLSocket::resetClientHelloParsing(SSL *ssl) {
...@@ -1711,22 +1711,22 @@ void AsyncSSLSocket::clientHelloParsingCallback(int written, ...@@ -1711,22 +1711,22 @@ void AsyncSSLSocket::clientHelloParsingCallback(int written,
if (cursor.totalLength() > 0) { if (cursor.totalLength() > 0) {
uint16_t extensionsLength = cursor.readBE<uint16_t>(); uint16_t extensionsLength = cursor.readBE<uint16_t>();
while (extensionsLength) { while (extensionsLength) {
TLSExtension extensionType = static_cast<TLSExtension>( ssl::TLSExtension extensionType =
cursor.readBE<uint16_t>()); static_cast<ssl::TLSExtension>(cursor.readBE<uint16_t>());
sock->clientHelloInfo_-> sock->clientHelloInfo_->
clientHelloExtensions_.push_back(extensionType); clientHelloExtensions_.push_back(extensionType);
extensionsLength -= 2; extensionsLength -= 2;
uint16_t extensionDataLength = cursor.readBE<uint16_t>(); uint16_t extensionDataLength = cursor.readBE<uint16_t>();
extensionsLength -= 2; extensionsLength -= 2;
if (extensionType == TLSExtension::SIGNATURE_ALGORITHMS) { if (extensionType == ssl::TLSExtension::SIGNATURE_ALGORITHMS) {
cursor.skip(2); cursor.skip(2);
extensionDataLength -= 2; extensionDataLength -= 2;
while (extensionDataLength) { while (extensionDataLength) {
HashAlgorithm hashAlg = static_cast<HashAlgorithm>( ssl::HashAlgorithm hashAlg =
cursor.readBE<uint8_t>()); static_cast<ssl::HashAlgorithm>(cursor.readBE<uint8_t>());
SignatureAlgorithm sigAlg = static_cast<SignatureAlgorithm>( ssl::SignatureAlgorithm sigAlg =
cursor.readBE<uint8_t>()); static_cast<ssl::SignatureAlgorithm>(cursor.readBE<uint8_t>());
extensionDataLength -= 2; extensionDataLength -= 2;
sock->clientHelloInfo_-> sock->clientHelloInfo_->
clientHelloSigAlgs_.emplace_back(hashAlg, sigAlg); clientHelloSigAlgs_.emplace_back(hashAlg, sigAlg);
......
...@@ -22,10 +22,12 @@ ...@@ -22,10 +22,12 @@
#include <folly/Optional.h> #include <folly/Optional.h>
#include <folly/String.h> #include <folly/String.h>
#include <folly/io/async/AsyncSocket.h> #include <folly/io/async/AsyncSocket.h>
#include <folly/io/async/SSLContext.h>
#include <folly/io/async/AsyncTimeout.h> #include <folly/io/async/AsyncTimeout.h>
#include <folly/io/async/OpenSSLPtrTypes.h> #include <folly/io/async/SSLContext.h>
#include <folly/io/async/TimeoutManager.h> #include <folly/io/async/TimeoutManager.h>
#include <folly/io/async/ssl/OpenSSLPtrTypes.h>
#include <folly/io/async/ssl/OpenSSLUtils.h>
#include <folly/io/async/ssl/TLSDefinitions.h>
#include <folly/Bits.h> #include <folly/Bits.h>
#include <folly/io/IOBuf.h> #include <folly/io/IOBuf.h>
...@@ -652,68 +654,8 @@ class AsyncSSLSocket : public virtual AsyncSocket { ...@@ -652,68 +654,8 @@ class AsyncSSLSocket : public virtual AsyncSocket {
int content_type, const void *buf, size_t len, SSL *ssl, void *arg); int content_type, const void *buf, size_t len, SSL *ssl, void *arg);
static const char* getSSLServerNameFromSSL(SSL* ssl); static const char* getSSLServerNameFromSSL(SSL* ssl);
// http://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml
enum class TLSExtension: uint16_t {
SERVER_NAME = 0,
MAX_FRAGMENT_LENGTH = 1,
CLIENT_CERTIFICATE_URL = 2,
TRUSTED_CA_KEYS = 3,
TRUNCATED_HMAC = 4,
STATUS_REQUEST = 5,
USER_MAPPING = 6,
CLIENT_AUTHZ = 7,
SERVER_AUTHZ = 8,
CERT_TYPE = 9,
SUPPORTED_GROUPS = 10,
EC_POINT_FORMATS = 11,
SRP = 12,
SIGNATURE_ALGORITHMS = 13,
USE_SRTP = 14,
HEARTBEAT = 15,
APPLICATION_LAYER_PROTOCOL_NEGOTIATION = 16,
STATUS_REQUEST_V2 = 17,
SIGNED_CERTIFICATE_TIMESTAMP = 18,
CLIENT_CERTIFICATE_TYPE = 19,
SERVER_CERTIFICATE_TYPE = 20,
PADDING = 21,
ENCRYPT_THEN_MAC = 22,
EXTENDED_MASTER_SECRET = 23,
SESSION_TICKET = 35,
RENEGOTIATION_INFO = 65281
};
// http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18
enum class HashAlgorithm: uint8_t {
NONE = 0,
MD5 = 1,
SHA1 = 2,
SHA224 = 3,
SHA256 = 4,
SHA384 = 5,
SHA512 = 6
};
// http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16
enum class SignatureAlgorithm: uint8_t {
ANONYMOUS = 0,
RSA = 1,
DSA = 2,
ECDSA = 3
};
struct ClientHelloInfo {
folly::IOBufQueue clientHelloBuf_;
uint8_t clientHelloMajorVersion_;
uint8_t clientHelloMinorVersion_;
std::vector<uint16_t> clientHelloCipherSuites_;
std::vector<uint8_t> clientHelloCompressionMethods_;
std::vector<TLSExtension> clientHelloExtensions_;
std::vector<
std::pair<HashAlgorithm, SignatureAlgorithm>> clientHelloSigAlgs_;
};
// For unit-tests // For unit-tests
ClientHelloInfo* getClientHelloInfo() const { ssl::ClientHelloInfo* getClientHelloInfo() const {
return clientHelloInfo_.get(); return clientHelloInfo_.get();
} }
...@@ -737,13 +679,13 @@ class AsyncSSLSocket : public virtual AsyncSocket { ...@@ -737,13 +679,13 @@ class AsyncSSLSocket : public virtual AsyncSocket {
/** /**
* Returns the peer certificate, or nullptr if no peer certificate received. * Returns the peer certificate, or nullptr if no peer certificate received.
*/ */
virtual X509_UniquePtr getPeerCert() const override { virtual ssl::X509UniquePtr getPeerCert() const override {
if (!ssl_) { if (!ssl_) {
return nullptr; return nullptr;
} }
X509* cert = SSL_get_peer_certificate(ssl_); X509* cert = SSL_get_peer_certificate(ssl_);
return X509_UniquePtr(cert); return ssl::X509UniquePtr(cert);
} }
/** /**
...@@ -886,7 +828,7 @@ class AsyncSSLSocket : public virtual AsyncSocket { ...@@ -886,7 +828,7 @@ class AsyncSSLSocket : public virtual AsyncSocket {
bool parseClientHello_{false}; bool parseClientHello_{false};
bool cacheAddrOnFailure_{false}; bool cacheAddrOnFailure_{false};
std::unique_ptr<ClientHelloInfo> clientHelloInfo_; std::unique_ptr<ssl::ClientHelloInfo> clientHelloInfo_;
// Time taken to complete the ssl handshake. // Time taken to complete the ssl handshake.
std::chrono::steady_clock::time_point handshakeStartTime_; std::chrono::steady_clock::time_point handshakeStartTime_;
......
...@@ -20,10 +20,10 @@ ...@@ -20,10 +20,10 @@
#include <sys/uio.h> #include <sys/uio.h>
#include <folly/io/IOBuf.h> #include <folly/io/IOBuf.h>
#include <folly/io/async/AsyncSocketBase.h>
#include <folly/io/async/DelayedDestruction.h> #include <folly/io/async/DelayedDestruction.h>
#include <folly/io/async/EventBase.h> #include <folly/io/async/EventBase.h>
#include <folly/io/async/AsyncSocketBase.h> #include <folly/io/async/ssl/OpenSSLPtrTypes.h>
#include <folly/io/async/OpenSSLPtrTypes.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
...@@ -324,7 +324,7 @@ class AsyncTransport : public DelayedDestruction, public AsyncSocketBase { ...@@ -324,7 +324,7 @@ class AsyncTransport : public DelayedDestruction, public AsyncSocketBase {
/** /**
* Get the certificate used to authenticate the peer. * Get the certificate used to authenticate the peer.
*/ */
virtual X509_UniquePtr getPeerCert() const { return nullptr; } virtual ssl::X509UniquePtr getPeerCert() const { return nullptr; }
/** /**
* @return True iff end of record tracking is enabled * @return True iff end of record tracking is enabled
......
/*
* Copyright 2016 Facebook, Inc.
*
* 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/Memory.h>
#include <openssl/ssl.h>
namespace folly {
using X509_deleter = static_function_deleter<X509, &X509_free>;
using X509_UniquePtr = std::unique_ptr<X509, X509_deleter>;
using EVP_PKEY_deleter =
folly::static_function_deleter<EVP_PKEY, &EVP_PKEY_free>;
using EVP_PKEY_UniquePtr = std::unique_ptr<EVP_PKEY, EVP_PKEY_deleter>;
using SSL_deleter = folly::static_function_deleter<SSL, &SSL_free>;
using SSL_UniquePtr = std::unique_ptr<SSL, SSL_deleter>;
}
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
#include <folly/Format.h> #include <folly/Format.h>
#include <folly/Memory.h> #include <folly/Memory.h>
#include <folly/SpinLock.h> #include <folly/SpinLock.h>
#include <folly/io/async/OpenSSLPtrTypes.h>
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// SSLContext implementation // SSLContext implementation
...@@ -45,9 +44,6 @@ std::mutex& initMutex() { ...@@ -45,9 +44,6 @@ std::mutex& initMutex() {
return m; return m;
} }
inline void BIO_free_fb(BIO* bio) { CHECK_EQ(1, BIO_free(bio)); }
using BIO_deleter = folly::static_function_deleter<BIO, &BIO_free_fb>;
} // anonymous namespace } // anonymous namespace
#ifdef OPENSSL_NPN_NEGOTIATED #ifdef OPENSSL_NPN_NEGOTIATED
...@@ -196,7 +192,7 @@ void SSLContext::loadCertificateFromBufferPEM(folly::StringPiece cert) { ...@@ -196,7 +192,7 @@ void SSLContext::loadCertificateFromBufferPEM(folly::StringPiece cert) {
throw std::invalid_argument("loadCertificate: <cert> is nullptr"); throw std::invalid_argument("loadCertificate: <cert> is nullptr");
} }
std::unique_ptr<BIO, BIO_deleter> bio(BIO_new(BIO_s_mem())); ssl::BioUniquePtr bio(BIO_new(BIO_s_mem()));
if (bio == nullptr) { if (bio == nullptr) {
throw std::runtime_error("BIO_new: " + getErrors()); throw std::runtime_error("BIO_new: " + getErrors());
} }
...@@ -206,7 +202,8 @@ void SSLContext::loadCertificateFromBufferPEM(folly::StringPiece cert) { ...@@ -206,7 +202,8 @@ void SSLContext::loadCertificateFromBufferPEM(folly::StringPiece cert) {
throw std::runtime_error("BIO_write: " + getErrors()); throw std::runtime_error("BIO_write: " + getErrors());
} }
X509_UniquePtr x509(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); ssl::X509UniquePtr x509(
PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
if (x509 == nullptr) { if (x509 == nullptr) {
throw std::runtime_error("PEM_read_bio_X509: " + getErrors()); throw std::runtime_error("PEM_read_bio_X509: " + getErrors());
} }
...@@ -235,7 +232,7 @@ void SSLContext::loadPrivateKeyFromBufferPEM(folly::StringPiece pkey) { ...@@ -235,7 +232,7 @@ void SSLContext::loadPrivateKeyFromBufferPEM(folly::StringPiece pkey) {
throw std::invalid_argument("loadPrivateKey: <pkey> is nullptr"); throw std::invalid_argument("loadPrivateKey: <pkey> is nullptr");
} }
std::unique_ptr<BIO, BIO_deleter> bio(BIO_new(BIO_s_mem())); ssl::BioUniquePtr bio(BIO_new(BIO_s_mem()));
if (bio == nullptr) { if (bio == nullptr) {
throw std::runtime_error("BIO_new: " + getErrors()); throw std::runtime_error("BIO_new: " + getErrors());
} }
...@@ -245,7 +242,7 @@ void SSLContext::loadPrivateKeyFromBufferPEM(folly::StringPiece pkey) { ...@@ -245,7 +242,7 @@ void SSLContext::loadPrivateKeyFromBufferPEM(folly::StringPiece pkey) {
throw std::runtime_error("BIO_write: " + getErrors()); throw std::runtime_error("BIO_write: " + getErrors());
} }
EVP_PKEY_UniquePtr key( ssl::EvpPkeyUniquePtr key(
PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
if (key == nullptr) { if (key == nullptr) {
throw std::runtime_error("PEM_read_bio_PrivateKey: " + getErrors()); throw std::runtime_error("PEM_read_bio_PrivateKey: " + getErrors());
...@@ -799,83 +796,4 @@ operator<<(std::ostream& os, const PasswordCollector& collector) { ...@@ -799,83 +796,4 @@ operator<<(std::ostream& os, const PasswordCollector& collector) {
return os; return os;
} }
bool OpenSSLUtils::getPeerAddressFromX509StoreCtx(X509_STORE_CTX* ctx,
sockaddr_storage* addrStorage,
socklen_t* addrLen) {
// Grab the ssl idx and then the ssl object so that we can get the peer
// name to compare against the ips in the subjectAltName
auto sslIdx = SSL_get_ex_data_X509_STORE_CTX_idx();
auto ssl =
reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(ctx, sslIdx));
int fd = SSL_get_fd(ssl);
if (fd < 0) {
LOG(ERROR) << "Inexplicably couldn't get fd from SSL";
return false;
}
*addrLen = sizeof(*addrStorage);
if (getpeername(fd, reinterpret_cast<sockaddr*>(addrStorage), addrLen) != 0) {
PLOG(ERROR) << "Unable to get peer name";
return false;
}
CHECK(*addrLen <= sizeof(*addrStorage));
return true;
}
bool OpenSSLUtils::validatePeerCertNames(X509* cert,
const sockaddr* addr,
socklen_t /* addrLen */) {
// Try to extract the names within the SAN extension from the certificate
auto altNames =
reinterpret_cast<STACK_OF(GENERAL_NAME)*>(
X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr));
SCOPE_EXIT {
if (altNames != nullptr) {
sk_GENERAL_NAME_pop_free(altNames, GENERAL_NAME_free);
}
};
if (altNames == nullptr) {
LOG(WARNING) << "No subjectAltName provided and we only support ip auth";
return false;
}
const sockaddr_in* addr4 = nullptr;
const sockaddr_in6* addr6 = nullptr;
if (addr != nullptr) {
if (addr->sa_family == AF_INET) {
addr4 = reinterpret_cast<const sockaddr_in*>(addr);
} else if (addr->sa_family == AF_INET6) {
addr6 = reinterpret_cast<const sockaddr_in6*>(addr);
} else {
LOG(FATAL) << "Unsupported sockaddr family: " << addr->sa_family;
}
}
for (int i = 0; i < sk_GENERAL_NAME_num(altNames); i++) {
auto name = sk_GENERAL_NAME_value(altNames, i);
if ((addr4 != nullptr || addr6 != nullptr) && name->type == GEN_IPADD) {
// Extra const-ness for paranoia
unsigned char const * const rawIpStr = name->d.iPAddress->data;
int const rawIpLen = name->d.iPAddress->length;
if (rawIpLen == 4 && addr4 != nullptr) {
if (::memcmp(rawIpStr, &addr4->sin_addr, rawIpLen) == 0) {
return true;
}
} else if (rawIpLen == 16 && addr6 != nullptr) {
if (::memcmp(rawIpStr, &addr6->sin6_addr, rawIpLen) == 0) {
return true;
}
} else if (rawIpLen != 4 && rawIpLen != 16) {
LOG(WARNING) << "Unexpected IP length: " << rawIpLen;
}
}
}
LOG(WARNING) << "Unable to match client cert against alt name ip";
return false;
}
} // folly } // folly
...@@ -38,6 +38,8 @@ ...@@ -38,6 +38,8 @@
#include <folly/Random.h> #include <folly/Random.h>
#include <folly/Range.h> #include <folly/Range.h>
#include <folly/io/async/ssl/OpenSSLPtrTypes.h>
#include <folly/io/async/ssl/OpenSSLUtils.h>
namespace folly { namespace folly {
...@@ -549,40 +551,5 @@ typedef std::shared_ptr<SSLContext> SSLContextPtr; ...@@ -549,40 +551,5 @@ typedef std::shared_ptr<SSLContext> SSLContextPtr;
std::ostream& operator<<(std::ostream& os, const folly::PasswordCollector& collector); std::ostream& operator<<(std::ostream& os, const folly::PasswordCollector& collector);
class OpenSSLUtils {
public:
/**
* Validate that the peer certificate's common name or subject alt names
* match what we expect. Currently this only checks for IPs within
* subject alt names but it could easily be expanded to check common name
* and hostnames as well.
*
* @param cert X509* peer certificate
* @param addr sockaddr object containing sockaddr to verify
* @param addrLen length of sockaddr as returned by getpeername or accept
* @return true iff a subject altname IP matches addr
*/
// TODO(agartrell): Add support for things like common name when
// necessary.
static bool validatePeerCertNames(X509* cert,
const sockaddr* addr,
socklen_t addrLen);
/**
* Get the peer socket address from an X509_STORE_CTX*. Unlike the
* accept, getsockname, getpeername, etc family of operations, addrLen's
* initial value is ignored and reset.
*
* @param ctx Context from which to retrieve peer sockaddr
* @param addrStorage out param for address
* @param addrLen out param for length of address
* @return true on success, false on failure
*/
static bool getPeerAddressFromX509StoreCtx(X509_STORE_CTX* ctx,
sockaddr_storage* addrStorage,
socklen_t* addrLen);
};
} // folly } // folly
/*
* Copyright 2016 Facebook, Inc.
*
* 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 <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/ssl.h>
#ifndef OPENSSL_NO_EC
#include <openssl/ec.h>
#endif
#include <folly/Memory.h>
#include <openssl/bio.h>
#include <openssl/x509.h>
namespace folly {
namespace ssl {
// X509
using X509Deleter = folly::static_function_deleter<X509, &X509_free>;
using X509UniquePtr = std::unique_ptr<X509, X509Deleter>;
using X509StoreCtxDeleter =
folly::static_function_deleter<X509_STORE_CTX, &X509_STORE_CTX_free>;
using X509StoreCtxUniquePtr =
std::unique_ptr<X509_STORE_CTX, X509StoreCtxDeleter>;
// EVP
using EvpPkeyDel = folly::static_function_deleter<EVP_PKEY, &EVP_PKEY_free>;
using EvpPkeyUniquePtr = std::unique_ptr<EVP_PKEY, EvpPkeyDel>;
using EvpPkeySharedPtr = std::shared_ptr<EVP_PKEY>;
// No EVP_PKEY_CTX <= 0.9.8b
#if OPENSSL_VERSION_NUMBER >= 0x10000002L
using EvpPkeyCtxDeleter =
folly::static_function_deleter<EVP_PKEY_CTX, &EVP_PKEY_CTX_free>;
using EvpPkeyCtxUniquePtr = std::unique_ptr<EVP_PKEY_CTX, EvpPkeyCtxDeleter>;
#else
struct EVP_PKEY_CTX;
#endif
using EvpMdCtxDeleter =
folly::static_function_deleter<EVP_MD_CTX, &EVP_MD_CTX_destroy>;
using EvpMdCtxUniquePtr = std::unique_ptr<EVP_MD_CTX, EvpMdCtxDeleter>;
// BIO
using BioDeleter = folly::static_function_deleter<BIO, &BIO_vfree>;
using BioUniquePtr = std::unique_ptr<BIO, BioDeleter>;
using BioChainDeleter = folly::static_function_deleter<BIO, &BIO_free_all>;
using BioChainUniquePtr = std::unique_ptr<BIO, BioChainDeleter>;
inline void BIO_free_fb(BIO* bio) { CHECK_EQ(1, BIO_free(bio)); }
using BioDeleterFb = folly::static_function_deleter<BIO, &BIO_free_fb>;
using BioUniquePtrFb = std::unique_ptr<BIO, BioDeleterFb>;
// RSA and EC
using RsaDeleter = folly::static_function_deleter<RSA, &RSA_free>;
using RsaUniquePtr = std::unique_ptr<RSA, RsaDeleter>;
#ifndef OPENSSL_NO_EC
using EcKeyDeleter = folly::static_function_deleter<EC_KEY, &EC_KEY_free>;
using EcKeyUniquePtr = std::unique_ptr<EC_KEY, EcKeyDeleter>;
#endif
// SSL and SSL_CTX
using SSLDeleter = folly::static_function_deleter<SSL, &SSL_free>;
using SSLUniquePtr = std::unique_ptr<SSL, SSLDeleter>;
}
}
/*
* Copyright 2016 Facebook, Inc.
*
* 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.
*/
#include <folly/io/async/ssl/OpenSSLUtils.h>
#include <folly/ScopeGuard.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <glog/logging.h>
namespace folly {
namespace ssl {
bool OpenSSLUtils::getPeerAddressFromX509StoreCtx(X509_STORE_CTX* ctx,
sockaddr_storage* addrStorage,
socklen_t* addrLen) {
// Grab the ssl idx and then the ssl object so that we can get the peer
// name to compare against the ips in the subjectAltName
auto sslIdx = SSL_get_ex_data_X509_STORE_CTX_idx();
auto ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(ctx, sslIdx));
int fd = SSL_get_fd(ssl);
if (fd < 0) {
LOG(ERROR) << "Inexplicably couldn't get fd from SSL";
return false;
}
*addrLen = sizeof(*addrStorage);
if (getpeername(fd, reinterpret_cast<sockaddr*>(addrStorage), addrLen) != 0) {
PLOG(ERROR) << "Unable to get peer name";
return false;
}
CHECK(*addrLen <= sizeof(*addrStorage));
return true;
}
bool OpenSSLUtils::validatePeerCertNames(X509* cert,
const sockaddr* addr,
socklen_t /* addrLen */) {
// Try to extract the names within the SAN extension from the certificate
auto altNames = reinterpret_cast<STACK_OF(GENERAL_NAME)*>(
X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr));
SCOPE_EXIT {
if (altNames != nullptr) {
sk_GENERAL_NAME_pop_free(altNames, GENERAL_NAME_free);
}
};
if (altNames == nullptr) {
LOG(WARNING) << "No subjectAltName provided and we only support ip auth";
return false;
}
const sockaddr_in* addr4 = nullptr;
const sockaddr_in6* addr6 = nullptr;
if (addr != nullptr) {
if (addr->sa_family == AF_INET) {
addr4 = reinterpret_cast<const sockaddr_in*>(addr);
} else if (addr->sa_family == AF_INET6) {
addr6 = reinterpret_cast<const sockaddr_in6*>(addr);
} else {
LOG(FATAL) << "Unsupported sockaddr family: " << addr->sa_family;
}
}
for (int i = 0; i < sk_GENERAL_NAME_num(altNames); i++) {
auto name = sk_GENERAL_NAME_value(altNames, i);
if ((addr4 != nullptr || addr6 != nullptr) && name->type == GEN_IPADD) {
// Extra const-ness for paranoia
unsigned char const* const rawIpStr = name->d.iPAddress->data;
int const rawIpLen = name->d.iPAddress->length;
if (rawIpLen == 4 && addr4 != nullptr) {
if (::memcmp(rawIpStr, &addr4->sin_addr, rawIpLen) == 0) {
return true;
}
} else if (rawIpLen == 16 && addr6 != nullptr) {
if (::memcmp(rawIpStr, &addr6->sin6_addr, rawIpLen) == 0) {
return true;
}
} else if (rawIpLen != 4 && rawIpLen != 16) {
LOG(WARNING) << "Unexpected IP length: " << rawIpLen;
}
}
}
LOG(WARNING) << "Unable to match client cert against alt name ip";
return false;
}
} // ssl
} // folly
/*
* Copyright 2016 Facebook, Inc.
*
* 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 <openssl/x509v3.h>
#include <netinet/in.h>
#include <sys/socket.h>
namespace folly {
namespace ssl {
class OpenSSLUtils {
public:
/**
* Validate that the peer certificate's common name or subject alt names
* match what we expect. Currently this only checks for IPs within
* subject alt names but it could easily be expanded to check common name
* and hostnames as well.
*
* @param cert X509* peer certificate
* @param addr sockaddr object containing sockaddr to verify
* @param addrLen length of sockaddr as returned by getpeername or accept
* @return true iff a subject altname IP matches addr
*/
// TODO(agartrell): Add support for things like common name when
// necessary.
static bool validatePeerCertNames(X509* cert,
const sockaddr* addr,
socklen_t addrLen);
/**
* Get the peer socket address from an X509_STORE_CTX*. Unlike the
* accept, getsockname, getpeername, etc family of operations, addrLen's
* initial value is ignored and reset.
*
* @param ctx Context from which to retrieve peer sockaddr
* @param addrStorage out param for address
* @param addrLen out param for length of address
* @return true on success, false on failure
*/
static bool getPeerAddressFromX509StoreCtx(X509_STORE_CTX* ctx,
sockaddr_storage* addrStorage,
socklen_t* addrLen);
};
} // ssl
} // folly
/*
* Copyright 2016 Facebook, Inc.
*
* 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/io/Cursor.h>
#include <folly/io/IOBuf.h>
#include <map>
#include <openssl/ssl.h>
#include <openssl/tls1.h>
#include <vector>
namespace folly {
namespace ssl {
// http://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml
enum class TLSExtension : uint16_t {
SERVER_NAME = 0,
MAX_FRAGMENT_LENGTH = 1,
CLIENT_CERTIFICATE_URL = 2,
TRUSTED_CA_KEYS = 3,
TRUNCATED_HMAC = 4,
STATUS_REQUEST = 5,
USER_MAPPING = 6,
CLIENT_AUTHZ = 7,
SERVER_AUTHZ = 8,
CERT_TYPE = 9,
SUPPORTED_GROUPS = 10,
EC_POINT_FORMATS = 11,
SRP = 12,
SIGNATURE_ALGORITHMS = 13,
USE_SRTP = 14,
HEARTBEAT = 15,
APPLICATION_LAYER_PROTOCOL_NEGOTIATION = 16,
STATUS_REQUEST_V2 = 17,
SIGNED_CERTIFICATE_TIMESTAMP = 18,
CLIENT_CERTIFICATE_TYPE = 19,
SERVER_CERTIFICATE_TYPE = 20,
PADDING = 21,
ENCRYPT_THEN_MAC = 22,
EXTENDED_MASTER_SECRET = 23,
SESSION_TICKET = 35,
// Facebook-specific, not IANA assigned yet
TLS_CACHED_INFO_FB = 60001,
// End Facebook-specific
RENEGOTIATION_INFO = 65281
};
// http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18
enum class HashAlgorithm : uint8_t {
NONE = 0,
MD5 = 1,
SHA1 = 2,
SHA224 = 3,
SHA256 = 4,
SHA384 = 5,
SHA512 = 6
};
// http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16
enum class SignatureAlgorithm : uint8_t {
ANONYMOUS = 0,
RSA = 1,
DSA = 2,
ECDSA = 3
};
struct ClientHelloInfo {
folly::IOBufQueue clientHelloBuf_;
uint8_t clientHelloMajorVersion_;
uint8_t clientHelloMinorVersion_;
std::vector<uint16_t> clientHelloCipherSuites_;
std::vector<uint8_t> clientHelloCompressionMethods_;
std::vector<TLSExtension> clientHelloExtensions_;
std::vector<std::pair<HashAlgorithm, SignatureAlgorithm>> clientHelloSigAlgs_;
};
} // ssl
} // folly
...@@ -57,9 +57,6 @@ const char* testCA = "folly/io/async/test/certs/ca-cert.pem"; ...@@ -57,9 +57,6 @@ const char* testCA = "folly/io/async/test/certs/ca-cert.pem";
constexpr size_t SSLClient::kMaxReadBufferSz; constexpr size_t SSLClient::kMaxReadBufferSz;
constexpr size_t SSLClient::kMaxReadsPerEvent; constexpr size_t SSLClient::kMaxReadsPerEvent;
inline void BIO_free_fb(BIO* bio) { CHECK_EQ(1, BIO_free(bio)); }
using BIO_deleter = folly::static_function_deleter<BIO, &BIO_free_fb>;
TestSSLServer::TestSSLServer(SSLServerAcceptCallbackBase* acb) TestSSLServer::TestSSLServer(SSLServerAcceptCallbackBase* acb)
: ctx_(new folly::SSLContext), : ctx_(new folly::SSLContext),
acb_(acb), acb_(acb),
...@@ -1384,15 +1381,15 @@ TEST(AsyncSSLSocketTest, LoadCertFromMemory) { ...@@ -1384,15 +1381,15 @@ TEST(AsyncSSLSocketTest, LoadCertFromMemory) {
auto cert = getFileAsBuf(testCert); auto cert = getFileAsBuf(testCert);
auto key = getFileAsBuf(testKey); auto key = getFileAsBuf(testKey);
std::unique_ptr<BIO, BIO_deleter> certBio(BIO_new(BIO_s_mem())); ssl::BioUniquePtr certBio(BIO_new(BIO_s_mem()));
BIO_write(certBio.get(), cert.data(), cert.size()); BIO_write(certBio.get(), cert.data(), cert.size());
std::unique_ptr<BIO, BIO_deleter> keyBio(BIO_new(BIO_s_mem())); ssl::BioUniquePtr keyBio(BIO_new(BIO_s_mem()));
BIO_write(keyBio.get(), key.data(), key.size()); BIO_write(keyBio.get(), key.data(), key.size());
// Create SSL structs from buffers to get properties // Create SSL structs from buffers to get properties
X509_UniquePtr certStruct( ssl::X509UniquePtr certStruct(
PEM_read_bio_X509(certBio.get(), nullptr, nullptr, nullptr)); PEM_read_bio_X509(certBio.get(), nullptr, nullptr, nullptr));
EVP_PKEY_UniquePtr keyStruct( ssl::EvpPkeyUniquePtr keyStruct(
PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr)); PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr));
certBio = nullptr; certBio = nullptr;
keyBio = nullptr; keyBio = nullptr;
...@@ -1407,7 +1404,7 @@ TEST(AsyncSSLSocketTest, LoadCertFromMemory) { ...@@ -1407,7 +1404,7 @@ TEST(AsyncSSLSocketTest, LoadCertFromMemory) {
ctx->loadCertificateFromBufferPEM(cert); ctx->loadCertificateFromBufferPEM(cert);
ctx->loadTrustedCertificates(testCA); ctx->loadTrustedCertificates(testCA);
SSL_UniquePtr ssl(ctx->createSSL()); ssl::SSLUniquePtr ssl(ctx->createSSL());
auto newCert = SSL_get_certificate(ssl.get()); auto newCert = SSL_get_certificate(ssl.get());
auto newKey = SSL_get_privatekey(ssl.get()); auto newKey = SSL_get_privatekey(ssl.get());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment