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