Rocket.Chat.ReactNative/ios/Pods/Flipper-Folly/folly/io/async/ssl/OpenSSLUtils.cpp

382 lines
9.9 KiB
C++
Raw Normal View History

/*
* Copyright (c) Facebook, Inc. and its 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.
*/
#include <folly/io/async/ssl/OpenSSLUtils.h>
#include <glog/logging.h>
#include <unordered_map>
#include <folly/ScopeGuard.h>
#include <folly/portability/Sockets.h>
#include <folly/portability/Unistd.h>
#include <folly/ssl/Init.h>
namespace {
#ifdef OPENSSL_IS_BORINGSSL
// BoringSSL doesn't (as of May 2016) export the equivalent
// of BIO_sock_should_retry, so this is one way around it :(
static int boringssl_bio_fd_should_retry(int err);
#endif
} // namespace
namespace folly {
namespace ssl {
bool OpenSSLUtils::getTLSMasterKey(
const SSL_SESSION* session,
MutableByteRange keyOut) {
#if FOLLY_OPENSSL_IS_110
auto size = SSL_SESSION_get_master_key(session, nullptr, 0);
if (size == keyOut.size()) {
return SSL_SESSION_get_master_key(session, keyOut.begin(), keyOut.size());
}
#else
(void)session;
(void)keyOut;
#endif
return false;
}
bool OpenSSLUtils::getTLSClientRandom(
const SSL* ssl,
MutableByteRange randomOut) {
#if FOLLY_OPENSSL_IS_110
auto size = SSL_get_client_random(ssl, nullptr, 0);
if (size == randomOut.size()) {
return SSL_get_client_random(ssl, randomOut.begin(), randomOut.size());
}
#else
(void)ssl;
(void)randomOut;
#endif
return false;
}
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;
auto const rawIpLen = size_t(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;
}
static std::unordered_map<uint16_t, std::string> getOpenSSLCipherNames() {
folly::ssl::init();
std::unordered_map<uint16_t, std::string> ret;
SSL_CTX* ctx = nullptr;
SSL* ssl = nullptr;
const SSL_METHOD* meth = TLS_server_method();
#if OPENSSL_VERSION_NUMBER < 0x10100000L
OpenSSL_add_ssl_algorithms();
#endif
if ((ctx = SSL_CTX_new(meth)) == nullptr) {
return ret;
}
SCOPE_EXIT {
SSL_CTX_free(ctx);
};
if ((ssl = SSL_new(ctx)) == nullptr) {
return ret;
}
SCOPE_EXIT {
SSL_free(ssl);
};
STACK_OF(SSL_CIPHER)* sk = SSL_get_ciphers(ssl);
for (int i = 0; i < sk_SSL_CIPHER_num(sk); i++) {
const SSL_CIPHER* c = sk_SSL_CIPHER_value(sk, i);
unsigned long id = SSL_CIPHER_get_id(c);
// OpenSSL 1.0.2 and prior does weird things such as stuff the SSL/TLS
// version into the top 16 bits. Let's ignore those for now. This is
// BoringSSL compatible (their id can be cast as uint16_t)
uint16_t cipherCode = id & 0xffffL;
ret[cipherCode] = SSL_CIPHER_get_name(c);
}
return ret;
}
const std::string& OpenSSLUtils::getCipherName(uint16_t cipherCode) {
// Having this in a hash map saves the binary search inside OpenSSL
static std::unordered_map<uint16_t, std::string> cipherCodeToName(
getOpenSSLCipherNames());
const auto& iter = cipherCodeToName.find(cipherCode);
if (iter != cipherCodeToName.end()) {
return iter->second;
} else {
static std::string empty("");
return empty;
}
}
void OpenSSLUtils::setSSLInitialCtx(SSL* ssl, SSL_CTX* ctx) {
(void)ssl;
(void)ctx;
#if !FOLLY_OPENSSL_IS_110 && !defined(OPENSSL_NO_TLSEXT)
if (ssl) {
if (ctx) {
SSL_CTX_up_ref(ctx);
}
ssl->initial_ctx = ctx;
}
#endif
}
SSL_CTX* OpenSSLUtils::getSSLInitialCtx(SSL* ssl) {
(void)ssl;
#if !FOLLY_OPENSSL_IS_110 && !defined(OPENSSL_NO_TLSEXT)
if (ssl) {
return ssl->initial_ctx;
}
#endif
return nullptr;
}
BioMethodUniquePtr OpenSSLUtils::newSocketBioMethod() {
BIO_METHOD* newmeth = nullptr;
#if FOLLY_OPENSSL_IS_110
if (!(newmeth = BIO_meth_new(BIO_TYPE_SOCKET, "socket_bio_method"))) {
return nullptr;
}
auto meth = const_cast<BIO_METHOD*>(BIO_s_socket());
BIO_meth_set_create(newmeth, BIO_meth_get_create(meth));
BIO_meth_set_destroy(newmeth, BIO_meth_get_destroy(meth));
BIO_meth_set_ctrl(newmeth, BIO_meth_get_ctrl(meth));
BIO_meth_set_callback_ctrl(newmeth, BIO_meth_get_callback_ctrl(meth));
BIO_meth_set_read(newmeth, BIO_meth_get_read(meth));
BIO_meth_set_write(newmeth, BIO_meth_get_write(meth));
BIO_meth_set_gets(newmeth, BIO_meth_get_gets(meth));
BIO_meth_set_puts(newmeth, BIO_meth_get_puts(meth));
#else
if (!(newmeth = (BIO_METHOD*)OPENSSL_malloc(sizeof(BIO_METHOD)))) {
return nullptr;
}
memcpy(newmeth, BIO_s_socket(), sizeof(BIO_METHOD));
#endif
return BioMethodUniquePtr(newmeth);
}
bool OpenSSLUtils::setCustomBioReadMethod(
BIO_METHOD* bioMeth,
int (*meth)(BIO*, char*, int)) {
bool ret = false;
ret = (BIO_meth_set_read(bioMeth, meth) == 1);
return ret;
}
bool OpenSSLUtils::setCustomBioWriteMethod(
BIO_METHOD* bioMeth,
int (*meth)(BIO*, const char*, int)) {
bool ret = false;
ret = (BIO_meth_set_write(bioMeth, meth) == 1);
return ret;
}
int OpenSSLUtils::getBioShouldRetryWrite(int r) {
int ret = 0;
#ifdef OPENSSL_IS_BORINGSSL
ret = boringssl_bio_fd_should_retry(r);
#else
ret = BIO_sock_should_retry(r);
#endif
return ret;
}
void OpenSSLUtils::setBioAppData(BIO* b, void* ptr) {
#ifdef OPENSSL_IS_BORINGSSL
BIO_set_callback_arg(b, static_cast<char*>(ptr));
#else
BIO_set_app_data(b, ptr);
#endif
}
void* OpenSSLUtils::getBioAppData(BIO* b) {
#ifdef OPENSSL_IS_BORINGSSL
return BIO_get_callback_arg(b);
#else
return BIO_get_app_data(b);
#endif
}
NetworkSocket OpenSSLUtils::getBioFd(BIO* b) {
auto ret = BIO_get_fd(b, nullptr);
#ifdef _WIN32
return NetworkSocket((SOCKET)ret);
#else
return NetworkSocket(ret);
#endif
}
void OpenSSLUtils::setBioFd(BIO* b, NetworkSocket fd, int flags) {
#ifdef _WIN32
// Internally OpenSSL uses this as an int for reasons completely
// beyond any form of sanity, so we do the cast ourselves to avoid
// the warnings that would be generated.
int sock = int(fd.data);
#else
int sock = fd.toFd();
#endif
BIO_set_fd(b, sock, flags);
}
std::string OpenSSLUtils::getCommonName(X509* x509) {
if (x509 == nullptr) {
return "";
}
X509_NAME* subject = X509_get_subject_name(x509);
std::string cn;
cn.resize(ub_common_name);
X509_NAME_get_text_by_NID(
subject, NID_commonName, const_cast<char*>(cn.data()), ub_common_name);
return cn;
}
} // namespace ssl
} // namespace folly
namespace {
#ifdef OPENSSL_IS_BORINGSSL
static int boringssl_bio_fd_non_fatal_error(int err) {
if (
#ifdef EWOULDBLOCK
err == EWOULDBLOCK ||
#endif
#ifdef WSAEWOULDBLOCK
err == WSAEWOULDBLOCK ||
#endif
#ifdef ENOTCONN
err == ENOTCONN ||
#endif
#ifdef EINTR
err == EINTR ||
#endif
#ifdef EAGAIN
err == EAGAIN ||
#endif
#ifdef EPROTO
err == EPROTO ||
#endif
#ifdef EINPROGRESS
err == EINPROGRESS ||
#endif
#ifdef EALREADY
err == EALREADY ||
#endif
0) {
return 1;
}
return 0;
}
#if defined(OPENSSL_WINDOWS)
int boringssl_bio_fd_should_retry(int i) {
if (i == -1) {
return boringssl_bio_fd_non_fatal_error((int)GetLastError());
}
return 0;
}
#else // !OPENSSL_WINDOWS
int boringssl_bio_fd_should_retry(int i) {
if (i == -1) {
return boringssl_bio_fd_non_fatal_error(errno);
}
return 0;
}
#endif // OPENSSL_WINDOWS
#endif // OEPNSSL_IS_BORINGSSL
} // namespace