1// 2// ssl/detail/impl/engine.ipp 3// ~~~~~~~~~~~~~~~~~~~~~~~~~~ 4// 5// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6// 7// Distributed under the Boost Software License, Version 1.0. (See accompanying 8// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9// 10 11#ifndef BOOST_ASIO_SSL_DETAIL_IMPL_ENGINE_IPP 12#define BOOST_ASIO_SSL_DETAIL_IMPL_ENGINE_IPP 13 14#if defined(_MSC_VER) && (_MSC_VER >= 1200) 15# pragma once 16#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 17 18#include <boost/asio/detail/config.hpp> 19 20#include <boost/asio/detail/throw_error.hpp> 21#include <boost/asio/error.hpp> 22#include <boost/asio/ssl/detail/engine.hpp> 23#include <boost/asio/ssl/error.hpp> 24#include <boost/asio/ssl/verify_context.hpp> 25 26#include <boost/asio/detail/push_options.hpp> 27 28namespace boost { 29namespace asio { 30namespace ssl { 31namespace detail { 32 33engine::engine(SSL_CTX* context) 34 : ssl_(::SSL_new(context)) 35{ 36 if (!ssl_) 37 { 38 boost::system::error_code ec( 39 static_cast<int>(::ERR_get_error()), 40 boost::asio::error::get_ssl_category()); 41 boost::asio::detail::throw_error(ec, "engine"); 42 } 43 44#if (OPENSSL_VERSION_NUMBER < 0x10000000L) 45 accept_mutex().init(); 46#endif // (OPENSSL_VERSION_NUMBER < 0x10000000L) 47 48 ::SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE); 49 ::SSL_set_mode(ssl_, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); 50#if defined(SSL_MODE_RELEASE_BUFFERS) 51 ::SSL_set_mode(ssl_, SSL_MODE_RELEASE_BUFFERS); 52#endif // defined(SSL_MODE_RELEASE_BUFFERS) 53 54 ::BIO* int_bio = 0; 55 ::BIO_new_bio_pair(&int_bio, 0, &ext_bio_, 0); 56 ::SSL_set_bio(ssl_, int_bio, int_bio); 57} 58 59#if defined(BOOST_ASIO_HAS_MOVE) 60engine::engine(engine&& other) BOOST_ASIO_NOEXCEPT 61 : ssl_(other.ssl_), 62 ext_bio_(other.ext_bio_) 63{ 64 other.ssl_ = 0; 65 other.ext_bio_ = 0; 66} 67#endif // defined(BOOST_ASIO_HAS_MOVE) 68 69engine::~engine() 70{ 71 if (ssl_ && SSL_get_app_data(ssl_)) 72 { 73 delete static_cast<verify_callback_base*>(SSL_get_app_data(ssl_)); 74 SSL_set_app_data(ssl_, 0); 75 } 76 77 if (ext_bio_) 78 ::BIO_free(ext_bio_); 79 80 if (ssl_) 81 ::SSL_free(ssl_); 82} 83 84SSL* engine::native_handle() 85{ 86 return ssl_; 87} 88 89boost::system::error_code engine::set_verify_mode( 90 verify_mode v, boost::system::error_code& ec) 91{ 92 ::SSL_set_verify(ssl_, v, ::SSL_get_verify_callback(ssl_)); 93 94 ec = boost::system::error_code(); 95 return ec; 96} 97 98boost::system::error_code engine::set_verify_depth( 99 int depth, boost::system::error_code& ec) 100{ 101 ::SSL_set_verify_depth(ssl_, depth); 102 103 ec = boost::system::error_code(); 104 return ec; 105} 106 107boost::system::error_code engine::set_verify_callback( 108 verify_callback_base* callback, boost::system::error_code& ec) 109{ 110 if (SSL_get_app_data(ssl_)) 111 delete static_cast<verify_callback_base*>(SSL_get_app_data(ssl_)); 112 113 SSL_set_app_data(ssl_, callback); 114 115 ::SSL_set_verify(ssl_, ::SSL_get_verify_mode(ssl_), 116 &engine::verify_callback_function); 117 118 ec = boost::system::error_code(); 119 return ec; 120} 121 122int engine::verify_callback_function(int preverified, X509_STORE_CTX* ctx) 123{ 124 if (ctx) 125 { 126 if (SSL* ssl = static_cast<SSL*>( 127 ::X509_STORE_CTX_get_ex_data( 128 ctx, ::SSL_get_ex_data_X509_STORE_CTX_idx()))) 129 { 130 if (SSL_get_app_data(ssl)) 131 { 132 verify_callback_base* callback = 133 static_cast<verify_callback_base*>( 134 SSL_get_app_data(ssl)); 135 136 verify_context verify_ctx(ctx); 137 return callback->call(preverified != 0, verify_ctx) ? 1 : 0; 138 } 139 } 140 } 141 142 return 0; 143} 144 145engine::want engine::handshake( 146 stream_base::handshake_type type, boost::system::error_code& ec) 147{ 148 return perform((type == boost::asio::ssl::stream_base::client) 149 ? &engine::do_connect : &engine::do_accept, 0, 0, ec, 0); 150} 151 152engine::want engine::shutdown(boost::system::error_code& ec) 153{ 154 return perform(&engine::do_shutdown, 0, 0, ec, 0); 155} 156 157engine::want engine::write(const boost::asio::const_buffer& data, 158 boost::system::error_code& ec, std::size_t& bytes_transferred) 159{ 160 if (data.size() == 0) 161 { 162 ec = boost::system::error_code(); 163 return engine::want_nothing; 164 } 165 166 return perform(&engine::do_write, 167 const_cast<void*>(data.data()), 168 data.size(), ec, &bytes_transferred); 169} 170 171engine::want engine::read(const boost::asio::mutable_buffer& data, 172 boost::system::error_code& ec, std::size_t& bytes_transferred) 173{ 174 if (data.size() == 0) 175 { 176 ec = boost::system::error_code(); 177 return engine::want_nothing; 178 } 179 180 return perform(&engine::do_read, data.data(), 181 data.size(), ec, &bytes_transferred); 182} 183 184boost::asio::mutable_buffer engine::get_output( 185 const boost::asio::mutable_buffer& data) 186{ 187 int length = ::BIO_read(ext_bio_, 188 data.data(), static_cast<int>(data.size())); 189 190 return boost::asio::buffer(data, 191 length > 0 ? static_cast<std::size_t>(length) : 0); 192} 193 194boost::asio::const_buffer engine::put_input( 195 const boost::asio::const_buffer& data) 196{ 197 int length = ::BIO_write(ext_bio_, 198 data.data(), static_cast<int>(data.size())); 199 200 return boost::asio::buffer(data + 201 (length > 0 ? static_cast<std::size_t>(length) : 0)); 202} 203 204const boost::system::error_code& engine::map_error_code( 205 boost::system::error_code& ec) const 206{ 207 // We only want to map the error::eof code. 208 if (ec != boost::asio::error::eof) 209 return ec; 210 211 // If there's data yet to be read, it's an error. 212 if (BIO_wpending(ext_bio_)) 213 { 214 ec = boost::asio::ssl::error::stream_truncated; 215 return ec; 216 } 217 218 // SSL v2 doesn't provide a protocol-level shutdown, so an eof on the 219 // underlying transport is passed through. 220#if (OPENSSL_VERSION_NUMBER < 0x10100000L) 221 if (SSL_version(ssl_) == SSL2_VERSION) 222 return ec; 223#endif // (OPENSSL_VERSION_NUMBER < 0x10100000L) 224 225 // Otherwise, the peer should have negotiated a proper shutdown. 226 if ((::SSL_get_shutdown(ssl_) & SSL_RECEIVED_SHUTDOWN) == 0) 227 { 228 ec = boost::asio::ssl::error::stream_truncated; 229 } 230 231 return ec; 232} 233 234#if (OPENSSL_VERSION_NUMBER < 0x10000000L) 235boost::asio::detail::static_mutex& engine::accept_mutex() 236{ 237 static boost::asio::detail::static_mutex mutex = BOOST_ASIO_STATIC_MUTEX_INIT; 238 return mutex; 239} 240#endif // (OPENSSL_VERSION_NUMBER < 0x10000000L) 241 242engine::want engine::perform(int (engine::* op)(void*, std::size_t), 243 void* data, std::size_t length, boost::system::error_code& ec, 244 std::size_t* bytes_transferred) 245{ 246 std::size_t pending_output_before = ::BIO_ctrl_pending(ext_bio_); 247 ::ERR_clear_error(); 248 int result = (this->*op)(data, length); 249 int ssl_error = ::SSL_get_error(ssl_, result); 250 int sys_error = static_cast<int>(::ERR_get_error()); 251 std::size_t pending_output_after = ::BIO_ctrl_pending(ext_bio_); 252 253 if (ssl_error == SSL_ERROR_SSL) 254 { 255 ec = boost::system::error_code(sys_error, 256 boost::asio::error::get_ssl_category()); 257 return pending_output_after > pending_output_before 258 ? want_output : want_nothing; 259 } 260 261 if (ssl_error == SSL_ERROR_SYSCALL) 262 { 263 if (sys_error == 0) 264 { 265 ec = boost::asio::ssl::error::unspecified_system_error; 266 } 267 else 268 { 269 ec = boost::system::error_code(sys_error, 270 boost::asio::error::get_ssl_category()); 271 } 272 return pending_output_after > pending_output_before 273 ? want_output : want_nothing; 274 } 275 276 if (result > 0 && bytes_transferred) 277 *bytes_transferred = static_cast<std::size_t>(result); 278 279 if (ssl_error == SSL_ERROR_WANT_WRITE) 280 { 281 ec = boost::system::error_code(); 282 return want_output_and_retry; 283 } 284 else if (pending_output_after > pending_output_before) 285 { 286 ec = boost::system::error_code(); 287 return result > 0 ? want_output : want_output_and_retry; 288 } 289 else if (ssl_error == SSL_ERROR_WANT_READ) 290 { 291 ec = boost::system::error_code(); 292 return want_input_and_retry; 293 } 294 else if (ssl_error == SSL_ERROR_ZERO_RETURN) 295 { 296 ec = boost::asio::error::eof; 297 return want_nothing; 298 } 299 else if (ssl_error == SSL_ERROR_NONE) 300 { 301 ec = boost::system::error_code(); 302 return want_nothing; 303 } 304 else 305 { 306 ec = boost::asio::ssl::error::unexpected_result; 307 return want_nothing; 308 } 309} 310 311int engine::do_accept(void*, std::size_t) 312{ 313#if (OPENSSL_VERSION_NUMBER < 0x10000000L) 314 boost::asio::detail::static_mutex::scoped_lock lock(accept_mutex()); 315#endif // (OPENSSL_VERSION_NUMBER < 0x10000000L) 316 return ::SSL_accept(ssl_); 317} 318 319int engine::do_connect(void*, std::size_t) 320{ 321 return ::SSL_connect(ssl_); 322} 323 324int engine::do_shutdown(void*, std::size_t) 325{ 326 int result = ::SSL_shutdown(ssl_); 327 if (result == 0) 328 result = ::SSL_shutdown(ssl_); 329 return result; 330} 331 332int engine::do_read(void* data, std::size_t length) 333{ 334 return ::SSL_read(ssl_, data, 335 length < INT_MAX ? static_cast<int>(length) : INT_MAX); 336} 337 338int engine::do_write(void* data, std::size_t length) 339{ 340 return ::SSL_write(ssl_, data, 341 length < INT_MAX ? static_cast<int>(length) : INT_MAX); 342} 343 344} // namespace detail 345} // namespace ssl 346} // namespace asio 347} // namespace boost 348 349#include <boost/asio/detail/pop_options.hpp> 350 351#endif // BOOST_ASIO_SSL_DETAIL_IMPL_ENGINE_IPP 352