aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/openssl
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/openssl
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/openssl')
-rw-r--r--library/cpp/openssl/holders/bio.cpp29
-rw-r--r--library/cpp/openssl/holders/bio.h25
-rw-r--r--library/cpp/openssl/holders/bn.h13
-rw-r--r--library/cpp/openssl/holders/evp.h13
-rw-r--r--library/cpp/openssl/holders/hmac.h10
-rw-r--r--library/cpp/openssl/holders/holder.h32
-rw-r--r--library/cpp/openssl/holders/ut/evp_ut.cpp15
-rw-r--r--library/cpp/openssl/holders/ut/hmac_ut.cpp10
-rw-r--r--library/cpp/openssl/holders/ut/ya.make10
-rw-r--r--library/cpp/openssl/holders/x509_vfy.cpp30
-rw-r--r--library/cpp/openssl/holders/x509_vfy.h25
-rw-r--r--library/cpp/openssl/holders/ya.make16
-rw-r--r--library/cpp/openssl/init/init.cpp66
-rw-r--r--library/cpp/openssl/init/init.h3
-rw-r--r--library/cpp/openssl/init/ya.make13
-rw-r--r--library/cpp/openssl/io/stream.cpp329
-rw-r--r--library/cpp/openssl/io/stream.h50
-rw-r--r--library/cpp/openssl/io/ut/builtin_ut.cpp9
-rw-r--r--library/cpp/openssl/io/ut/ya.make12
-rw-r--r--library/cpp/openssl/io/ya.make16
-rw-r--r--library/cpp/openssl/method/io.cpp122
-rw-r--r--library/cpp/openssl/method/io.h31
-rw-r--r--library/cpp/openssl/method/ut/io_ut.cpp52
-rw-r--r--library/cpp/openssl/method/ut/ya.make9
-rw-r--r--library/cpp/openssl/method/ya.make14
-rw-r--r--library/cpp/openssl/ya.make13
26 files changed, 967 insertions, 0 deletions
diff --git a/library/cpp/openssl/holders/bio.cpp b/library/cpp/openssl/holders/bio.cpp
new file mode 100644
index 00000000000..42cc5fc1ef7
--- /dev/null
+++ b/library/cpp/openssl/holders/bio.cpp
@@ -0,0 +1,29 @@
+#include "bio.h"
+
+namespace NOpenSSL {
+
+ TBioMethod::TBioMethod(
+ int type,
+ const char* name,
+ int (*write)(BIO*, const char*, int),
+ int (*read)(BIO*, char*, int),
+ int (*puts)(BIO*, const char*),
+ int (*gets)(BIO*, char*, int),
+ long (*ctrl)(BIO*, int, long, void*),
+ int (*create)(BIO*),
+ int (*destroy)(BIO*),
+ long (*callbackCtrl)(BIO*, int, bio_info_cb*)
+ )
+ : THolder(type, name)
+ {
+ BIO_meth_set_write(*this, write);
+ BIO_meth_set_read(*this, read);
+ BIO_meth_set_puts(*this, puts);
+ BIO_meth_set_gets(*this, gets);
+ BIO_meth_set_ctrl(*this, ctrl);
+ BIO_meth_set_create(*this, create);
+ BIO_meth_set_destroy(*this, destroy);
+ BIO_meth_set_callback_ctrl(*this, callbackCtrl);
+ }
+
+} // namespace NOpenSSL
diff --git a/library/cpp/openssl/holders/bio.h b/library/cpp/openssl/holders/bio.h
new file mode 100644
index 00000000000..bcd6a7a9d66
--- /dev/null
+++ b/library/cpp/openssl/holders/bio.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <contrib/libs/openssl/include/openssl/bio.h>
+
+#include <library/cpp/openssl/holders/holder.h>
+
+namespace NOpenSSL {
+
+class TBioMethod : public THolder<BIO_METHOD, BIO_meth_new, BIO_meth_free, int, const char*> {
+public:
+ TBioMethod(
+ int type,
+ const char* name,
+ int (*write)(BIO*, const char*, int),
+ int (*read)(BIO*, char*, int),
+ int (*puts)(BIO*, const char*),
+ int (*gets)(BIO*, char*, int),
+ long (*ctrl)(BIO*, int, long, void*),
+ int (*create)(BIO*),
+ int (*destroy)(BIO*),
+ long (*callbackCtrl)(BIO*, int, bio_info_cb*)
+ );
+};
+
+} // namespace NOpenSSL
diff --git a/library/cpp/openssl/holders/bn.h b/library/cpp/openssl/holders/bn.h
new file mode 100644
index 00000000000..9d133d4bdd8
--- /dev/null
+++ b/library/cpp/openssl/holders/bn.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "holder.h"
+
+#include <contrib/libs/openssl/include/openssl/bn.h>
+
+namespace NOpenSSL {
+ class TBignum : public THolder<BIGNUM, BN_new, BN_clear_free> {
+ };
+
+ class TBnCtx : public THolder<BN_CTX, BN_CTX_new, BN_CTX_free> {
+ };
+}
diff --git a/library/cpp/openssl/holders/evp.h b/library/cpp/openssl/holders/evp.h
new file mode 100644
index 00000000000..df3cc4c2fa5
--- /dev/null
+++ b/library/cpp/openssl/holders/evp.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "holder.h"
+
+#include <contrib/libs/openssl/include/openssl/evp.h>
+
+namespace NOpenSSL {
+ class TEvpCipherCtx : public THolder<EVP_CIPHER_CTX, EVP_CIPHER_CTX_new, EVP_CIPHER_CTX_free> {
+ };
+
+ class TEvpMdCtx : public THolder<EVP_MD_CTX, EVP_MD_CTX_new, EVP_MD_CTX_free> {
+ };
+}
diff --git a/library/cpp/openssl/holders/hmac.h b/library/cpp/openssl/holders/hmac.h
new file mode 100644
index 00000000000..4110e06f000
--- /dev/null
+++ b/library/cpp/openssl/holders/hmac.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "holder.h"
+
+#include <contrib/libs/openssl/include/openssl/hmac.h>
+
+namespace NOpenSSL {
+ class THmacCtx : public THolder<HMAC_CTX, HMAC_CTX_new, HMAC_CTX_free> {
+ };
+}
diff --git a/library/cpp/openssl/holders/holder.h b/library/cpp/openssl/holders/holder.h
new file mode 100644
index 00000000000..c2a26ce4312
--- /dev/null
+++ b/library/cpp/openssl/holders/holder.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <util/generic/yexception.h>
+
+namespace NOpenSSL {
+
+template <typename TType, auto Create, auto Destroy, class... Args>
+class THolder {
+public:
+ inline THolder(Args... args) {
+ Ptr = Create(args...);
+ if (!Ptr) {
+ throw std::bad_alloc();
+ }
+ }
+
+ THolder(const THolder&) = delete;
+ THolder& operator=(const THolder&) = delete;
+
+ inline ~THolder() noexcept {
+ Destroy(Ptr);
+ }
+
+ inline operator TType* () noexcept {
+ return Ptr;
+ }
+
+private:
+ TType* Ptr;
+};
+
+}
diff --git a/library/cpp/openssl/holders/ut/evp_ut.cpp b/library/cpp/openssl/holders/ut/evp_ut.cpp
new file mode 100644
index 00000000000..0f8c0aed011
--- /dev/null
+++ b/library/cpp/openssl/holders/ut/evp_ut.cpp
@@ -0,0 +1,15 @@
+#include "evp.h"
+
+#include <library/cpp/testing/unittest/registar.h>
+
+Y_UNIT_TEST_SUITE(Evp) {
+ Y_UNIT_TEST(Cipher) {
+ NOpenSSL::TEvpCipherCtx ctx;
+ UNIT_ASSERT(ctx);
+ }
+
+ Y_UNIT_TEST(Md) {
+ NOpenSSL::TEvpMdCtx ctx;
+ UNIT_ASSERT(ctx);
+ }
+}
diff --git a/library/cpp/openssl/holders/ut/hmac_ut.cpp b/library/cpp/openssl/holders/ut/hmac_ut.cpp
new file mode 100644
index 00000000000..60f561c337d
--- /dev/null
+++ b/library/cpp/openssl/holders/ut/hmac_ut.cpp
@@ -0,0 +1,10 @@
+#include "hmac.h"
+
+#include <library/cpp/testing/unittest/registar.h>
+
+Y_UNIT_TEST_SUITE(Hmac) {
+ Y_UNIT_TEST(Ctx) {
+ NOpenSSL::THmacCtx ctx;
+ UNIT_ASSERT(ctx);
+ }
+}
diff --git a/library/cpp/openssl/holders/ut/ya.make b/library/cpp/openssl/holders/ut/ya.make
new file mode 100644
index 00000000000..045cdc35664
--- /dev/null
+++ b/library/cpp/openssl/holders/ut/ya.make
@@ -0,0 +1,10 @@
+UNITTEST_FOR(library/cpp/openssl/holders)
+
+OWNER(somov deshevoy)
+
+SRCS(
+ evp_ut.cpp
+ hmac_ut.cpp
+)
+
+END()
diff --git a/library/cpp/openssl/holders/x509_vfy.cpp b/library/cpp/openssl/holders/x509_vfy.cpp
new file mode 100644
index 00000000000..731baa9055c
--- /dev/null
+++ b/library/cpp/openssl/holders/x509_vfy.cpp
@@ -0,0 +1,30 @@
+#include "x509_vfy.h"
+
+namespace NOpenSSL {
+
+ TX509LookupMethod::TX509LookupMethod(
+ const char* name,
+ int (*newItem) (X509_LOOKUP *ctx),
+ void (*free) (X509_LOOKUP *ctx),
+ int (*init) (X509_LOOKUP *ctx),
+ int (*shutdown) (X509_LOOKUP *ctx),
+ X509_LOOKUP_ctrl_fn ctrl,
+ X509_LOOKUP_get_by_subject_fn getBySubject,
+ X509_LOOKUP_get_by_issuer_serial_fn getByIssuerSerial,
+ X509_LOOKUP_get_by_fingerprint_fn getByFingerprint,
+ X509_LOOKUP_get_by_alias_fn getByAlias
+ )
+ : THolder(name)
+ {
+ X509_LOOKUP_meth_set_new_item(*this, newItem);
+ X509_LOOKUP_meth_set_free(*this, free);
+ X509_LOOKUP_meth_set_init(*this, init);
+ X509_LOOKUP_meth_set_shutdown(*this, shutdown);
+ X509_LOOKUP_meth_set_ctrl(*this, ctrl);
+ X509_LOOKUP_meth_set_get_by_subject(*this, getBySubject);
+ X509_LOOKUP_meth_set_get_by_issuer_serial(*this, getByIssuerSerial);
+ X509_LOOKUP_meth_set_get_by_fingerprint(*this, getByFingerprint);
+ X509_LOOKUP_meth_set_get_by_alias(*this, getByAlias);
+ }
+
+} // namespace NOpenSSL
diff --git a/library/cpp/openssl/holders/x509_vfy.h b/library/cpp/openssl/holders/x509_vfy.h
new file mode 100644
index 00000000000..b735d8a0426
--- /dev/null
+++ b/library/cpp/openssl/holders/x509_vfy.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <contrib/libs/openssl/include/openssl/x509_vfy.h>
+
+#include <library/cpp/openssl/holders/holder.h>
+
+namespace NOpenSSL {
+
+class TX509LookupMethod : public THolder<X509_LOOKUP_METHOD, X509_LOOKUP_meth_new, X509_LOOKUP_meth_free, const char*> {
+public:
+ TX509LookupMethod(
+ const char* name,
+ int (*newItem) (X509_LOOKUP *ctx),
+ void (*free) (X509_LOOKUP *ctx),
+ int (*init) (X509_LOOKUP *ctx),
+ int (*shutdown) (X509_LOOKUP *ctx),
+ X509_LOOKUP_ctrl_fn ctrl,
+ X509_LOOKUP_get_by_subject_fn getBySubject,
+ X509_LOOKUP_get_by_issuer_serial_fn getByIssuerSerial,
+ X509_LOOKUP_get_by_fingerprint_fn getByFingerprint,
+ X509_LOOKUP_get_by_alias_fn getByAlias
+ );
+};
+
+} // namespace NOpenSSL
diff --git a/library/cpp/openssl/holders/ya.make b/library/cpp/openssl/holders/ya.make
new file mode 100644
index 00000000000..3a2fbf3ba54
--- /dev/null
+++ b/library/cpp/openssl/holders/ya.make
@@ -0,0 +1,16 @@
+LIBRARY()
+
+OWNER(somov deshevoy)
+
+PEERDIR(
+ contrib/libs/openssl
+)
+
+SRCS(
+ bio.cpp
+ x509_vfy.cpp
+)
+
+END()
+
+NEED_CHECK()
diff --git a/library/cpp/openssl/init/init.cpp b/library/cpp/openssl/init/init.cpp
new file mode 100644
index 00000000000..ae68ef08eaa
--- /dev/null
+++ b/library/cpp/openssl/init/init.cpp
@@ -0,0 +1,66 @@
+#include "init.h"
+
+#include <util/generic/singleton.h>
+#include <util/generic/vector.h>
+#include <util/generic/ptr.h>
+#include <util/generic/buffer.h>
+
+#include <util/system/yassert.h>
+#include <util/system/mutex.h>
+#include <util/system/thread.h>
+
+#include <util/random/entropy.h>
+#include <util/stream/input.h>
+
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/conf.h>
+#include <openssl/crypto.h>
+
+namespace {
+ struct TInitSsl {
+ struct TOpensslLocks {
+ inline TOpensslLocks()
+ : Mutexes(CRYPTO_num_locks())
+ {
+ for (auto& mpref : Mutexes) {
+ mpref.Reset(new TMutex());
+ }
+ }
+
+ inline void LockOP(int mode, int n) {
+ auto& mutex = *Mutexes.at(n);
+
+ if (mode & CRYPTO_LOCK) {
+ mutex.Acquire();
+ } else {
+ mutex.Release();
+ }
+ }
+
+ TVector<TAutoPtr<TMutex>> Mutexes;
+ };
+
+ inline TInitSsl() {
+ OPENSSL_init_crypto(OPENSSL_INIT_NO_ATEXIT, nullptr);
+ }
+
+ inline ~TInitSsl() {
+ OPENSSL_cleanup();
+ }
+
+ static void LockingFunction(int mode, int n, const char* /*file*/, int /*line*/) {
+ Singleton<TOpensslLocks>()->LockOP(mode, n);
+ }
+
+ static unsigned long ThreadIdFunction() {
+ return TThread::CurrentThreadId();
+ }
+ };
+}
+
+void InitOpenSSL() {
+ (void)SingletonWithPriority<TInitSsl, 0>();
+}
diff --git a/library/cpp/openssl/init/init.h b/library/cpp/openssl/init/init.h
new file mode 100644
index 00000000000..a626b316b3b
--- /dev/null
+++ b/library/cpp/openssl/init/init.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void InitOpenSSL();
diff --git a/library/cpp/openssl/init/ya.make b/library/cpp/openssl/init/ya.make
new file mode 100644
index 00000000000..aac073497ca
--- /dev/null
+++ b/library/cpp/openssl/init/ya.make
@@ -0,0 +1,13 @@
+LIBRARY()
+
+OWNER(pg g:zora)
+
+PEERDIR(
+ contrib/libs/openssl
+)
+
+SRCS(
+ init.cpp
+)
+
+END()
diff --git a/library/cpp/openssl/io/stream.cpp b/library/cpp/openssl/io/stream.cpp
new file mode 100644
index 00000000000..0b4be38c0e3
--- /dev/null
+++ b/library/cpp/openssl/io/stream.cpp
@@ -0,0 +1,329 @@
+#include "stream.h"
+
+#include <util/generic/deque.h>
+#include <util/generic/singleton.h>
+#include <util/generic/yexception.h>
+
+#include <library/cpp/openssl/init/init.h>
+#include <library/cpp/openssl/method/io.h>
+#include <library/cpp/resource/resource.h>
+
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/tls1.h>
+#include <openssl/x509v3.h>
+
+using TOptions = TOpenSslClientIO::TOptions;
+
+namespace {
+ struct TSslIO;
+
+ struct TSslInitOnDemand {
+ inline TSslInitOnDemand() {
+ InitOpenSSL();
+ }
+ };
+
+ int GetLastSslError() noexcept {
+ return ERR_peek_last_error();
+ }
+
+ const char* SslErrorText(int error) noexcept {
+ return ERR_error_string(error, nullptr);
+ }
+
+ inline TStringBuf SslLastError() noexcept {
+ return SslErrorText(GetLastSslError());
+ }
+
+ struct TSslError: public yexception {
+ inline TSslError() {
+ *this << SslLastError();
+ }
+ };
+
+ struct TSslDestroy {
+ static inline void Destroy(ssl_ctx_st* ctx) noexcept {
+ SSL_CTX_free(ctx);
+ }
+
+ static inline void Destroy(ssl_st* ssl) noexcept {
+ SSL_free(ssl);
+ }
+
+ static inline void Destroy(bio_st* bio) noexcept {
+ BIO_free(bio);
+ }
+
+ static inline void Destroy(x509_st* x509) noexcept {
+ X509_free(x509);
+ }
+ };
+
+ template <class T>
+ using TSslHolderPtr = THolder<T, TSslDestroy>;
+
+ using TSslContextPtr = TSslHolderPtr<ssl_ctx_st>;
+ using TSslPtr = TSslHolderPtr<ssl_st>;
+ using TBioPtr = TSslHolderPtr<bio_st>;
+ using TX509Ptr = TSslHolderPtr<x509_st>;
+
+ inline TSslContextPtr CreateSslCtx(const ssl_method_st* method) {
+ TSslContextPtr ctx(SSL_CTX_new(method));
+
+ if (!ctx) {
+ ythrow TSslError() << "SSL_CTX_new";
+ }
+
+ SSL_CTX_set_options(ctx.Get(), SSL_OP_NO_SSLv2);
+ SSL_CTX_set_options(ctx.Get(), SSL_OP_NO_SSLv3);
+ SSL_CTX_set_options(ctx.Get(), SSL_OP_MICROSOFT_SESS_ID_BUG);
+ SSL_CTX_set_options(ctx.Get(), SSL_OP_NETSCAPE_CHALLENGE_BUG);
+
+ return ctx;
+ }
+
+ struct TStreamIO : public NOpenSSL::TAbstractIO {
+ inline TStreamIO(IInputStream* in, IOutputStream* out)
+ : In(in)
+ , Out(out)
+ {
+ }
+
+ int Write(const char* data, size_t dlen, size_t* written) override {
+ Out->Write(data, dlen);
+
+ *written = dlen;
+ return 1;
+ }
+
+ int Read(char* data, size_t dlen, size_t* readbytes) override {
+ *readbytes = In->Read(data, dlen);
+ return 1;
+ }
+
+ int Puts(const char* buf) override {
+ Y_UNUSED(buf);
+ return -1;
+ }
+
+ int Gets(char* buf, int size) override {
+ Y_UNUSED(buf);
+ Y_UNUSED(size);
+ return -1;
+ }
+
+ void Flush() override {
+ }
+
+ IInputStream* In;
+ IOutputStream* Out;
+ };
+
+ struct TSslIO: public TSslInitOnDemand, public TOptions {
+ inline TSslIO(IInputStream* in, IOutputStream* out, const TOptions& opts)
+ : TOptions(opts)
+ , Io(in, out)
+ , Ctx(CreateClientContext())
+ , Ssl(ConstructSsl())
+ {
+ Connect();
+ }
+
+ inline TSslContextPtr CreateClientContext() {
+ TSslContextPtr ctx = CreateSslCtx(SSLv23_client_method());
+ if (ClientCert_) {
+ if (!ClientCert_->CertificateFile_ || !ClientCert_->PrivateKeyFile_) {
+ ythrow yexception() << "both client certificate and private key are required";
+ }
+ if (ClientCert_->PrivateKeyPassword_) {
+ SSL_CTX_set_default_passwd_cb(ctx.Get(), [](char* buf, int size, int rwflag, void* userData) -> int {
+ Y_UNUSED(rwflag);
+ auto io = static_cast<TSslIO*>(userData);
+ if (!io) {
+ return -1;
+ }
+ if (size < static_cast<int>(io->ClientCert_->PrivateKeyPassword_.size())) {
+ return -1;
+ }
+ return io->ClientCert_->PrivateKeyPassword_.copy(buf, size, 0);
+ });
+ SSL_CTX_set_default_passwd_cb_userdata(ctx.Get(), this);
+ }
+ if (1 != SSL_CTX_use_certificate_chain_file(ctx.Get(), ClientCert_->CertificateFile_.c_str())) {
+ ythrow TSslError() << "SSL_CTX_use_certificate_chain_file";
+ }
+ if (1 != SSL_CTX_use_PrivateKey_file(ctx.Get(), ClientCert_->PrivateKeyFile_.c_str(), SSL_FILETYPE_PEM)) {
+ ythrow TSslError() << "SSL_CTX_use_PrivateKey_file";
+ }
+ if (1 != SSL_CTX_check_private_key(ctx.Get())) {
+ ythrow TSslError() << "SSL_CTX_check_private_key (client)";
+ }
+ }
+ return ctx;
+ }
+
+ inline TSslPtr ConstructSsl() {
+ TSslPtr ssl(SSL_new(Ctx.Get()));
+
+ if (!ssl) {
+ ythrow TSslError() << "SSL_new";
+ }
+
+ if (VerifyCert_) {
+ InitVerification(ssl.Get());
+ }
+
+ BIO_up_ref(Io); // SSL_set_bio consumes only one reference if rbio and wbio are the same
+ SSL_set_bio(ssl.Get(), Io, Io);
+
+ return ssl;
+ }
+
+ inline void InitVerification(ssl_st* ssl) {
+ X509_VERIFY_PARAM* param = SSL_get0_param(ssl);
+ X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+ Y_ENSURE(X509_VERIFY_PARAM_set1_host(param, VerifyCert_->Hostname_.data(), VerifyCert_->Hostname_.size()));
+ SSL_set_tlsext_host_name(ssl, VerifyCert_->Hostname_.data()); // TLS extenstion: SNI
+
+ SSL_CTX_set_cert_store(Ctx.Get(), GetBuiltinOpenSslX509Store().Release());
+
+ Y_ENSURE_EX(1 == SSL_CTX_set_default_verify_paths(Ctx.Get()),
+ TSslError());
+ // it is OK to ignore result of SSL_CTX_load_verify_locations():
+ // Dir "/etc/ssl/certs/" may be missing
+ SSL_CTX_load_verify_locations(Ctx.Get(),
+ "/etc/ssl/certs/ca-certificates.crt",
+ "/etc/ssl/certs/");
+
+ SSL_set_verify(ssl, SSL_VERIFY_PEER, nullptr);
+ }
+
+ inline void Connect() {
+ if (SSL_connect(Ssl.Get()) != 1) {
+ ythrow TSslError() << "SSL_connect";
+ }
+ }
+
+ inline void Finish() const {
+ SSL_shutdown(Ssl.Get());
+ }
+
+ inline size_t Read(void* buf, size_t len) {
+ const int ret = SSL_read(Ssl.Get(), buf, len);
+
+ if (ret < 0) {
+ ythrow TSslError() << "SSL_read";
+ }
+
+ return ret;
+ }
+
+ inline void Write(const char* buf, size_t len) {
+ while (len) {
+ const int ret = SSL_write(Ssl.Get(), buf, len);
+
+ if (ret < 0) {
+ ythrow TSslError() << "SSL_write";
+ }
+
+ buf += (size_t)ret;
+ len -= (size_t)ret;
+ }
+ }
+
+ TStreamIO Io;
+ TSslContextPtr Ctx;
+ TSslPtr Ssl;
+ };
+}
+
+struct TOpenSslClientIO::TImpl: public TSslIO {
+ inline TImpl(IInputStream* in, IOutputStream* out, const TOptions& opts)
+ : TSslIO(in, out, opts)
+ {
+ }
+};
+
+TOpenSslClientIO::TOpenSslClientIO(IInputStream* in, IOutputStream* out)
+ : Impl_(new TImpl(in, out, TOptions()))
+{
+}
+
+TOpenSslClientIO::TOpenSslClientIO(IInputStream* in, IOutputStream* out, const TOptions& options)
+ : Impl_(new TImpl(in, out, options))
+{
+}
+
+TOpenSslClientIO::~TOpenSslClientIO() {
+ try {
+ Impl_->Finish();
+ } catch (...) {
+ }
+}
+
+void TOpenSslClientIO::DoWrite(const void* buf, size_t len) {
+ Impl_->Write((const char*)buf, len);
+}
+
+size_t TOpenSslClientIO::DoRead(void* buf, size_t len) {
+ return Impl_->Read(buf, len);
+}
+
+namespace NPrivate {
+ void TSslDestroy::Destroy(x509_store_st* x509) noexcept {
+ X509_STORE_free(x509);
+ }
+}
+
+class TBuiltinCerts {
+public:
+ TBuiltinCerts() {
+ TString c = NResource::Find("/builtin/cacert");
+
+ TBioPtr cbio(BIO_new_mem_buf(c.data(), c.size()));
+ Y_ENSURE_EX(cbio, TSslError() << "BIO_new_mem_buf");
+
+ while (true) {
+ TX509Ptr cert(PEM_read_bio_X509(cbio.Get(), nullptr, nullptr, nullptr));
+ if (!cert) {
+ break;
+ }
+ Certs.push_back(std::move(cert));
+ }
+
+ int err = GetLastSslError();
+ if (!Certs.empty() && ERR_GET_LIB(err) == ERR_LIB_PEM && ERR_GET_REASON(err) == PEM_R_NO_START_LINE) {
+ ERR_clear_error();
+ } else {
+ ythrow TSslError() << "can't load provided bundle: " << ERR_reason_error_string(err);
+ }
+
+ Y_ENSURE_EX(!Certs.empty(), TSslError());
+ }
+
+ TOpenSslX509StorePtr GetX509Store() const {
+ TOpenSslX509StorePtr store(X509_STORE_new());
+
+ for (const TX509Ptr& c : Certs) {
+ if (0 == X509_STORE_add_cert(store.Get(), c.Get())) {
+ int err = GetLastSslError();
+ if (ERR_GET_LIB(err) == ERR_LIB_X509 && ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+ ERR_clear_error();
+ } else {
+ ythrow TSslError() << "can't load provided bundle: " << ERR_reason_error_string(err);
+ }
+ }
+ }
+
+ return store;
+ }
+
+private:
+ TDeque<TX509Ptr> Certs;
+};
+
+TOpenSslX509StorePtr GetBuiltinOpenSslX509Store() {
+ return Singleton<TBuiltinCerts>()->GetX509Store();
+}
diff --git a/library/cpp/openssl/io/stream.h b/library/cpp/openssl/io/stream.h
new file mode 100644
index 00000000000..7bca8f80ef8
--- /dev/null
+++ b/library/cpp/openssl/io/stream.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <util/generic/maybe.h>
+#include <util/generic/ptr.h>
+#include <util/stream/input.h>
+#include <util/stream/output.h>
+
+class TOpenSslClientIO: public IInputStream, public IOutputStream {
+public:
+ struct TOptions {
+ struct TVerifyCert {
+ // Uses builtin certs.
+ // Also uses default CA path /etc/ssl/certs/ - can be provided with debian package: ca-certificates.deb.
+ // It can be expanded with ENV: SSL_CERT_DIR.
+ TString Hostname_;
+ };
+ struct TClientCert {
+ TString CertificateFile_;
+ TString PrivateKeyFile_;
+ TString PrivateKeyPassword_;
+ };
+
+ TMaybe<TVerifyCert> VerifyCert_;
+ TMaybe<TClientCert> ClientCert_;
+ // TODO - keys, cyphers, etc
+ };
+
+ TOpenSslClientIO(IInputStream* in, IOutputStream* out);
+ TOpenSslClientIO(IInputStream* in, IOutputStream* out, const TOptions& options);
+ ~TOpenSslClientIO() override;
+
+private:
+ void DoWrite(const void* buf, size_t len) override;
+ size_t DoRead(void* buf, size_t len) override;
+
+private:
+ struct TImpl;
+ THolder<TImpl> Impl_;
+};
+
+struct x509_store_st;
+
+namespace NPrivate {
+ struct TSslDestroy {
+ static void Destroy(x509_store_st* x509) noexcept;
+ };
+}
+
+using TOpenSslX509StorePtr = THolder<x509_store_st, NPrivate::TSslDestroy>;
+TOpenSslX509StorePtr GetBuiltinOpenSslX509Store();
diff --git a/library/cpp/openssl/io/ut/builtin_ut.cpp b/library/cpp/openssl/io/ut/builtin_ut.cpp
new file mode 100644
index 00000000000..987cd084922
--- /dev/null
+++ b/library/cpp/openssl/io/ut/builtin_ut.cpp
@@ -0,0 +1,9 @@
+#include <library/cpp/openssl/io/stream.h>
+#include <library/cpp/testing/unittest/registar.h>
+
+Y_UNIT_TEST_SUITE(Builtin) {
+ Y_UNIT_TEST(Init) {
+ UNIT_ASSERT_NO_EXCEPTION(GetBuiltinOpenSslX509Store());
+ UNIT_ASSERT_NO_EXCEPTION(GetBuiltinOpenSslX509Store());
+ }
+}
diff --git a/library/cpp/openssl/io/ut/ya.make b/library/cpp/openssl/io/ut/ya.make
new file mode 100644
index 00000000000..b978a6c0462
--- /dev/null
+++ b/library/cpp/openssl/io/ut/ya.make
@@ -0,0 +1,12 @@
+UNITTEST_FOR(library/cpp/openssl/io)
+
+OWNER(
+ pg
+ cerevra
+)
+
+SRCS(
+ builtin_ut.cpp
+)
+
+END()
diff --git a/library/cpp/openssl/io/ya.make b/library/cpp/openssl/io/ya.make
new file mode 100644
index 00000000000..aaebba40113
--- /dev/null
+++ b/library/cpp/openssl/io/ya.make
@@ -0,0 +1,16 @@
+LIBRARY()
+
+OWNER(pg)
+
+PEERDIR(
+ certs
+ contrib/libs/openssl
+ library/cpp/openssl/init
+ library/cpp/openssl/method
+)
+
+SRCS(
+ stream.cpp
+)
+
+END()
diff --git a/library/cpp/openssl/method/io.cpp b/library/cpp/openssl/method/io.cpp
new file mode 100644
index 00000000000..d184b6456c7
--- /dev/null
+++ b/library/cpp/openssl/method/io.cpp
@@ -0,0 +1,122 @@
+#include "io.h"
+
+#include <util/generic/singleton.h>
+#include <util/generic/yexception.h>
+#include <util/system/compiler.h>
+#include <util/system/yassert.h>
+
+namespace {
+ using NOpenSSL::TAbstractIO;
+
+ TAbstractIO* IO(BIO* bio) noexcept {
+ void* ptr = BIO_get_data(bio);
+ Y_VERIFY(ptr);
+ return static_cast<TAbstractIO*>(ptr);
+ }
+
+ template<class T, class Callable, class... Args>
+ T ExceptionBoundary(BIO* bio, Callable&& f, T err, Args&&... args) noexcept {
+ try {
+ return (IO(bio)->*f)(args...);
+ } catch (...) {
+ return err;
+ }
+ }
+
+ int Write(BIO* bio, const char* data, int dlen) noexcept {
+ return ExceptionBoundary(bio, &TAbstractIO::WriteOld, -1, data, dlen);
+ }
+
+ int Read(BIO* bio, char* data, int dlen) noexcept {
+ return ExceptionBoundary(bio, &TAbstractIO::ReadOld, -1, data, dlen);
+ }
+
+ int Puts(BIO* bio, const char* buf) noexcept {
+ return ExceptionBoundary(bio, &TAbstractIO::Puts, -1, buf);
+ }
+
+ int Gets(BIO* bio, char* buf, int size) noexcept {
+ return ExceptionBoundary(bio, &TAbstractIO::Gets, -1, buf, size);
+ }
+
+ long Ctrl(BIO* bio, int cmd, long larg, void* parg) noexcept {
+ return ExceptionBoundary(bio, &TAbstractIO::Ctrl, -1, cmd, larg, parg);
+ }
+
+ int Create(BIO* bio) noexcept {
+ BIO_set_data(bio, nullptr);
+ BIO_set_init(bio, 1);
+ return 1;
+ }
+
+ int Destroy(BIO* bio) noexcept {
+ BIO_set_data(bio, nullptr);
+ BIO_set_init(bio, 0);
+ return 1;
+ }
+
+ NOpenSSL::TBioMethod* Method() {
+ return SingletonWithPriority<NOpenSSL::TBioMethod, 32768>(
+ BIO_get_new_index() | BIO_TYPE_SOURCE_SINK,
+ "AbstractIO",
+ Write,
+ Read,
+ Puts,
+ Gets,
+ Ctrl,
+ Create,
+ Destroy,
+ nullptr
+ );
+ }
+}
+
+namespace NOpenSSL {
+
+ TAbstractIO::TAbstractIO()
+ : Bio(BIO_new(*Method())) {
+ if (Y_UNLIKELY(!Bio)) {
+ throw std::bad_alloc();
+ }
+ BIO_set_data(Bio, this);
+ }
+
+ TAbstractIO::~TAbstractIO() {
+ BIO_free(Bio);
+ }
+
+ int TAbstractIO::WriteOld(const char* data, int dlen) {
+ size_t written = 0;
+
+ int ret = Write(data, dlen, &written);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ return written;
+ }
+
+ int TAbstractIO::ReadOld(char* data, int dlen) {
+ size_t readbytes = 0;
+
+ int ret = Read(data, dlen, &readbytes);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ return readbytes;
+ }
+
+ long TAbstractIO::Ctrl(int cmd, long larg, void* parg) {
+ Y_UNUSED(larg);
+ Y_UNUSED(parg);
+
+ if (cmd == BIO_CTRL_FLUSH) {
+ Flush();
+ return 1;
+ }
+
+ return 0;
+ }
+
+} // namespace NOpenSSL
diff --git a/library/cpp/openssl/method/io.h b/library/cpp/openssl/method/io.h
new file mode 100644
index 00000000000..f1d3df978d7
--- /dev/null
+++ b/library/cpp/openssl/method/io.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <library/cpp/openssl/holders/bio.h>
+
+namespace NOpenSSL {
+
+class TAbstractIO {
+public:
+ TAbstractIO();
+ virtual ~TAbstractIO();
+
+ virtual int Write(const char* data, size_t dlen, size_t* written) = 0;
+ virtual int Read(char* data, size_t dlen, size_t* readbytes) = 0;
+ virtual int Puts(const char* buf) = 0;
+ virtual int Gets(char* buf, int size) = 0;
+
+ virtual long Ctrl(int cmd, long larg, void* parg);
+ virtual void Flush() = 0;
+
+ int WriteOld(const char* data, int dlen);
+ int ReadOld(char* data, int dlen);
+
+ inline operator BIO* () noexcept {
+ return Bio;
+ }
+
+private:
+ BIO* Bio;
+};
+
+} // namespace NOpenSSL
diff --git a/library/cpp/openssl/method/ut/io_ut.cpp b/library/cpp/openssl/method/ut/io_ut.cpp
new file mode 100644
index 00000000000..bff2b23d31b
--- /dev/null
+++ b/library/cpp/openssl/method/ut/io_ut.cpp
@@ -0,0 +1,52 @@
+#include <library/cpp/openssl/method/io.h>
+
+#include <library/cpp/testing/unittest/registar.h>
+
+class TTestIO : public NOpenSSL::TAbstractIO {
+public:
+ int Write(const char* data, size_t dlen, size_t* written) override {
+ Y_UNUSED(data);
+ *written = dlen;
+ return 1;
+ }
+
+ int Read(char* data, size_t dlen, size_t* readbytes) override {
+ Y_UNUSED(data);
+ Y_UNUSED(dlen);
+ *readbytes = 0;
+ return 0;
+ }
+
+ int Puts(const char* buf) override {
+ if (buf == nullptr) {
+ return 0;
+ }
+
+ return strlen(buf);
+ }
+
+ int Gets(char* buf, int size) override {
+ Y_UNUSED(buf);
+ Y_UNUSED(size);
+ return 0;
+ }
+
+ void Flush() override {
+
+ }
+};
+
+Y_UNIT_TEST_SUITE(IO) {
+ Y_UNIT_TEST(AbstractIO) {
+ static const char s[] = "12345";
+
+ TTestIO test;
+
+ UNIT_ASSERT(BIO_write(test, s, sizeof(s)) == sizeof(s));
+ UNIT_ASSERT(BIO_puts(test, s) == strlen(s));
+
+ char buf[128];
+ UNIT_ASSERT(BIO_read(test, buf, sizeof(buf)) == 0);
+ UNIT_ASSERT(BIO_gets(test, buf, sizeof(buf)) == 0);
+ }
+}
diff --git a/library/cpp/openssl/method/ut/ya.make b/library/cpp/openssl/method/ut/ya.make
new file mode 100644
index 00000000000..3645ad17e65
--- /dev/null
+++ b/library/cpp/openssl/method/ut/ya.make
@@ -0,0 +1,9 @@
+UNITTEST_FOR(library/cpp/openssl/method)
+
+OWNER(somov deshevoy)
+
+SRCS(
+ io_ut.cpp
+)
+
+END()
diff --git a/library/cpp/openssl/method/ya.make b/library/cpp/openssl/method/ya.make
new file mode 100644
index 00000000000..c8f6f18b6ba
--- /dev/null
+++ b/library/cpp/openssl/method/ya.make
@@ -0,0 +1,14 @@
+LIBRARY()
+
+OWNER(somov deshevoy)
+
+PEERDIR(
+ contrib/libs/openssl
+ library/cpp/openssl/holders
+)
+
+SRCS(
+ io.cpp
+)
+
+END()
diff --git a/library/cpp/openssl/ya.make b/library/cpp/openssl/ya.make
new file mode 100644
index 00000000000..7c10963e26f
--- /dev/null
+++ b/library/cpp/openssl/ya.make
@@ -0,0 +1,13 @@
+RECURSE(
+ big_integer
+ big_integer/ut
+ crypto
+ crypto/ut
+ holders
+ holders/ut
+ io
+ io/ut
+ method
+ method/ut
+ init
+)