stream.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. #include "stream.h"
  2. #include <util/generic/deque.h>
  3. #include <util/generic/singleton.h>
  4. #include <util/generic/yexception.h>
  5. #include <library/cpp/openssl/init/init.h>
  6. #include <library/cpp/openssl/method/io.h>
  7. #include <library/cpp/resource/resource.h>
  8. #include <openssl/bio.h>
  9. #include <openssl/ssl.h>
  10. #include <openssl/err.h>
  11. #include <openssl/tls1.h>
  12. #include <openssl/x509v3.h>
  13. using TOptions = TOpenSslClientIO::TOptions;
  14. namespace {
  15. struct TSslIO;
  16. struct TSslInitOnDemand {
  17. inline TSslInitOnDemand() {
  18. InitOpenSSL();
  19. }
  20. };
  21. int GetLastSslError() noexcept {
  22. return ERR_peek_last_error();
  23. }
  24. const char* SslErrorText(int error) noexcept {
  25. return ERR_error_string(error, nullptr);
  26. }
  27. inline TStringBuf SslLastError() noexcept {
  28. return SslErrorText(GetLastSslError());
  29. }
  30. struct TSslError: public yexception {
  31. inline TSslError() {
  32. *this << SslLastError();
  33. }
  34. };
  35. struct TSslDestroy {
  36. static inline void Destroy(ssl_ctx_st* ctx) noexcept {
  37. SSL_CTX_free(ctx);
  38. }
  39. static inline void Destroy(ssl_st* ssl) noexcept {
  40. SSL_free(ssl);
  41. }
  42. static inline void Destroy(bio_st* bio) noexcept {
  43. BIO_free(bio);
  44. }
  45. static inline void Destroy(x509_st* x509) noexcept {
  46. X509_free(x509);
  47. }
  48. };
  49. template <class T>
  50. using TSslHolderPtr = THolder<T, TSslDestroy>;
  51. using TSslContextPtr = TSslHolderPtr<ssl_ctx_st>;
  52. using TSslPtr = TSslHolderPtr<ssl_st>;
  53. using TBioPtr = TSslHolderPtr<bio_st>;
  54. using TX509Ptr = TSslHolderPtr<x509_st>;
  55. inline TSslContextPtr CreateSslCtx(const ssl_method_st* method) {
  56. TSslContextPtr ctx(SSL_CTX_new(method));
  57. if (!ctx) {
  58. ythrow TSslError() << "SSL_CTX_new";
  59. }
  60. SSL_CTX_set_options(ctx.Get(), SSL_OP_NO_SSLv2);
  61. SSL_CTX_set_options(ctx.Get(), SSL_OP_NO_SSLv3);
  62. SSL_CTX_set_options(ctx.Get(), SSL_OP_MICROSOFT_SESS_ID_BUG);
  63. SSL_CTX_set_options(ctx.Get(), SSL_OP_NETSCAPE_CHALLENGE_BUG);
  64. return ctx;
  65. }
  66. struct TStreamIO : public NOpenSSL::TAbstractIO {
  67. inline TStreamIO(IInputStream* in, IOutputStream* out)
  68. : In(in)
  69. , Out(out)
  70. {
  71. }
  72. int Write(const char* data, size_t dlen, size_t* written) override {
  73. Out->Write(data, dlen);
  74. *written = dlen;
  75. return 1;
  76. }
  77. int Read(char* data, size_t dlen, size_t* readbytes) override {
  78. *readbytes = In->Read(data, dlen);
  79. return 1;
  80. }
  81. int Puts(const char* buf) override {
  82. Y_UNUSED(buf);
  83. return -1;
  84. }
  85. int Gets(char* buf, int size) override {
  86. Y_UNUSED(buf);
  87. Y_UNUSED(size);
  88. return -1;
  89. }
  90. void Flush() override {
  91. }
  92. IInputStream* In;
  93. IOutputStream* Out;
  94. };
  95. struct TSslIO: public TSslInitOnDemand, public TOptions {
  96. inline TSslIO(IInputStream* in, IOutputStream* out, const TOptions& opts)
  97. : TOptions(opts)
  98. , Io(in, out)
  99. , Ctx(CreateClientContext())
  100. , Ssl(ConstructSsl())
  101. {
  102. Connect();
  103. }
  104. inline TSslContextPtr CreateClientContext() {
  105. TSslContextPtr ctx = CreateSslCtx(SSLv23_client_method());
  106. if (ClientCert_) {
  107. if (!ClientCert_->CertificateFile_ || !ClientCert_->PrivateKeyFile_) {
  108. ythrow yexception() << "both client certificate and private key are required";
  109. }
  110. if (ClientCert_->PrivateKeyPassword_) {
  111. SSL_CTX_set_default_passwd_cb(ctx.Get(), [](char* buf, int size, int rwflag, void* userData) -> int {
  112. Y_UNUSED(rwflag);
  113. auto io = static_cast<TSslIO*>(userData);
  114. if (!io) {
  115. return -1;
  116. }
  117. if (size < static_cast<int>(io->ClientCert_->PrivateKeyPassword_.size())) {
  118. return -1;
  119. }
  120. return io->ClientCert_->PrivateKeyPassword_.copy(buf, size, 0);
  121. });
  122. SSL_CTX_set_default_passwd_cb_userdata(ctx.Get(), this);
  123. }
  124. if (1 != SSL_CTX_use_certificate_chain_file(ctx.Get(), ClientCert_->CertificateFile_.c_str())) {
  125. ythrow TSslError() << "SSL_CTX_use_certificate_chain_file";
  126. }
  127. if (1 != SSL_CTX_use_PrivateKey_file(ctx.Get(), ClientCert_->PrivateKeyFile_.c_str(), SSL_FILETYPE_PEM)) {
  128. ythrow TSslError() << "SSL_CTX_use_PrivateKey_file";
  129. }
  130. if (1 != SSL_CTX_check_private_key(ctx.Get())) {
  131. ythrow TSslError() << "SSL_CTX_check_private_key (client)";
  132. }
  133. }
  134. return ctx;
  135. }
  136. inline TSslPtr ConstructSsl() {
  137. TSslPtr ssl(SSL_new(Ctx.Get()));
  138. if (!ssl) {
  139. ythrow TSslError() << "SSL_new";
  140. }
  141. if (VerifyCert_) {
  142. InitVerification(ssl.Get());
  143. }
  144. BIO_up_ref(Io); // SSL_set_bio consumes only one reference if rbio and wbio are the same
  145. SSL_set_bio(ssl.Get(), Io, Io);
  146. return ssl;
  147. }
  148. inline void InitVerification(ssl_st* ssl) {
  149. X509_VERIFY_PARAM* param = SSL_get0_param(ssl);
  150. X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
  151. Y_ENSURE(X509_VERIFY_PARAM_set1_host(param, VerifyCert_->Hostname_.data(), VerifyCert_->Hostname_.size()));
  152. SSL_set_tlsext_host_name(ssl, VerifyCert_->Hostname_.data()); // TLS extenstion: SNI
  153. SSL_CTX_set_cert_store(Ctx.Get(), GetBuiltinOpenSslX509Store().Release());
  154. Y_ENSURE_EX(1 == SSL_CTX_set_default_verify_paths(Ctx.Get()),
  155. TSslError());
  156. // it is OK to ignore result of SSL_CTX_load_verify_locations():
  157. // Dir "/etc/ssl/certs/" may be missing
  158. SSL_CTX_load_verify_locations(Ctx.Get(),
  159. "/etc/ssl/certs/ca-certificates.crt",
  160. "/etc/ssl/certs/");
  161. SSL_set_verify(ssl, SSL_VERIFY_PEER, nullptr);
  162. }
  163. inline void Connect() {
  164. if (SSL_connect(Ssl.Get()) != 1) {
  165. ythrow TSslError() << "SSL_connect";
  166. }
  167. }
  168. inline void Finish() const {
  169. SSL_shutdown(Ssl.Get());
  170. }
  171. inline size_t Read(void* buf, size_t len) {
  172. const int ret = SSL_read(Ssl.Get(), buf, len);
  173. if (ret < 0) {
  174. ythrow TSslError() << "SSL_read";
  175. }
  176. return ret;
  177. }
  178. inline void Write(const char* buf, size_t len) {
  179. while (len) {
  180. const int ret = SSL_write(Ssl.Get(), buf, len);
  181. if (ret < 0) {
  182. ythrow TSslError() << "SSL_write";
  183. }
  184. buf += (size_t)ret;
  185. len -= (size_t)ret;
  186. }
  187. }
  188. TStreamIO Io;
  189. TSslContextPtr Ctx;
  190. TSslPtr Ssl;
  191. };
  192. }
  193. struct TOpenSslClientIO::TImpl: public TSslIO {
  194. inline TImpl(IInputStream* in, IOutputStream* out, const TOptions& opts)
  195. : TSslIO(in, out, opts)
  196. {
  197. }
  198. };
  199. TOpenSslClientIO::TOpenSslClientIO(IInputStream* in, IOutputStream* out)
  200. : Impl_(new TImpl(in, out, TOptions()))
  201. {
  202. }
  203. TOpenSslClientIO::TOpenSslClientIO(IInputStream* in, IOutputStream* out, const TOptions& options)
  204. : Impl_(new TImpl(in, out, options))
  205. {
  206. }
  207. TOpenSslClientIO::~TOpenSslClientIO() {
  208. try {
  209. Impl_->Finish();
  210. } catch (...) {
  211. }
  212. }
  213. void TOpenSslClientIO::DoWrite(const void* buf, size_t len) {
  214. Impl_->Write((const char*)buf, len);
  215. }
  216. size_t TOpenSslClientIO::DoRead(void* buf, size_t len) {
  217. return Impl_->Read(buf, len);
  218. }
  219. namespace NPrivate {
  220. void TSslDestroy::Destroy(x509_store_st* x509) noexcept {
  221. X509_STORE_free(x509);
  222. }
  223. }
  224. class TBuiltinCerts {
  225. public:
  226. TBuiltinCerts() {
  227. TString c = NResource::Find("/builtin/cacert");
  228. TBioPtr cbio(BIO_new_mem_buf(c.data(), c.size()));
  229. Y_ENSURE_EX(cbio, TSslError() << "BIO_new_mem_buf");
  230. while (true) {
  231. TX509Ptr cert(PEM_read_bio_X509(cbio.Get(), nullptr, nullptr, nullptr));
  232. if (!cert) {
  233. break;
  234. }
  235. Certs.push_back(std::move(cert));
  236. }
  237. int err = GetLastSslError();
  238. if (!Certs.empty() && ERR_GET_LIB(err) == ERR_LIB_PEM && ERR_GET_REASON(err) == PEM_R_NO_START_LINE) {
  239. ERR_clear_error();
  240. } else {
  241. ythrow TSslError() << "can't load provided bundle: " << ERR_reason_error_string(err);
  242. }
  243. Y_ENSURE_EX(!Certs.empty(), TSslError());
  244. }
  245. TOpenSslX509StorePtr GetX509Store() const {
  246. TOpenSslX509StorePtr store(X509_STORE_new());
  247. for (const TX509Ptr& c : Certs) {
  248. if (0 == X509_STORE_add_cert(store.Get(), c.Get())) {
  249. int err = GetLastSslError();
  250. if (ERR_GET_LIB(err) == ERR_LIB_X509 && ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
  251. ERR_clear_error();
  252. } else {
  253. ythrow TSslError() << "can't load provided bundle: " << ERR_reason_error_string(err);
  254. }
  255. }
  256. }
  257. return store;
  258. }
  259. private:
  260. TDeque<TX509Ptr> Certs;
  261. };
  262. TOpenSslX509StorePtr GetBuiltinOpenSslX509Store() {
  263. return Singleton<TBuiltinCerts>()->GetX509Store();
  264. }