blob: 41a533f3db53223060a7974ecd7a2ae67f73024b [file] [log] [blame]
//
// 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