Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r84325 - in trunk/boost/asio/ssl: . impl
From: chris_at_[hidden]
Date: 2013-05-17 07:04:13


Author: chris_kohlhoff
Date: 2013-05-17 07:04:11 EDT (Fri, 17 May 2013)
New Revision: 84325
URL: http://svn.boost.org/trac/boost/changeset/84325

Log:
Allow loading of SSL certificate and key data from memory buffers.

Added new buffer-based interfaces:
add_certificate_authority, use_certificate, use_certificate_chain,
use_private_key, use_rsa_private_key, use_tmp_dh.

Thanks go to Nick Jones <nick dot fa dot jones at gmail dot com>, on
whose work this commit is based.

Text files modified:
   trunk/boost/asio/ssl/context.hpp | 198 ++++++++++++++++++++++
   trunk/boost/asio/ssl/impl/context.ipp | 356 +++++++++++++++++++++++++++++++++++++--
   2 files changed, 534 insertions(+), 20 deletions(-)

Modified: trunk/boost/asio/ssl/context.hpp
==============================================================================
--- trunk/boost/asio/ssl/context.hpp (original)
+++ trunk/boost/asio/ssl/context.hpp 2013-05-17 07:04:11 EDT (Fri, 17 May 2013)
@@ -22,6 +22,7 @@
 # include <boost/asio/ssl/context_service.hpp>
 #else // defined(BOOST_ASIO_ENABLE_OLD_SSL)
 # include <string>
+# include <boost/asio/buffer.hpp>
 # include <boost/asio/io_service.hpp>
 # include <boost/asio/ssl/context_base.hpp>
 # include <boost/asio/ssl/detail/openssl_types.hpp>
@@ -268,6 +269,35 @@
   BOOST_ASIO_DECL boost::system::error_code load_verify_file(
       const std::string& filename, boost::system::error_code& ec);
 
