Make SslCtx public.
This commit is contained in:
parent
32456872af
commit
28d1dd02e9
@ -35,7 +35,7 @@
|
|||||||
#include "td/mtproto/TransportType.h"
|
#include "td/mtproto/TransportType.h"
|
||||||
|
|
||||||
#if !TD_EMSCRIPTEN //FIXME
|
#if !TD_EMSCRIPTEN //FIXME
|
||||||
#include "td/net/SslStream.h"
|
#include "td/net/SslCtx.h"
|
||||||
#include "td/net/Wget.h"
|
#include "td/net/Wget.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ static ActorOwn<> get_simple_config_impl(Promise<SimpleConfigResult> promise, in
|
|||||||
return std::move(res);
|
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)));
|
std::move(content_type)));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ set(TDNET_SOURCE
|
|||||||
td/net/HttpQuery.cpp
|
td/net/HttpQuery.cpp
|
||||||
td/net/HttpReader.cpp
|
td/net/HttpReader.cpp
|
||||||
td/net/Socks5.cpp
|
td/net/Socks5.cpp
|
||||||
|
td/net/SslCtx.cpp
|
||||||
td/net/SslStream.cpp
|
td/net/SslStream.cpp
|
||||||
td/net/TcpListener.cpp
|
td/net/TcpListener.cpp
|
||||||
td/net/TransparentProxy.cpp
|
td/net/TransparentProxy.cpp
|
||||||
@ -42,6 +43,7 @@ set(TDNET_SOURCE
|
|||||||
td/net/HttpReader.h
|
td/net/HttpReader.h
|
||||||
td/net/NetStats.h
|
td/net/NetStats.h
|
||||||
td/net/Socks5.h
|
td/net/Socks5.h
|
||||||
|
td/net/SslCtx.h
|
||||||
td/net/SslStream.h
|
td/net/SslStream.h
|
||||||
td/net/TcpListener.h
|
td/net/TcpListener.h
|
||||||
td/net/TransparentProxy.h
|
td/net/TransparentProxy.h
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#include "td/net/GetHostByNameActor.h"
|
#include "td/net/GetHostByNameActor.h"
|
||||||
|
|
||||||
#include "td/net/HttpQuery.h"
|
#include "td/net/HttpQuery.h"
|
||||||
#include "td/net/SslStream.h"
|
#include "td/net/SslCtx.h"
|
||||||
#include "td/net/Wget.h"
|
#include "td/net/Wget.h"
|
||||||
|
|
||||||
#include "td/utils/common.h"
|
#include "td/utils/common.h"
|
||||||
@ -51,7 +51,7 @@ class GoogleDnsResolver final : public Actor {
|
|||||||
"GoogleDnsResolver", std::move(wget_promise),
|
"GoogleDnsResolver", std::move(wget_promise),
|
||||||
PSTRING() << "https://dns.google/resolve?name=" << url_encode(host_) << "&type=" << (prefer_ipv6_ ? 28 : 1),
|
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_,
|
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) {
|
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
|
#if !TD_EMSCRIPTEN
|
||||||
#include "td/utils/common.h"
|
#include "td/utils/common.h"
|
||||||
#include "td/utils/crypto.h"
|
#include "td/utils/crypto.h"
|
||||||
#include "td/utils/FlatHashMap.h"
|
|
||||||
#include "td/utils/logging.h"
|
#include "td/utils/logging.h"
|
||||||
#include "td/utils/misc.h"
|
#include "td/utils/misc.h"
|
||||||
#include "td/utils/port/IPAddress.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/Status.h"
|
||||||
#include "td/utils/Time.h"
|
#include "td/utils/Time.h"
|
||||||
|
|
||||||
|
#include <openssl/bio.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <openssl/evp.h>
|
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
#include <openssl/x509v3.h>
|
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
#if TD_PORT_WINDOWS
|
|
||||||
#include <wincrypt.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace td {
|
namespace td {
|
||||||
|
|
||||||
@ -122,33 +112,6 @@ BIO_METHOD *BIO_s_sslstream() {
|
|||||||
return result;
|
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 {
|
struct SslHandleDeleter {
|
||||||
void operator()(SSL *ssl_handle) {
|
void operator()(SSL *ssl_handle) {
|
||||||
auto start_time = Time::now();
|
auto start_time = Time::now();
|
||||||
@ -168,162 +131,18 @@ struct SslHandleDeleter {
|
|||||||
|
|
||||||
using SslHandle = std::unique_ptr<SSL, 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
|
} // namespace
|
||||||
|
|
||||||
class SslStreamImpl {
|
class SslStreamImpl {
|
||||||
public:
|
public:
|
||||||
Status init(CSlice host, CSlice cert_file, SslStream::VerifyPeer verify_peer, bool check_ip_address_as_host) {
|
Status init(CSlice host, CSlice cert_file, SslCtx::VerifyPeer verify_peer, bool check_ip_address_as_host) {
|
||||||
static bool init_openssl = [] {
|
SslCtx::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);
|
|
||||||
|
|
||||||
clear_openssl_errors("Before SslFd::init");
|
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) {
|
if (!ssl_handle) {
|
||||||
return create_openssl_error(-13, "Failed to create an 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::operator=(SslStream &&) noexcept = default;
|
||||||
SslStream::~SslStream() = 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) {
|
bool use_ip_address_as_host) {
|
||||||
auto impl = make_unique<detail::SslStreamImpl>();
|
auto impl = make_unique<detail::SslStreamImpl>();
|
||||||
TRY_STATUS(impl->init(host, cert_file, verify_peer, use_ip_address_as_host));
|
TRY_STATUS(impl->init(host, cert_file, verify_peer, use_ip_address_as_host));
|
||||||
@ -569,13 +388,13 @@ class SslStreamImpl {};
|
|||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
SslStream::SslStream() = default;
|
SslStream::SslStream() = default;
|
||||||
SslStream::SslStream(SslStream &&) = default;
|
SslStream::SslStream(SslStream &&) noexcept = default;
|
||||||
SslStream &SslStream::operator=(SslStream &&) = default;
|
SslStream &SslStream::operator=(SslStream &&) noexcept = default;
|
||||||
SslStream::~SslStream() = 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) {
|
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)) {
|
SslStream::SslStream(unique_ptr<detail::SslStreamImpl> impl) : impl_(std::move(impl)) {
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "td/net/SslCtx.h"
|
||||||
|
|
||||||
#include "td/utils/ByteFlow.h"
|
#include "td/utils/ByteFlow.h"
|
||||||
#include "td/utils/Slice.h"
|
#include "td/utils/Slice.h"
|
||||||
#include "td/utils/Status.h"
|
#include "td/utils/Status.h"
|
||||||
@ -23,9 +25,8 @@ class SslStream {
|
|||||||
SslStream &operator=(SslStream &&) noexcept;
|
SslStream &operator=(SslStream &&) noexcept;
|
||||||
~SslStream();
|
~SslStream();
|
||||||
|
|
||||||
enum class VerifyPeer { On, Off };
|
static Result<SslStream> create(CSlice host, CSlice cert_file = CSlice(),
|
||||||
|
SslCtx::VerifyPeer verify_peer = SslCtx::VerifyPeer::On,
|
||||||
static Result<SslStream> create(CSlice host, CSlice cert_file = CSlice(), VerifyPeer verify_peer = VerifyPeer::On,
|
|
||||||
bool use_ip_address_as_host = false);
|
bool use_ip_address_as_host = false);
|
||||||
|
|
||||||
ByteFlowInterface &read_byte_flow();
|
ByteFlowInterface &read_byte_flow();
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
namespace td {
|
namespace td {
|
||||||
|
|
||||||
Wget::Wget(Promise<unique_ptr<HttpQuery>> promise, string url, std::vector<std::pair<string, string>> headers,
|
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)
|
string content_type)
|
||||||
: promise_(std::move(promise))
|
: promise_(std::move(promise))
|
||||||
, input_url_(std::move(url))
|
, input_url_(std::move(url))
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include "td/net/HttpOutboundConnection.h"
|
#include "td/net/HttpOutboundConnection.h"
|
||||||
#include "td/net/HttpQuery.h"
|
#include "td/net/HttpQuery.h"
|
||||||
#include "td/net/SslStream.h"
|
#include "td/net/SslCtx.h"
|
||||||
|
|
||||||
#include "td/actor/actor.h"
|
#include "td/actor/actor.h"
|
||||||
|
|
||||||
@ -24,8 +24,7 @@ class Wget final : public HttpOutboundConnection::Callback {
|
|||||||
public:
|
public:
|
||||||
explicit Wget(Promise<unique_ptr<HttpQuery>> promise, string url, std::vector<std::pair<string, string>> headers = {},
|
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,
|
int32 timeout_in = 10, int32 ttl = 3, bool prefer_ipv6 = false,
|
||||||
SslStream::VerifyPeer verify_peer = SslStream::VerifyPeer::On, string content = {},
|
SslCtx::VerifyPeer verify_peer = SslCtx::VerifyPeer::On, string content = {}, string content_type = {});
|
||||||
string content_type = {});
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Status try_init();
|
Status try_init();
|
||||||
@ -46,7 +45,7 @@ class Wget final : public HttpOutboundConnection::Callback {
|
|||||||
int32 timeout_in_;
|
int32 timeout_in_;
|
||||||
int32 ttl_;
|
int32 ttl_;
|
||||||
bool prefer_ipv6_ = false;
|
bool prefer_ipv6_ = false;
|
||||||
SslStream::VerifyPeer verify_peer_;
|
SslCtx::VerifyPeer verify_peer_;
|
||||||
string content_;
|
string content_;
|
||||||
string content_type_;
|
string content_type_;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user