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 84#if defined(BOOST_ASIO_HAS_MOVE) 85engine& engine::operator=(engine&& other) BOOST_ASIO_NOEXCEPT 86{ 87 if (this != &other) 88 { 89 ssl_ = other.ssl_; 90 ext_bio_ = other.ext_bio_; 91 other.ssl_ = 0; 92 other.ext_bio_ = 0; 93 } 94 return *this; 95} 96#endif // defined(BOOST_ASIO_HAS_MOVE) 97 98SSL* engine::native_handle() 99{ 100 return ssl_; 101} 102 103boost::system::error_code engine::set_verify_mode( 104 verify_mode v, boost::system::error_code& ec) 105{ 106 ::SSL_set_verify(ssl_, v, ::SSL_get_verify_callback(ssl_)); 107 108 ec = boost::system::error_code(); 109 return ec; 110} 111 112boost::system::error_code engine::set_verify_depth( 113 int depth, boost::system::error_code& ec) 114{ 115 ::SSL_set_verify_depth(ssl_, depth); 116 117 ec = boost::system::error_code(); 118 return ec; 119} 120 121boost::system::error_code engine::set_verify_callback( 122 verify_callback_base* callback, boost::system::error_code& ec) 123{ 124 if (SSL_get_app_data(ssl_)) 125 delete static_cast<verify_callback_base*>(SSL_get_app_data(ssl_)); 126 127 SSL_set_app_data(ssl_, callback); 128 129 ::SSL_set_verify(ssl_, ::SSL_get_verify_mode(ssl_), 130 &engine::verify_callback_function); 131 132 ec = boost::system::error_code(); 133 return ec; 134} 135 136int engine::verify_callback_function(int preverified, X509_STORE_CTX* ctx) 137{ 138 if (ctx) 139 { 140 if (SSL* ssl = static_cast<SSL*>( 141 ::X509_STORE_CTX_get_ex_data( 142 ctx, ::SSL_get_ex_data_X509_STORE_CTX_idx()))) 143 { 144 if (SSL_get_app_data(ssl)) 145 { 146 verify_callback_base* callback = 147 static_cast<verify_callback_base*>( 148 SSL_get_app_data(ssl)); 149 150 verify_context verify_ctx(ctx); 151 return callback->call(preverified != 0, verify_ctx) ? 1 : 0; 152 } 153 } 154 } 155 156 return 0; 157} 158 159engine::want engine::handshake( 160 stream_base::handshake_type type, boost::system::error_code& ec) 161{ 162 return perform((type == boost::asio::ssl::stream_base::client) 163 ? &engine::do_connect : &engine::do_accept, 0, 0, ec, 0); 164} 165 166engine::want engine::shutdown(boost::system::error_code& ec) 167{ 168 return perform(&engine::do_shutdown, 0, 0, ec, 0); 169} 170 171engine::want engine::write(const boost::asio::const_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_write, 181 const_cast<void*>(data.data()), 182 data.size(), ec, &bytes_transferred); 183} 184 185engine::want engine::read(const boost::asio::mutable_buffer& data, 186 boost::system::error_code& ec, std::size_t& bytes_transferred) 187{ 188 if (data.size() == 0) 189 { 190 ec = boost::system::error_code(); 191 return engine::want_nothing; 192 } 193 194 return perform(&engine::do_read, data.data(), 195 data.size(), ec, &bytes_transferred); 196} 197 198boost::asio::mutable_buffer engine::get_output( 199 const boost::asio::mutable_buffer& data) 200{ 201 int length = ::BIO_read(ext_bio_, 202 data.data(), static_cast<int>(data.size())); 203 204 return boost::asio::buffer(data, 205 length > 0 ? static_cast<std::size_t>(length) : 0); 206} 207 208boost::asio::const_buffer engine::put_input( 209 const boost::asio::const_buffer& data) 210{ 211 int length = ::BIO_write(ext_bio_, 212 data.data(), static_cast<int>(data.size())); 213 214 return boost::asio::buffer(data + 215 (length > 0 ? static_cast<std::size_t>(length) : 0)); 216} 217 218const boost::system::error_code& engine::map_error_code( 219 boost::system::error_code& ec) const 220{ 221 // We only want to map the error::eof code. 222 if (ec != boost::asio::error::eof) 223 return ec; 224 225 // If there's data yet to be read, it's an error. 226 if (BIO_wpending(ext_bio_)) 227 { 228 ec = boost::asio::ssl::error::stream_truncated; 229 return ec; 230 } 231 232 // SSL v2 doesn't provide a protocol-level shutdown, so an eof on the 233 // underlying transport is passed through. 234#if (OPENSSL_VERSION_NUMBER < 0x10100000L) 235 if (SSL_version(ssl_) == SSL2_VERSION) 236 return ec; 237#endif // (OPENSSL_VERSION_NUMBER < 0x10100000L) 238 239 // Otherwise, the peer should have negotiated a proper shutdown. 240 if ((::SSL_get_shutdown(ssl_) & SSL_RECEIVED_SHUTDOWN) == 0) 241 { 242 ec = boost::asio::ssl::error::stream_truncated; 243 } 244 245 return ec; 246} 247 248#if (OPENSSL_VERSION_NUMBER < 0x10000000L) 249boost::asio::detail::static_mutex& engine::accept_mutex() 250{ 251 static boost::asio::detail::static_mutex mutex = BOOST_ASIO_STATIC_MUTEX_INIT; 252 return mutex; 253} 254#endif // (OPENSSL_VERSION_NUMBER < 0x10000000L) 255 256engine::want engine::perform(int (engine::* op)(void*, std::size_t), 257 void* data, std::size_t length, boost::system::error_code& ec, 258 std::size_t* bytes_transferred) 259{ 260 std::size_t pending_output_before = ::BIO_ctrl_pending(ext_bio_); 261 ::ERR_clear_error(); 262 int result = (this->*op)(data, length); 263 int ssl_error = ::SSL_get_error(ssl_, result); 264 int sys_error = static_cast<int>(::ERR_get_error()); 265 std::size_t pending_output_after = ::BIO_ctrl_pending(ext_bio_); 266 267 if (ssl_error == SSL_ERROR_SSL) 268 { 269 ec = boost::system::error_code(sys_error, 270 boost::asio::error::get_ssl_category()); 271 return pending_output_after > pending_output_before 272 ? want_output : want_nothing; 273 } 274 275 if (ssl_error == SSL_ERROR_SYSCALL) 276 { 277 if (sys_error == 0) 278 { 279 ec = boost::asio::ssl::error::unspecified_system_error; 280 } 281 else 282 { 283 ec = boost::system::error_code(sys_error, 284 boost::asio::error::get_ssl_category()); 285 } 286 return pending_output_after > pending_output_before 287 ? want_output : want_nothing; 288 } 289 290 if (result > 0 && bytes_transferred) 291 *bytes_transferred = static_cast<std::size_t>(result); 292 293 if (ssl_error == SSL_ERROR_WANT_WRITE) 294 { 295 ec = boost::system::error_code(); 296 return want_output_and_retry; 297 } 298 else if (pending_output_after > pending_output_before) 299 { 300 ec = boost::system::error_code(); 301 return result > 0 ? want_output : want_output_and_retry; 302 } 303 else if (ssl_error == SSL_ERROR_WANT_READ) 304 { 305 ec = boost::system::error_code(); 306 return want_input_and_retry; 307 } 308 else if (ssl_error == SSL_ERROR_ZERO_RETURN) 309 { 310 ec = boost::asio::error::eof; 311 return want_nothing; 312 } 313 else if (ssl_error == SSL_ERROR_NONE) 314 { 315 ec = boost::system::error_code(); 316 return want_nothing; 317 } 318 else 319 { 320 ec = boost::asio::ssl::error::unexpected_result; 321 return want_nothing; 322 } 323} 324 325int engine::do_accept(void*, std::size_t) 326{ 327#if (OPENSSL_VERSION_NUMBER < 0x10000000L) 328 boost::asio::detail::static_mutex::scoped_lock lock(accept_mutex()); 329#endif // (OPENSSL_VERSION_NUMBER < 0x10000000L) 330 return ::SSL_accept(ssl_); 331} 332 333int engine::do_connect(void*, std::size_t) 334{ 335 return ::SSL_connect(ssl_); 336} 337 338int engine::do_shutdown(void*, std::size_t) 339{ 340 int result = ::SSL_shutdown(ssl_); 341 if (result == 0) 342 result = ::SSL_shutdown(ssl_); 343 return result; 344} 345 346int engine::do_read(void* data, std::size_t length) 347{ 348 return ::SSL_read(ssl_, data, 349 length < INT_MAX ? static_cast<int>(length) : INT_MAX); 350} 351 352int engine::do_write(void* data, std::size_t length) 353{ 354 return ::SSL_write(ssl_, data, 355 length < INT_MAX ? static_cast<int>(length) : INT_MAX); 356} 357 358} // namespace detail 359} // namespace ssl 360} // namespace asio 361} // namespace boost 362 363#include <boost/asio/detail/pop_options.hpp> 364 365#endif // BOOST_ASIO_SSL_DETAIL_IMPL_ENGINE_IPP 366