+ /// Add certification authority for performing verification.
+ /**
+ * This function is used to add one trusted certification authority
+ * from a memory buffer.
+ *
+ * @param ca The buffer containing the certification authority certificate.
+ * The certificate must use the PEM format.
+ *
+ * @throws boost::system::system_error Thrown on failure.
+ *
+ * @note Calls @c SSL_CTX_get_cert_store and @c X509_STORE_add_cert.
+ */
+ BOOST_ASIO_DECL void add_certificate_authority(const const_buffer& ca);
+
+ /// Add certification authority for performing verification.
+ /**
+ * This function is used to add one trusted certification authority
+ * from a memory buffer.
+ *
+ * @param ca The buffer containing the certification authority certificate.
+ * The certificate must use the PEM format.
+ *
+ * @param ec Set to indicate what error occurred, if any.
+ *
+ * @note Calls @c SSL_CTX_get_cert_store and @c X509_STORE_add_cert.
+ */
+ BOOST_ASIO_DECL boost::system::error_code add_certificate_authority(
+ const const_buffer& ca, boost::system::error_code& ec);
+
   /// Configures the context to use the default directories for finding
   /// certification authority certificates.
   /**
@@ -328,6 +358,37 @@
   BOOST_ASIO_DECL boost::system::error_code add_verify_path(
       const std::string& path, boost::system::error_code& ec);
 
+ /// Use a certificate from a memory buffer.
+ /**
+ * This function is used to load a certificate into the context from a buffer.
+ *
+ * @param certificate The buffer containing the certificate.
+ *
+ * @param format The certificate format (ASN.1 or PEM).
+ *
+ * @throws boost::system::system_error Thrown on failure.
+ *
+ * @note Calls @c SSL_CTX_use_certificate or SSL_CTX_use_certificate_ASN1.
+ */
+ BOOST_ASIO_DECL void use_certificate(
+ const const_buffer& certificate, file_format format);
+
+ /// Use a certificate from a memory buffer.
+ /**
+ * This function is used to load a certificate into the context from a buffer.
+ *
+ * @param certificate The buffer containing the certificate.
+ *
+ * @param format The certificate format (ASN.1 or PEM).
+ *
+ * @param ec Set to indicate what error occurred, if any.
+ *
+ * @note Calls @c SSL_CTX_use_certificate or SSL_CTX_use_certificate_ASN1.
+ */
+ BOOST_ASIO_DECL boost::system::error_code use_certificate(
+ const const_buffer& certificate, file_format format,
+ boost::system::error_code& ec);
+
   /// Use a certificate from a file.
   /**
    * This function is used to load a certificate into the context from a file.
@@ -359,6 +420,35 @@
       const std::string& filename, file_format format,
       boost::system::error_code& ec);
 
+ /// Use a certificate chain from a memory buffer.
+ /**
+ * This function is used to load a certificate chain into the context from a
+ * buffer.
+ *
+ * @param chain The buffer containing the certificate chain. The certificate
+ * chain must use the PEM format.
+ *
+ * @throws boost::system::system_error Thrown on failure.
+ *
+ * @note Calls @c SSL_CTX_use_certificate and SSL_CTX_add_extra_chain_cert.
+ */
+ BOOST_ASIO_DECL void use_certificate_chain(const const_buffer& chain);
+
+ /// Use a certificate chain from a memory buffer.
+ /**
+ * This function is used to load a certificate chain into the context from a
+ * buffer.
+ *
+ * @param chain The buffer containing the certificate chain. The certificate
+ * chain must use the PEM format.
+ *
+ * @param ec Set to indicate what error occurred, if any.
+ *
+ * @note Calls @c SSL_CTX_use_certificate and SSL_CTX_add_extra_chain_cert.
+ */
+ BOOST_ASIO_DECL boost::system::error_code use_certificate_chain(
+ const const_buffer& chain, boost::system::error_code& ec);
+
   /// Use a certificate chain from a file.
   /**
    * This function is used to load a certificate chain into the context from a
@@ -388,6 +478,37 @@
   BOOST_ASIO_DECL boost::system::error_code use_certificate_chain_file(
       const std::string& filename, boost::system::error_code& ec);
 
+ /// Use a private key from a memory buffer.
+ /**
+ * This function is used to load a private key into the context from a buffer.
+ *
+ * @param private_key The buffer containing the private key.
+ *
+ * @param format The private key format (ASN.1 or PEM).
+ *
+ * @throws boost::system::system_error Thrown on failure.
+ *
+ * @note Calls @c SSL_CTX_use_PrivateKey or SSL_CTX_use_PrivateKey_ASN1.
+ */
+ BOOST_ASIO_DECL void use_private_key(
+ const const_buffer& private_key, file_format format);
+
+ /// Use a private key from a memory buffer.
+ /**
+ * This function is used to load a private key into the context from a buffer.
+ *
+ * @param private_key The buffer containing the private key.
+ *
+ * @param format The private key format (ASN.1 or PEM).
+ *
+ * @param ec Set to indicate what error occurred, if any.
+ *
+ * @note Calls @c SSL_CTX_use_PrivateKey or SSL_CTX_use_PrivateKey_ASN1.
+ */
+ BOOST_ASIO_DECL boost::system::error_code use_private_key(
+ const const_buffer& private_key, file_format format,
+ boost::system::error_code& ec);
+
   /// Use a private key from a file.
   /**
    * This function is used to load a private key into the context from a file.
@@ -419,6 +540,39 @@
       const std::string& filename, file_format format,
       boost::system::error_code& ec);
 
+ /// Use an RSA private key from a memory buffer.
+ /**
+ * This function is used to load an RSA private key into the context from a
+ * buffer.
+ *
+ * @param private_key The buffer containing the RSA private key.
+ *
+ * @param format The private key format (ASN.1 or PEM).
+ *
+ * @throws boost::system::system_error Thrown on failure.
+ *
+ * @note Calls @c SSL_CTX_use_RSAPrivateKey or SSL_CTX_use_RSAPrivateKey_ASN1.
+ */
+ BOOST_ASIO_DECL void use_rsa_private_key(
+ const const_buffer& private_key, file_format format);
+
+ /// Use an RSA private key from a memory buffer.
+ /**
+ * This function is used to load an RSA private key into the context from a
+ * buffer.
+ *
+ * @param private_key The buffer containing the RSA private key.
+ *
+ * @param format The private key format (ASN.1 or PEM).
+ *
+ * @param ec Set to indicate what error occurred, if any.
+ *
+ * @note Calls @c SSL_CTX_use_RSAPrivateKey or SSL_CTX_use_RSAPrivateKey_ASN1.
+ */
+ BOOST_ASIO_DECL boost::system::error_code use_rsa_private_key(
+ const const_buffer& private_key, file_format format,
+ boost::system::error_code& ec);
+
   /// Use an RSA private key from a file.
   /**
    * This function is used to load an RSA private key into the context from a
@@ -452,6 +606,37 @@
       const std::string& filename, file_format format,
       boost::system::error_code& ec);
 
+ /// Use the specified memory buffer to obtain the temporary Diffie-Hellman
+ /// parameters.
+ /**
+ * This function is used to load Diffie-Hellman parameters into the context
+ * from a buffer.
+ *
+ * @param dh The memory buffer containing the Diffie-Hellman parameters. The
+ * buffer must use the PEM format.
+ *
+ * @throws boost::system::system_error Thrown on failure.
+ *
+ * @note Calls @c SSL_CTX_set_tmp_dh.
+ */
+ BOOST_ASIO_DECL void use_tmp_dh(const const_buffer& dh);
+
+ /// Use the specified memory buffer to obtain the temporary Diffie-Hellman
+ /// parameters.
+ /**
+ * This function is used to load Diffie-Hellman parameters into the context
+ * from a buffer.
+ *
+ * @param dh The memory buffer containing the Diffie-Hellman parameters. The
+ * buffer must use the PEM format.
+ *
+ * @param ec Set to indicate what error occurred, if any.
+ *
+ * @note Calls @c SSL_CTX_set_tmp_dh.
+ */
+ BOOST_ASIO_DECL boost::system::error_code use_tmp_dh(
+ const const_buffer& dh, boost::system::error_code& ec);
+
   /// Use the specified file to obtain the temporary Diffie-Hellman parameters.
   /**
    * This function is used to load Diffie-Hellman parameters into the context
@@ -523,6 +708,12 @@
       boost::system::error_code& ec);
 
 private:
+ struct bio_cleanup;
+ struct x509_cleanup;
+ struct evp_pkey_cleanup;
+ struct rsa_cleanup;
+ struct dh_cleanup;
+
   // Helper function used to set a peer certificate verification callback.
   BOOST_ASIO_DECL boost::system::error_code do_set_verify_callback(
       detail::verify_callback_base* callback, boost::system::error_code& ec);
@@ -539,6 +730,13 @@
   BOOST_ASIO_DECL static int password_callback_function(
       char* buf, int size, int purpose, void* data);
 
+ // Helper function to set the temporary Diffie-Hellman parameters from a BIO.
+ BOOST_ASIO_DECL boost::system::error_code do_use_tmp_dh(
+ BIO* bio, boost::system::error_code& ec);
+
+ // Helper function to make a BIO from a memory buffer.
+ BOOST_ASIO_DECL BIO* make_buffer_bio(const const_buffer& b);
+
   // The underlying native implementation.
   native_handle_type handle_;
 

Modified: trunk/boost/asio/ssl/impl/context.ipp
==============================================================================
--- trunk/boost/asio/ssl/impl/context.ipp (original)
+++ trunk/boost/asio/ssl/impl/context.ipp 2013-05-17 07:04:11 EDT (Fri, 17 May 2013)
@@ -34,6 +34,36 @@
 
 #if !defined(BOOST_ASIO_ENABLE_OLD_SSL)
 
+struct context::bio_cleanup
+{
+ BIO* p;
+ ~bio_cleanup() { if (p) ::BIO_free(p); }
+};
+
+struct context::x509_cleanup
+{
+ X509* p;
+ ~x509_cleanup() { if (p) ::X509_free(p); }
+};
+
+struct context::evp_pkey_cleanup
+{
+ EVP_PKEY* p;
+ ~evp_pkey_cleanup() { if (p) ::EVP_PKEY_free(p); }
+};
+
+struct context::rsa_cleanup
+{
+ RSA* p;
+ ~rsa_cleanup() { if (p) ::RSA_free(p); }
+};
+
+struct context::dh_cleanup
+{
+ DH* p;
+ ~dh_cleanup() { if (p) ::DH_free(p); }
+};
+
 context::context(context::method m)
   : handle_(0)
 {
@@ -264,6 +294,41 @@
   return ec;
 }
 
+void context::add_certificate_authority(const const_buffer& ca)
+{
+ boost::system::error_code ec;
+ add_certificate_authority(ca, ec);
+ boost::asio::detail::throw_error(ec, "add_certificate_authority");
+}
+
+boost::system::error_code context::add_certificate_authority(
+ const const_buffer& ca, boost::system::error_code& ec)
+{
+ ::ERR_clear_error();
+
+ bio_cleanup bio = { make_buffer_bio(ca) };
+ if (bio.p)
+ {
+ x509_cleanup cert = { ::PEM_read_bio_X509(bio.p, 0, 0, 0) };
+ if (cert.p)
+ {
+ if (X509_STORE* store = ::SSL_CTX_get_cert_store(handle_))
+ {
+ if (::X509_STORE_add_cert(store, cert.p) == 1)
+ {
+ ec = boost::system::error_code();
+ return ec;
+ }
+ }
+ }
+ }
+
+ ec = boost::system::error_code(
+ static_cast<int>(::ERR_get_error()),
+ boost::asio::error::get_ssl_category());
+ return ec;
+}
+
 void context::set_default_verify_paths()
 {
   boost::system::error_code ec;
@@ -308,6 +373,57 @@
   return ec;
 }
 
+void context::use_certificate(
+ const const_buffer& certificate, file_format format)
+{
+ boost::system::error_code ec;
+ use_certificate(certificate, format, ec);
+ boost::asio::detail::throw_error(ec, "use_certificate");
+}
+
+boost::system::error_code context::use_certificate(
+ const const_buffer& certificate, file_format format,
+ boost::system::error_code& ec)
+{
+ ::ERR_clear_error();
+
+ if (format == context_base::asn1)
+ {
+ if (::SSL_CTX_use_certificate_ASN1(handle_, buffer_size(certificate),
+ buffer_cast<const unsigned char*>(certificate)) == 1)
+ {
+ ec = boost::system::error_code();
+ return ec;
+ }
+ }
+ else if (format == context_base::pem)
+ {
+ bio_cleanup bio = { make_buffer_bio(certificate) };
+ if (bio.p)
+ {
+ x509_cleanup cert = { ::PEM_read_bio_X509(bio.p, 0, 0, 0) };
+ if (cert.p)
+ {
+ if (::SSL_CTX_use_certificate(handle_, cert.p) == 1)
+ {
+ ec = boost::system::error_code();
+ return ec;
+ }
+ }
+ }
+ }
+ else
+ {
+ ec = boost::asio::error::invalid_argument;
+ return ec;
+ }
+
+ ec = boost::system::error_code(
+ static_cast<int>(::ERR_get_error()),
+ boost::asio::error::get_ssl_category());
+ return ec;
+}
+
 void context::use_certificate_file(
     const std::string& filename, file_format format)
 {
@@ -348,6 +464,76 @@
   return ec;
 }
 
+void context::use_certificate_chain(const const_buffer& chain)
+{
+ boost::system::error_code ec;
+ use_certificate_chain(chain, ec);
+ boost::asio::detail::throw_error(ec, "use_certificate_chain");
+}
+
+boost::system::error_code context::use_certificate_chain(
+ const const_buffer& chain, boost::system::error_code& ec)
+{
+ ::ERR_clear_error();
+
+ bio_cleanup bio = { make_buffer_bio(chain) };
+ if (bio.p)
+ {
+ x509_cleanup cert = {
+ ::PEM_read_bio_X509_AUX(bio.p, 0,
+ handle_->default_passwd_callback,
+ handle_->default_passwd_callback_userdata) };
+ if (!cert.p)
+ {
+ ec = boost::system::error_code(ERR_R_PEM_LIB,
+ boost::asio::error::get_ssl_category());
+ return ec;
+ }
+
+ int result = ::SSL_CTX_use_certificate(handle_, cert.p);
+ if (result == 0 || ::ERR_peek_error() != 0)
+ {
+ ec = boost::system::error_code(
+ static_cast<int>(::ERR_get_error()),
+ boost::asio::error::get_ssl_category());
+ return ec;
+ }
+
+ if (handle_->extra_certs)
+ {
+ ::sk_X509_pop_free(handle_->extra_certs, X509_free);
+ handle_->extra_certs = 0;
+ }
+
+ while (X509* cacert = ::PEM_read_bio_X509(bio.p, 0,
+ handle_->default_passwd_callback,
+ handle_->default_passwd_callback_userdata))
+ {
+ if (!::SSL_CTX_add_extra_chain_cert(handle_, cacert))
+ {
+ ec = boost::system::error_code(
+ static_cast<int>(::ERR_get_error()),
+ boost::asio::error::get_ssl_category());
+ return ec;
+ }
+ }
+
+ result = ::ERR_peek_last_error();
+ if ((ERR_GET_LIB(result) == ERR_LIB_PEM)
+ && (ERR_GET_REASON(result) == PEM_R_NO_START_LINE))
+ {
+ ::ERR_clear_error();
+ ec = boost::system::error_code();
+ return ec;
+ }
+ }
+
+ ec = boost::system::error_code(
+ static_cast<int>(::ERR_get_error()),
+ boost::asio::error::get_ssl_category());
+ return ec;
+}
+
 void context::use_certificate_chain_file(const std::string& filename)
 {
   boost::system::error_code ec;
@@ -370,6 +556,55 @@
   return ec;
 }
 
+void context::use_private_key(
+ const const_buffer& private_key, context::file_format format)
+{
+ boost::system::error_code ec;
+ use_private_key(private_key, format, ec);
+ boost::asio::detail::throw_error(ec, "use_private_key");
+}
+
+boost::system::error_code context::use_private_key(
+ const const_buffer& private_key, context::file_format format,
+ boost::system::error_code& ec)
+{
+ ::ERR_clear_error();
+
+ bio_cleanup bio = { make_buffer_bio(private_key) };
+ if (bio.p)
+ {
+ evp_pkey_cleanup evp_private_key = { 0 };
+ switch (format)
+ {
+ case context_base::asn1:
+ evp_private_key.p = ::d2i_PrivateKey_bio(bio.p, 0);
+ break;
+ case context_base::pem:
+ evp_private_key.p = ::PEM_read_bio_PrivateKey(bio.p, 0, 0, 0);
+ break;
+ default:
+ {
+ ec = boost::asio::error::invalid_argument;
+ return ec;
+ }
+ }
+
+ if (evp_private_key.p)
+ {
+ if (::SSL_CTX_use_PrivateKey(handle_, evp_private_key.p) == 1)
+ {
+ ec = boost::system::error_code();
+ return ec;
+ }
+ }
+ }
+
+ ec = boost::system::error_code(
+ static_cast<int>(::ERR_get_error()),
+ boost::asio::error::get_ssl_category());
+ return ec;
+}
+
 void context::use_private_key_file(
     const std::string& filename, context::file_format format)
 {
@@ -378,6 +613,55 @@
   boost::asio::detail::throw_error(ec, "use_private_key_file");
 }
 
+void context::use_rsa_private_key(
+ const const_buffer& private_key, context::file_format format)
+{
+ boost::system::error_code ec;
+ use_rsa_private_key(private_key, format, ec);
+ boost::asio::detail::throw_error(ec, "use_rsa_private_key");
+}
+
+boost::system::error_code context::use_rsa_private_key(
+ const const_buffer& private_key, context::file_format format,
+ boost::system::error_code& ec)
+{
+ ::ERR_clear_error();
+
+ bio_cleanup bio = { make_buffer_bio(private_key) };
+ if (bio.p)
+ {
+ rsa_cleanup rsa_private_key = { 0 };
+ switch (format)
+ {
+ case context_base::asn1:
+ rsa_private_key.p = ::d2i_RSAPrivateKey_bio(bio.p, 0);
+ break;
+ case context_base::pem:
+ rsa_private_key.p = ::PEM_read_bio_RSAPrivateKey(bio.p, 0, 0, 0);
+ break;
+ default:
+ {
+ ec = boost::asio::error::invalid_argument;
+ return ec;
+ }
+ }
+
+ if (rsa_private_key.p)
+ {
+ if (::SSL_CTX_use_RSAPrivateKey(handle_, rsa_private_key.p) == 1)
+ {
+ ec = boost::system::error_code();
+ return ec;
+ }
+ }
+ }
+
+ ec = boost::system::error_code(
+ static_cast<int>(::ERR_get_error()),
+ boost::asio::error::get_ssl_category());
+ return ec;
+}
+
 boost::system::error_code context::use_private_key_file(
     const std::string& filename, context::file_format format,
     boost::system::error_code& ec)
@@ -451,6 +735,28 @@
   return ec;
 }
 
+void context::use_tmp_dh(const const_buffer& dh)
+{
+ boost::system::error_code ec;
+ use_tmp_dh(dh, ec);
+ boost::asio::detail::throw_error(ec, "use_tmp_dh");
+}
+
+boost::system::error_code context::use_tmp_dh(
+ const const_buffer& dh, boost::system::error_code& ec)
+{
+ bio_cleanup bio = { make_buffer_bio(dh) };
+ if (bio.p)
+ {
+ return do_use_tmp_dh(bio.p, ec);
+ }
+
+ ec = boost::system::error_code(
+ static_cast<int>(::ERR_get_error()),
+ boost::asio::error::get_ssl_category());
+ return ec;
+}
+
 void context::use_tmp_dh_file(const std::string& filename)
 {
   boost::system::error_code ec;
@@ -461,33 +767,36 @@
 boost::system::error_code context::use_tmp_dh_file(
     const std::string& filename, boost::system::error_code& ec)
 {
- ::BIO* bio = ::BIO_new_file(filename.c_str(), "r");
- if (!bio)
+ bio_cleanup bio = { ::BIO_new_file(filename.c_str(), "r") };
+ if (bio.p)
   {
- ec = boost::asio::error::invalid_argument;
- return ec;
+ return do_use_tmp_dh(bio.p, ec);
   }
 
- ::DH* dh = ::PEM_read_bio_DHparams(bio, 0, 0, 0);
- if (!dh)
- {
- ::BIO_free(bio);
- ec = boost::asio::error::invalid_argument;
- return ec;
- }
+ ec = boost::system::error_code(
+ static_cast<int>(::ERR_get_error()),
+ boost::asio::error::get_ssl_category());
+ return ec;
+}
 
- ::BIO_free(bio);
- long result = ::SSL_CTX_set_tmp_dh(handle_, dh);
- ::DH_free(dh);
- if (result != 1)
+boost::system::error_code context::do_use_tmp_dh(
+ BIO* bio, boost::system::error_code& ec)
+{
+ ::ERR_clear_error();
+
+ dh_cleanup dh = { ::PEM_read_bio_DHparams(bio, 0, 0, 0) };
+ if (dh.p)
   {
- ec = boost::system::error_code(
- static_cast<int>(::ERR_get_error()),
- boost::asio::error::get_ssl_category());
- return ec;
+ if (::SSL_CTX_set_tmp_dh(handle_, dh.p) == 1)
+ {
+ ec = boost::system::error_code();
+ return ec;
+ }
   }
 
- ec = boost::system::error_code();
+ ec = boost::system::error_code(
+ static_cast<int>(::ERR_get_error()),
+ boost::asio::error::get_ssl_category());
   return ec;
 }
 
@@ -577,6 +886,13 @@
   return 0;
 }
 
+BIO* context::make_buffer_bio(const const_buffer& b)
+{
+ return ::BIO_new_mem_buf(
+ const_cast<void*>(buffer_cast<const void*>(b)),
+ buffer_size(b));
+}
+
 #endif // !defined(BOOST_ASIO_ENABLE_OLD_SSL)
 
 } // namespace ssl


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk