Make SslCtx public.
This commit is contained in:
parent
32456872af
commit
28d1dd02e9
@ -35,7 +35,7 @@
|
||||
#include "td/mtproto/TransportType.h"
|
||||
|
||||
#if !TD_EMSCRIPTEN //FIXME
|
||||
#include "td/net/SslStream.h"
|
||||
#include "td/net/SslCtx.h"
|
||||
#include "td/net/Wget.h"
|
||||
#endif
|
||||
|
||||
@ -239,7 +239,7 @@ static ActorOwn<> get_simple_config_impl(Promise<SimpleConfigResult> promise, in
|
||||
return std::move(res);
|
||||
}());
|
||||
}),
|
||||
std::move(url), std::move(headers), timeout, ttl, prefer_ipv6, SslStream::VerifyPeer::Off, std::move(content),
|
||||
std::move(url), std::move(headers), timeout, ttl, prefer_ipv6, SslCtx::VerifyPeer::Off, std::move(content),
|
||||
std::move(content_type)));
|
||||
#endif
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ set(TDNET_SOURCE
|
||||
td/net/HttpQuery.cpp
|
||||
td/net/HttpReader.cpp
|
||||
td/net/Socks5.cpp
|
||||
td/net/SslCtx.cpp
|
||||
td/net/SslStream.cpp
|
||||
td/net/TcpListener.cpp
|
||||
td/net/TransparentProxy.cpp
|
||||
@ -42,6 +43,7 @@ set(TDNET_SOURCE
|
||||
td/net/HttpReader.h
|
||||
td/net/NetStats.h
|
||||
td/net/Socks5.h
|
||||
td/net/SslCtx.h
|
||||
td/net/SslStream.h
|
||||
td/net/TcpListener.h
|
||||
td/net/TransparentProxy.h
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include "td/net/GetHostByNameActor.h"
|
||||
|
||||
#include "td/net/HttpQuery.h"
|
||||
#include "td/net/SslStream.h"
|
||||
#include "td/net/SslCtx.h"
|
||||
#include "td/net/Wget.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
@ -51,7 +51,7 @@ class GoogleDnsResolver final : public Actor {
|
||||
"GoogleDnsResolver", std::move(wget_promise),
|
||||
PSTRING() << "https://dns.google/resolve?name=" << url_encode(host_) << "&type=" << (prefer_ipv6_ ? 28 : 1),
|
||||
std::vector<std::pair<string, string>>({{"Host", "dns.google"}}), timeout, ttl, prefer_ipv6_,
|
||||
SslStream::VerifyPeer::Off);
|
||||
SslCtx::VerifyPeer::Off);
|
||||
}
|
||||
|
||||
static Result<IPAddress> get_ip_address(Result<unique_ptr<HttpQuery>> r_http_query) {
|
||||
|
311
tdnet/td/net/SslCtx.cpp
Normal file
311
tdnet/td/net/SslCtx.cpp
Normal file
@ -0,0 +1,311 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/net/SslStream.h"
|
||||
|
||||
#include "td/utils/crypto.h"
|
||||
#include "td/utils/FlatHashMap.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/wstring_convert.h"
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
#include "td/utils/Time.h"
|
||||
|
||||
#if !TD_EMSCRIPTEN
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/x509.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#if TD_PORT_WINDOWS
|
||||
#include <wincrypt.h>
|
||||
#endif
|
||||
|
||||
namespace td {
|
||||
|
||||
namespace detail {
|
||||
namespace {
|
||||
int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
|
||||
if (!preverify_ok) {
|
||||
char buf[256];
|
||||
X509_NAME_oneline(X509_get_subject_name(X509_STORE_CTX_get_current_cert(ctx)), buf, 256);
|
||||
|
||||
int err = X509_STORE_CTX_get_error(ctx);
|
||||
auto warning = PSTRING() << "verify error:num=" << err << ":" << X509_verify_cert_error_string(err)
|
||||
<< ":depth=" << X509_STORE_CTX_get_error_depth(ctx) << ":" << Slice(buf, std::strlen(buf));
|
||||
double now = Time::now();
|
||||
|
||||
static std::mutex warning_mutex;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(warning_mutex);
|
||||
static FlatHashMap<string, double> next_warning_time;
|
||||
double &next = next_warning_time[warning];
|
||||
if (next <= now) {
|
||||
next = now + 300; // one warning per 5 minutes
|
||||
LOG(WARNING) << warning;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return preverify_ok;
|
||||
}
|
||||
|
||||
using SslCtxPtr = std::shared_ptr<SSL_CTX>;
|
||||
|
||||
Result<SslCtxPtr> do_create_ssl_ctx(CSlice cert_file, SslCtx::VerifyPeer verify_peer) {
|
||||
auto ssl_method =
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
TLS_client_method();
|
||||
#else
|
||||
SSLv23_client_method();
|
||||
#endif
|
||||
if (ssl_method == nullptr) {
|
||||
return create_openssl_error(-6, "Failed to create an SSL client method");
|
||||
}
|
||||
auto ssl_ctx = SSL_CTX_new(ssl_method);
|
||||
if (!ssl_ctx) {
|
||||
return create_openssl_error(-7, "Failed to create an SSL context");
|
||||
}
|
||||
auto ssl_ctx_ptr = SslCtxPtr(ssl_ctx, SSL_CTX_free);
|
||||
long options = 0;
|
||||
#ifdef SSL_OP_NO_SSLv2
|
||||
options |= SSL_OP_NO_SSLv2;
|
||||
#endif
|
||||
#ifdef SSL_OP_NO_SSLv3
|
||||
options |= SSL_OP_NO_SSLv3;
|
||||
#endif
|
||||
SSL_CTX_set_options(ssl_ctx, options);
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION);
|
||||
#endif
|
||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE);
|
||||
|
||||
if (cert_file.empty()) {
|
||||
#if TD_PORT_WINDOWS
|
||||
LOG(DEBUG) << "Begin to load system store";
|
||||
auto flags = CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG | CERT_SYSTEM_STORE_CURRENT_USER;
|
||||
HCERTSTORE system_store =
|
||||
CertOpenStore(CERT_STORE_PROV_SYSTEM_W, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, HCRYPTPROV_LEGACY(), flags,
|
||||
static_cast<const void *>(to_wstring("ROOT").ok().c_str()));
|
||||
|
||||
if (system_store) {
|
||||
X509_STORE *store = X509_STORE_new();
|
||||
|
||||
for (PCCERT_CONTEXT cert_context = CertEnumCertificatesInStore(system_store, nullptr); cert_context != nullptr;
|
||||
cert_context = CertEnumCertificatesInStore(system_store, cert_context)) {
|
||||
const unsigned char *in = cert_context->pbCertEncoded;
|
||||
X509 *x509 = d2i_X509(nullptr, &in, static_cast<long>(cert_context->cbCertEncoded));
|
||||
if (x509 != nullptr) {
|
||||
if (X509_STORE_add_cert(store, x509) != 1) {
|
||||
auto error_code = ERR_peek_error();
|
||||
auto error = create_openssl_error(-20, "Failed to add certificate");
|
||||
if (ERR_GET_REASON(error_code) != X509_R_CERT_ALREADY_IN_HASH_TABLE) {
|
||||
LOG(ERROR) << error;
|
||||
} else {
|
||||
LOG(INFO) << error;
|
||||
}
|
||||
}
|
||||
|
||||
X509_free(x509);
|
||||
} else {
|
||||
LOG(ERROR) << create_openssl_error(-21, "Failed to load X509 certificate");
|
||||
}
|
||||
}
|
||||
|
||||
CertCloseStore(system_store, 0);
|
||||
|
||||
SSL_CTX_set_cert_store(ssl_ctx, store);
|
||||
LOG(DEBUG) << "End to load system store";
|
||||
} else {
|
||||
LOG(ERROR) << create_openssl_error(-22, "Failed to open system certificate store");
|
||||
}
|
||||
#else
|
||||
if (SSL_CTX_set_default_verify_paths(ssl_ctx) == 0) {
|
||||
auto error = create_openssl_error(-8, "Failed to load default verify paths");
|
||||
if (verify_peer == SslCtx::VerifyPeer::On) {
|
||||
return std::move(error);
|
||||
} else {
|
||||
LOG(ERROR) << error;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
if (SSL_CTX_load_verify_locations(ssl_ctx, cert_file.c_str(), nullptr) == 0) {
|
||||
return create_openssl_error(-8, "Failed to set custom certificate file");
|
||||
}
|
||||
}
|
||||
|
||||
if (verify_peer == SslCtx::VerifyPeer::On) {
|
||||
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, verify_callback);
|
||||
|
||||
constexpr int DEFAULT_VERIFY_DEPTH = 10;
|
||||
SSL_CTX_set_verify_depth(ssl_ctx, DEFAULT_VERIFY_DEPTH);
|
||||
} else {
|
||||
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, nullptr);
|
||||
}
|
||||
|
||||
string cipher_list;
|
||||
if (SSL_CTX_set_cipher_list(ssl_ctx, cipher_list.empty() ? "DEFAULT" : cipher_list.c_str()) == 0) {
|
||||
return create_openssl_error(-9, PSLICE() << "Failed to set cipher list \"" << cipher_list << '"');
|
||||
}
|
||||
|
||||
return std::move(ssl_ctx_ptr);
|
||||
}
|
||||
|
||||
Result<SslCtxPtr> get_default_ssl_ctx() {
|
||||
static auto ctx = do_create_ssl_ctx(CSlice(), SslCtx::VerifyPeer::On);
|
||||
if (ctx.is_error()) {
|
||||
return ctx.error().clone();
|
||||
}
|
||||
|
||||
return ctx.ok();
|
||||
}
|
||||
|
||||
Result<SslCtxPtr> get_default_unverified_ssl_ctx() {
|
||||
static auto ctx = do_create_ssl_ctx(CSlice(), SslCtx::VerifyPeer::Off);
|
||||
if (ctx.is_error()) {
|
||||
return ctx.error().clone();
|
||||
}
|
||||
|
||||
return ctx.ok();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class SslCtxImpl {
|
||||
public:
|
||||
Status init(CSlice cert_file, SslCtx::VerifyPeer verify_peer) {
|
||||
SslCtx::init_openssl();
|
||||
|
||||
clear_openssl_errors("Before SslCtx::init");
|
||||
|
||||
if (cert_file.empty()) {
|
||||
if (verify_peer == SslCtx::VerifyPeer::On) {
|
||||
TRY_RESULT_ASSIGN(ssl_ctx_ptr_, get_default_ssl_ctx());
|
||||
} else {
|
||||
TRY_RESULT_ASSIGN(ssl_ctx_ptr_, get_default_unverified_ssl_ctx());
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
auto start_time = Time::now();
|
||||
auto r_ssl_ctx_ptr = do_create_ssl_ctx(cert_file, verify_peer);
|
||||
auto elapsed_time = Time::now() - start_time;
|
||||
if (elapsed_time >= 0.1) {
|
||||
LOG(ERROR) << "SSL context creation took " << elapsed_time << " seconds";
|
||||
}
|
||||
if (r_ssl_ctx_ptr.is_error()) {
|
||||
return r_ssl_ctx_ptr.move_as_error();
|
||||
}
|
||||
ssl_ctx_ptr_ = r_ssl_ctx_ptr.move_as_ok();
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void *get_openssl_ctx() const {
|
||||
return static_cast<void *>(ssl_ctx_ptr_.get());
|
||||
}
|
||||
|
||||
private:
|
||||
SslCtxPtr ssl_ctx_ptr_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SslCtx::SslCtx() = default;
|
||||
|
||||
SslCtx::SslCtx(const SslCtx &other) {
|
||||
if (other.impl_) {
|
||||
impl_ = make_unique<detail::SslCtxImpl>(*other.impl_);
|
||||
}
|
||||
}
|
||||
|
||||
SslCtx &SslCtx::operator=(const SslCtx &other) {
|
||||
if (other.impl_) {
|
||||
impl_ = make_unique<detail::SslCtxImpl>(*other.impl_);
|
||||
} else {
|
||||
impl_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
SslCtx::SslCtx(SslCtx &&) noexcept = default;
|
||||
|
||||
SslCtx &SslCtx::operator=(SslCtx &&) noexcept = default;
|
||||
|
||||
SslCtx::~SslCtx() = default;
|
||||
|
||||
void SslCtx::init_openssl() {
|
||||
static bool is_inited = [] {
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
return OPENSSL_init_ssl(0, nullptr) != 0;
|
||||
#else
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
return OpenSSL_add_ssl_algorithms() != 0;
|
||||
#endif
|
||||
}();
|
||||
CHECK(is_inited);
|
||||
}
|
||||
|
||||
Result<SslCtx> SslCtx::create(CSlice cert_file, VerifyPeer verify_peer) {
|
||||
auto impl = make_unique<detail::SslCtxImpl>();
|
||||
TRY_STATUS(impl->init(cert_file, verify_peer));
|
||||
return SslCtx(std::move(impl));
|
||||
}
|
||||
|
||||
void *SslCtx::get_openssl_ctx() const {
|
||||
return impl_ == nullptr ? nullptr : impl_->get_openssl_ctx();
|
||||
}
|
||||
|
||||
SslCtx::SslCtx(unique_ptr<detail::SslCtxImpl> impl) : impl_(std::move(impl)) {
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
||||
#else
|
||||
|
||||
namespace td {
|
||||
|
||||
namespace detail {
|
||||
class SslCtxImpl {};
|
||||
} // namespace detail
|
||||
|
||||
SslCtx::SslCtx() = default;
|
||||
|
||||
SslCtx::SslCtx(const SslCtx &other) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
SslCtx &SslCtx::operator=(const SslCtx &other) {
|
||||
UNREACHABLE();
|
||||
return *this;
|
||||
}
|
||||
|
||||
SslCtx::SslCtx(SslCtx &&) noexcept = default;
|
||||
|
||||
SslCtx &SslCtx::operator=(SslCtx &&) noexcept = default;
|
||||
|
||||
SslCtx::~SslCtx() = default;
|
||||
|
||||
void SslCtx::init_openssl() {
|
||||
}
|
||||
|
||||
Result<SslCtx> SslCtx::create(CSlice cert_file, VerifyPeer verify_peer) {
|
||||
return Status::Error("Not supported in Emscripten");
|
||||
}
|
||||
|
||||
void *SslCtx::get_openssl_ctx() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SslCtx::SslCtx(unique_ptr<detail::SslCtxImpl> impl) : impl_(std::move(impl)) {
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
||||
#endif
|
45
tdnet/td/net/SslCtx.h
Normal file
45
tdnet/td/net/SslCtx.h
Normal file
@ -0,0 +1,45 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
namespace detail {
|
||||
class SslCtxImpl;
|
||||
} // namespace detail
|
||||
|
||||
class SslCtx {
|
||||
public:
|
||||
SslCtx();
|
||||
SslCtx(const SslCtx &other);
|
||||
SslCtx &operator=(const SslCtx &other);
|
||||
SslCtx(SslCtx &&) noexcept;
|
||||
SslCtx &operator=(SslCtx &&) noexcept;
|
||||
~SslCtx();
|
||||
|
||||
static void init_openssl();
|
||||
|
||||
enum class VerifyPeer { On, Off };
|
||||
|
||||
static Result<SslCtx> create(CSlice cert_file, VerifyPeer verify_peer);
|
||||
|
||||
void *get_openssl_ctx() const;
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
return static_cast<bool>(impl_);
|
||||
}
|
||||
|
||||
private:
|
||||
unique_ptr<detail::SslCtxImpl> impl_;
|
||||
|
||||
explicit SslCtx(unique_ptr<detail::SslCtxImpl> impl);
|
||||
};
|
||||
|
||||
} // namespace td
|
@ -9,28 +9,18 @@
|
||||
#if !TD_EMSCRIPTEN
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/crypto.h"
|
||||
#include "td/utils/FlatHashMap.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/IPAddress.h"
|
||||
#include "td/utils/port/wstring_convert.h"
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/Time.h"
|
||||
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509v3.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#if TD_PORT_WINDOWS
|
||||
#include <wincrypt.h>
|
||||
#endif
|
||||
|
||||
namespace td {
|
||||
|
||||
@ -122,33 +112,6 @@ BIO_METHOD *BIO_s_sslstream() {
|
||||
return result;
|
||||
}
|
||||
|
||||
int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
|
||||
if (!preverify_ok) {
|
||||
char buf[256];
|
||||
X509_NAME_oneline(X509_get_subject_name(X509_STORE_CTX_get_current_cert(ctx)), buf, 256);
|
||||
|
||||
int err = X509_STORE_CTX_get_error(ctx);
|
||||
auto warning = PSTRING() << "verify error:num=" << err << ":" << X509_verify_cert_error_string(err)
|
||||
<< ":depth=" << X509_STORE_CTX_get_error_depth(ctx) << ":" << Slice(buf, std::strlen(buf));
|
||||
double now = Time::now();
|
||||
|
||||
static std::mutex warning_mutex;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(warning_mutex);
|
||||
static FlatHashMap<string, double> next_warning_time;
|
||||
double &next = next_warning_time[warning];
|
||||
if (next <= now) {
|
||||
next = now + 300; // one warning per 5 minutes
|
||||
LOG(WARNING) << warning;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return preverify_ok;
|
||||
}
|
||||
|
||||
using SslCtx = std::shared_ptr<SSL_CTX>;
|
||||
|
||||
struct SslHandleDeleter {
|
||||
void operator()(SSL *ssl_handle) {
|
||||
auto start_time = Time::now();
|
||||
@ -168,162 +131,18 @@ struct SslHandleDeleter {
|
||||
|
||||
using SslHandle = std::unique_ptr<SSL, SslHandleDeleter>;
|
||||
|
||||
Result<SslCtx> do_create_ssl_ctx(CSlice cert_file, SslStream::VerifyPeer verify_peer) {
|
||||
auto ssl_method =
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
TLS_client_method();
|
||||
#else
|
||||
SSLv23_client_method();
|
||||
#endif
|
||||
if (ssl_method == nullptr) {
|
||||
return create_openssl_error(-6, "Failed to create an SSL client method");
|
||||
}
|
||||
auto ssl_ctx = SSL_CTX_new(ssl_method);
|
||||
if (!ssl_ctx) {
|
||||
return create_openssl_error(-7, "Failed to create an SSL context");
|
||||
}
|
||||
auto ssl_ctx_ptr = SslCtx(ssl_ctx, SSL_CTX_free);
|
||||
long options = 0;
|
||||
#ifdef SSL_OP_NO_SSLv2
|
||||
options |= SSL_OP_NO_SSLv2;
|
||||
#endif
|
||||
#ifdef SSL_OP_NO_SSLv3
|
||||
options |= SSL_OP_NO_SSLv3;
|
||||
#endif
|
||||
SSL_CTX_set_options(ssl_ctx, options);
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION);
|
||||
#endif
|
||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE);
|
||||
|
||||
if (cert_file.empty()) {
|
||||
#if TD_PORT_WINDOWS
|
||||
LOG(DEBUG) << "Begin to load system store";
|
||||
auto flags = CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG | CERT_SYSTEM_STORE_CURRENT_USER;
|
||||
HCERTSTORE system_store =
|
||||
CertOpenStore(CERT_STORE_PROV_SYSTEM_W, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, HCRYPTPROV_LEGACY(), flags,
|
||||
static_cast<const void *>(to_wstring("ROOT").ok().c_str()));
|
||||
|
||||
if (system_store) {
|
||||
X509_STORE *store = X509_STORE_new();
|
||||
|
||||
for (PCCERT_CONTEXT cert_context = CertEnumCertificatesInStore(system_store, nullptr); cert_context != nullptr;
|
||||
cert_context = CertEnumCertificatesInStore(system_store, cert_context)) {
|
||||
const unsigned char *in = cert_context->pbCertEncoded;
|
||||
X509 *x509 = d2i_X509(nullptr, &in, static_cast<long>(cert_context->cbCertEncoded));
|
||||
if (x509 != nullptr) {
|
||||
if (X509_STORE_add_cert(store, x509) != 1) {
|
||||
auto error_code = ERR_peek_error();
|
||||
auto error = create_openssl_error(-20, "Failed to add certificate");
|
||||
if (ERR_GET_REASON(error_code) != X509_R_CERT_ALREADY_IN_HASH_TABLE) {
|
||||
LOG(ERROR) << error;
|
||||
} else {
|
||||
LOG(INFO) << error;
|
||||
}
|
||||
}
|
||||
|
||||
X509_free(x509);
|
||||
} else {
|
||||
LOG(ERROR) << create_openssl_error(-21, "Failed to load X509 certificate");
|
||||
}
|
||||
}
|
||||
|
||||
CertCloseStore(system_store, 0);
|
||||
|
||||
SSL_CTX_set_cert_store(ssl_ctx, store);
|
||||
LOG(DEBUG) << "End to load system store";
|
||||
} else {
|
||||
LOG(ERROR) << create_openssl_error(-22, "Failed to open system certificate store");
|
||||
}
|
||||
#else
|
||||
if (SSL_CTX_set_default_verify_paths(ssl_ctx) == 0) {
|
||||
auto error = create_openssl_error(-8, "Failed to load default verify paths");
|
||||
if (verify_peer == SslStream::VerifyPeer::On) {
|
||||
return std::move(error);
|
||||
} else {
|
||||
LOG(ERROR) << error;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
if (SSL_CTX_load_verify_locations(ssl_ctx, cert_file.c_str(), nullptr) == 0) {
|
||||
return create_openssl_error(-8, "Failed to set custom certificate file");
|
||||
}
|
||||
}
|
||||
|
||||
if (verify_peer == SslStream::VerifyPeer::On) {
|
||||
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, verify_callback);
|
||||
|
||||
constexpr int DEFAULT_VERIFY_DEPTH = 10;
|
||||
SSL_CTX_set_verify_depth(ssl_ctx, DEFAULT_VERIFY_DEPTH);
|
||||
} else {
|
||||
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, nullptr);
|
||||
}
|
||||
|
||||
string cipher_list;
|
||||
if (SSL_CTX_set_cipher_list(ssl_ctx, cipher_list.empty() ? "DEFAULT" : cipher_list.c_str()) == 0) {
|
||||
return create_openssl_error(-9, PSLICE() << "Failed to set cipher list \"" << cipher_list << '"');
|
||||
}
|
||||
|
||||
return std::move(ssl_ctx_ptr);
|
||||
}
|
||||
|
||||
Result<SslCtx> get_default_ssl_ctx() {
|
||||
static auto ctx = do_create_ssl_ctx("", SslStream::VerifyPeer::On);
|
||||
if (ctx.is_error()) {
|
||||
return ctx.error().clone();
|
||||
}
|
||||
|
||||
return ctx.ok();
|
||||
}
|
||||
|
||||
Result<SslCtx> get_default_unverified_ssl_ctx() {
|
||||
static auto ctx = do_create_ssl_ctx("", SslStream::VerifyPeer::Off);
|
||||
if (ctx.is_error()) {
|
||||
return ctx.error().clone();
|
||||
}
|
||||
|
||||
return ctx.ok();
|
||||
}
|
||||
|
||||
Result<SslCtx> create_ssl_ctx(CSlice cert_file, SslStream::VerifyPeer verify_peer) {
|
||||
if (cert_file.empty()) {
|
||||
if (verify_peer == SslStream::VerifyPeer::On) {
|
||||
return get_default_ssl_ctx();
|
||||
} else {
|
||||
return get_default_unverified_ssl_ctx();
|
||||
}
|
||||
}
|
||||
auto start_time = Time::now();
|
||||
auto result = do_create_ssl_ctx(cert_file, verify_peer);
|
||||
auto elapsed_time = Time::now() - start_time;
|
||||
if (elapsed_time >= 0.1) {
|
||||
LOG(ERROR) << "SSL context creation took " << elapsed_time << " seconds";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class SslStreamImpl {
|
||||
public:
|
||||
Status init(CSlice host, CSlice cert_file, SslStream::VerifyPeer verify_peer, bool check_ip_address_as_host) {
|
||||
static bool init_openssl = [] {
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
return OPENSSL_init_ssl(0, nullptr) != 0;
|
||||
#else
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
return OpenSSL_add_ssl_algorithms() != 0;
|
||||
#endif
|
||||
}();
|
||||
CHECK(init_openssl);
|
||||
Status init(CSlice host, CSlice cert_file, SslCtx::VerifyPeer verify_peer, bool check_ip_address_as_host) {
|
||||
SslCtx::init_openssl();
|
||||
|
||||
clear_openssl_errors("Before SslFd::init");
|
||||
|
||||
TRY_RESULT(ssl_ctx, create_ssl_ctx(cert_file, verify_peer));
|
||||
TRY_RESULT(ssl_ctx, SslCtx::create(cert_file, verify_peer));
|
||||
|
||||
auto ssl_handle = SslHandle(SSL_new(ssl_ctx.get()));
|
||||
auto ssl_handle = SslHandle(SSL_new(static_cast<SSL_CTX *>(ssl_ctx.get_openssl_ctx())));
|
||||
if (!ssl_handle) {
|
||||
return create_openssl_error(-13, "Failed to create an SSL handle");
|
||||
}
|
||||
@ -537,7 +356,7 @@ SslStream::SslStream(SslStream &&) noexcept = default;
|
||||
SslStream &SslStream::operator=(SslStream &&) noexcept = default;
|
||||
SslStream::~SslStream() = default;
|
||||
|
||||
Result<SslStream> SslStream::create(CSlice host, CSlice cert_file, VerifyPeer verify_peer,
|
||||
Result<SslStream> SslStream::create(CSlice host, CSlice cert_file, SslCtx::VerifyPeer verify_peer,
|
||||
bool use_ip_address_as_host) {
|
||||
auto impl = make_unique<detail::SslStreamImpl>();
|
||||
TRY_STATUS(impl->init(host, cert_file, verify_peer, use_ip_address_as_host));
|
||||
@ -569,13 +388,13 @@ class SslStreamImpl {};
|
||||
} // namespace detail
|
||||
|
||||
SslStream::SslStream() = default;
|
||||
SslStream::SslStream(SslStream &&) = default;
|
||||
SslStream &SslStream::operator=(SslStream &&) = default;
|
||||
SslStream::SslStream(SslStream &&) noexcept = default;
|
||||
SslStream &SslStream::operator=(SslStream &&) noexcept = default;
|
||||
SslStream::~SslStream() = default;
|
||||
|
||||
Result<SslStream> SslStream::create(CSlice host, CSlice cert_file, VerifyPeer verify_peer,
|
||||
Result<SslStream> SslStream::create(CSlice host, CSlice cert_file, SslCtx::VerifyPeer verify_peer,
|
||||
bool check_ip_address_as_host) {
|
||||
return Status::Error("Not supported in emscripten");
|
||||
return Status::Error("Not supported in Emscripten");
|
||||
}
|
||||
|
||||
SslStream::SslStream(unique_ptr<detail::SslStreamImpl> impl) : impl_(std::move(impl)) {
|
||||
|
@ -6,6 +6,8 @@
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "td/net/SslCtx.h"
|
||||
|
||||
#include "td/utils/ByteFlow.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Status.h"
|
||||
@ -23,9 +25,8 @@ class SslStream {
|
||||
SslStream &operator=(SslStream &&) noexcept;
|
||||
~SslStream();
|
||||
|
||||
enum class VerifyPeer { On, Off };
|
||||
|
||||
static Result<SslStream> create(CSlice host, CSlice cert_file = CSlice(), VerifyPeer verify_peer = VerifyPeer::On,
|
||||
static Result<SslStream> create(CSlice host, CSlice cert_file = CSlice(),
|
||||
SslCtx::VerifyPeer verify_peer = SslCtx::VerifyPeer::On,
|
||||
bool use_ip_address_as_host = false);
|
||||
|
||||
ByteFlowInterface &read_byte_flow();
|
||||
|
@ -25,7 +25,7 @@
|
||||
namespace td {
|
||||
|
||||
Wget::Wget(Promise<unique_ptr<HttpQuery>> promise, string url, std::vector<std::pair<string, string>> headers,
|
||||
int32 timeout_in, int32 ttl, bool prefer_ipv6, SslStream::VerifyPeer verify_peer, string content,
|
||||
int32 timeout_in, int32 ttl, bool prefer_ipv6, SslCtx::VerifyPeer verify_peer, string content,
|
||||
string content_type)
|
||||
: promise_(std::move(promise))
|
||||
, input_url_(std::move(url))
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#include "td/net/HttpOutboundConnection.h"
|
||||
#include "td/net/HttpQuery.h"
|
||||
#include "td/net/SslStream.h"
|
||||
#include "td/net/SslCtx.h"
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
|
||||
@ -24,8 +24,7 @@ class Wget final : public HttpOutboundConnection::Callback {
|
||||
public:
|
||||
explicit Wget(Promise<unique_ptr<HttpQuery>> promise, string url, std::vector<std::pair<string, string>> headers = {},
|
||||
int32 timeout_in = 10, int32 ttl = 3, bool prefer_ipv6 = false,
|
||||
SslStream::VerifyPeer verify_peer = SslStream::VerifyPeer::On, string content = {},
|
||||
string content_type = {});
|
||||
SslCtx::VerifyPeer verify_peer = SslCtx::VerifyPeer::On, string content = {}, string content_type = {});
|
||||
|
||||
private:
|
||||
Status try_init();
|
||||
@ -46,7 +45,7 @@ class Wget final : public HttpOutboundConnection::Callback {
|
||||
int32 timeout_in_;
|
||||
int32 ttl_;
|
||||
bool prefer_ipv6_ = false;
|
||||
SslStream::VerifyPeer verify_peer_;
|
||||
SslCtx::VerifyPeer verify_peer_;
|
||||
string content_;
|
||||
string content_type_;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user