// | |
// ssl/detail/openssl_context_service.hpp | |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
// | |
// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com | |
// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) | |
// | |
// Distributed under the Boost Software License, Version 1.0. (See accompanying | |
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
// | |
#ifndef BOOST_ASIO_SSL_DETAIL_OPENSSL_CONTEXT_SERVICE_HPP | |
#define BOOST_ASIO_SSL_DETAIL_OPENSSL_CONTEXT_SERVICE_HPP | |
#if defined(_MSC_VER) && (_MSC_VER >= 1200) | |
# pragma once | |
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) | |
#include <boost/asio/detail/config.hpp> | |
#include <cstring> | |
#include <string> | |
#include <boost/function.hpp> | |
#include <boost/asio/error.hpp> | |
#include <boost/asio/io_service.hpp> | |
#include <boost/asio/ssl/context_base.hpp> | |
#include <boost/asio/ssl/detail/openssl_init.hpp> | |
#include <boost/asio/ssl/detail/openssl_types.hpp> | |
#include <boost/asio/detail/push_options.hpp> | |
namespace boost { | |
namespace asio { | |
namespace ssl { | |
namespace detail { | |
class openssl_context_service | |
: public boost::asio::detail::service_base<openssl_context_service> | |
{ | |
public: | |
// The native type of the context. | |
typedef ::SSL_CTX* impl_type; | |
// The type for the password callback function object. | |
typedef boost::function<std::string(std::size_t, | |
context_base::password_purpose)> password_callback_type; | |
// Constructor. | |
openssl_context_service(boost::asio::io_service& io_service) | |
: boost::asio::detail::service_base<openssl_context_service>(io_service) | |
{ | |
} | |
// Destroy all user-defined handler objects owned by the service. | |
void shutdown_service() | |
{ | |
} | |
// Return a null context implementation. | |
static impl_type null() | |
{ | |
return 0; | |
} | |
// Create a new context implementation. | |
void create(impl_type& impl, context_base::method m) | |
{ | |
switch (m) | |
{ | |
case context_base::sslv2: | |
impl = ::SSL_CTX_new(::SSLv2_method()); | |
break; | |
case context_base::sslv2_client: | |
impl = ::SSL_CTX_new(::SSLv2_client_method()); | |
break; | |
case context_base::sslv2_server: | |
impl = ::SSL_CTX_new(::SSLv2_server_method()); | |
break; | |
case context_base::sslv3: | |
impl = ::SSL_CTX_new(::SSLv3_method()); | |
break; | |
case context_base::sslv3_client: | |
impl = ::SSL_CTX_new(::SSLv3_client_method()); | |
break; | |
case context_base::sslv3_server: | |
impl = ::SSL_CTX_new(::SSLv3_server_method()); | |
break; | |
case context_base::tlsv1: | |
impl = ::SSL_CTX_new(::TLSv1_method()); | |
break; | |
case context_base::tlsv1_client: | |
impl = ::SSL_CTX_new(::TLSv1_client_method()); | |
break; | |
case context_base::tlsv1_server: | |
impl = ::SSL_CTX_new(::TLSv1_server_method()); | |
break; | |
case context_base::sslv23: | |
impl = ::SSL_CTX_new(::SSLv23_method()); | |
break; | |
case context_base::sslv23_client: | |
impl = ::SSL_CTX_new(::SSLv23_client_method()); | |
break; | |
case context_base::sslv23_server: | |
impl = ::SSL_CTX_new(::SSLv23_server_method()); | |
break; | |
default: | |
impl = ::SSL_CTX_new(0); | |
break; | |
} | |
} | |
// Destroy a context implementation. | |
void destroy(impl_type& impl) | |
{ | |
if (impl != null()) | |
{ | |
if (impl->default_passwd_callback_userdata) | |
{ | |
password_callback_type* callback = | |
static_cast<password_callback_type*>( | |
impl->default_passwd_callback_userdata); | |
delete callback; | |
impl->default_passwd_callback_userdata = 0; | |
} | |
::SSL_CTX_free(impl); | |
impl = null(); | |
} | |
} | |
// Set options on the context. | |
boost::system::error_code set_options(impl_type& impl, | |
context_base::options o, boost::system::error_code& ec) | |
{ | |
::SSL_CTX_set_options(impl, o); | |
ec = boost::system::error_code(); | |
return ec; | |
} | |
// Set peer verification mode. | |
boost::system::error_code set_verify_mode(impl_type& impl, | |
context_base::verify_mode v, boost::system::error_code& ec) | |
{ | |
::SSL_CTX_set_verify(impl, v, 0); | |
ec = boost::system::error_code(); | |
return ec; | |
} | |
// Load a certification authority file for performing verification. | |
boost::system::error_code load_verify_file(impl_type& impl, | |
const std::string& filename, boost::system::error_code& ec) | |
{ | |
if (::SSL_CTX_load_verify_locations(impl, filename.c_str(), 0) != 1) | |
{ | |
ec = boost::asio::error::invalid_argument; | |
return ec; | |
} | |
ec = boost::system::error_code(); | |
return ec; | |
} | |
// Add a directory containing certification authority files to be used for | |
// performing verification. | |
boost::system::error_code add_verify_path(impl_type& impl, | |
const std::string& path, boost::system::error_code& ec) | |
{ | |
if (::SSL_CTX_load_verify_locations(impl, 0, path.c_str()) != 1) | |
{ | |
ec = boost::asio::error::invalid_argument; | |
return ec; | |
} | |
ec = boost::system::error_code(); | |
return ec; | |
} | |
// Use a certificate from a file. | |
boost::system::error_code use_certificate_file(impl_type& impl, | |
const std::string& filename, context_base::file_format format, | |
boost::system::error_code& ec) | |
{ | |
int file_type; | |
switch (format) | |
{ | |
case context_base::asn1: | |
file_type = SSL_FILETYPE_ASN1; | |
break; | |
case context_base::pem: | |
file_type = SSL_FILETYPE_PEM; | |
break; | |
default: | |
{ | |
ec = boost::asio::error::invalid_argument; | |
return ec; | |
} | |
} | |
if (::SSL_CTX_use_certificate_file(impl, filename.c_str(), file_type) != 1) | |
{ | |
ec = boost::asio::error::invalid_argument; | |
return ec; | |
} | |
ec = boost::system::error_code(); | |
return ec; | |
} | |
// Use a certificate chain from a file. | |
boost::system::error_code use_certificate_chain_file(impl_type& impl, | |
const std::string& filename, boost::system::error_code& ec) | |
{ | |
if (::SSL_CTX_use_certificate_chain_file(impl, filename.c_str()) != 1) | |
{ | |
ec = boost::asio::error::invalid_argument; | |
return ec; | |
} | |
ec = boost::system::error_code(); | |
return ec; | |
} | |
// Use a private key from a file. | |
boost::system::error_code use_private_key_file(impl_type& impl, | |
const std::string& filename, context_base::file_format format, | |
boost::system::error_code& ec) | |
{ | |
int file_type; | |
switch (format) | |
{ | |
case context_base::asn1: | |
file_type = SSL_FILETYPE_ASN1; | |
break; | |
case context_base::pem: | |
file_type = SSL_FILETYPE_PEM; | |
break; | |
default: | |
{ | |
ec = boost::asio::error::invalid_argument; | |
return ec; | |
} | |
} | |
if (::SSL_CTX_use_PrivateKey_file(impl, filename.c_str(), file_type) != 1) | |
{ | |
ec = boost::asio::error::invalid_argument; | |
return ec; | |
} | |
ec = boost::system::error_code(); | |
return ec; | |
} | |
// Use an RSA private key from a file. | |
boost::system::error_code use_rsa_private_key_file(impl_type& impl, | |
const std::string& filename, context_base::file_format format, | |
boost::system::error_code& ec) | |
{ | |
int file_type; | |
switch (format) | |
{ | |
case context_base::asn1: | |
file_type = SSL_FILETYPE_ASN1; | |
break; | |
case context_base::pem: | |
file_type = SSL_FILETYPE_PEM; | |
break; | |
default: | |
{ | |
ec = boost::asio::error::invalid_argument; | |
return ec; | |
} | |
} | |
if (::SSL_CTX_use_RSAPrivateKey_file( | |
impl, filename.c_str(), file_type) != 1) | |
{ | |
ec = boost::asio::error::invalid_argument; | |
return ec; | |
} | |
ec = boost::system::error_code(); | |
return ec; | |
} | |
// Use the specified file to obtain the temporary Diffie-Hellman parameters. | |
boost::system::error_code use_tmp_dh_file(impl_type& impl, | |
const std::string& filename, boost::system::error_code& ec) | |
{ | |
::BIO* bio = ::BIO_new_file(filename.c_str(), "r"); | |
if (!bio) | |
{ | |
ec = boost::asio::error::invalid_argument; | |
return ec; | |
} | |
::DH* dh = ::PEM_read_bio_DHparams(bio, 0, 0, 0); | |
if (!dh) | |
{ | |
::BIO_free(bio); | |
ec = boost::asio::error::invalid_argument; | |
return ec; | |
} | |
::BIO_free(bio); | |
int result = ::SSL_CTX_set_tmp_dh(impl, dh); | |
::DH_free(dh); | |
if (result != 1) | |
{ | |
ec = boost::asio::error::invalid_argument; | |
return ec; | |
} | |
ec = boost::system::error_code(); | |
return ec; | |
} | |
static int password_callback(char* buf, int size, int purpose, void* data) | |
{ | |
using namespace std; // For strncat and strlen. | |
if (data) | |
{ | |
password_callback_type* callback = | |
static_cast<password_callback_type*>(data); | |
std::string passwd = (*callback)(static_cast<std::size_t>(size), | |
purpose ? context_base::for_writing : context_base::for_reading); | |
*buf = '\0'; | |
strncat(buf, passwd.c_str(), size); | |
return strlen(buf); | |
} | |
return 0; | |
} | |
// Set the password callback. | |
template <typename Password_Callback> | |
boost::system::error_code set_password_callback(impl_type& impl, | |
Password_Callback callback, boost::system::error_code& ec) | |
{ | |
// Allocate callback function object if not already present. | |
if (impl->default_passwd_callback_userdata) | |
{ | |
password_callback_type* callback_function = | |
static_cast<password_callback_type*>( | |
impl->default_passwd_callback_userdata); | |
*callback_function = callback; | |
} | |
else | |
{ | |
password_callback_type* callback_function = | |
new password_callback_type(callback); | |
impl->default_passwd_callback_userdata = callback_function; | |
} | |
// Set the password callback. | |
SSL_CTX_set_default_passwd_cb(impl, | |
&openssl_context_service::password_callback); | |
ec = boost::system::error_code(); | |
return ec; | |
} | |
private: | |
// Ensure openssl is initialised. | |
openssl_init<> init_; | |
}; | |
} // namespace detail | |
} // namespace ssl | |
} // namespace asio | |
} // namespace boost | |
#include <boost/asio/detail/pop_options.hpp> | |
#endif // BOOST_ASIO_SSL_DETAIL_OPENSSL_CONTEXT_SERVICE_HPP |