$include_dir="/home/hyper-archives/boost-commit/include"; include("$include_dir/msg-header.inc") ?>
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