mirror of
https://github.com/matrix-construct/construct
synced 2025-03-14 05:20:17 +01:00
ircd::net: OpenSSL/net interface for peer certificates so m:: can hash/verify.
This commit is contained in:
parent
08469eb2a0
commit
6937e91fc3
5 changed files with 306 additions and 135 deletions
|
@ -83,6 +83,8 @@ namespace ircd::net
|
|||
|
||||
std::shared_ptr<socket> connect(const remote &, const milliseconds &timeout = 30000ms);
|
||||
bool disconnect(socket &, const dc &type = dc::SSL_NOTIFY) noexcept;
|
||||
|
||||
const_raw_buffer peer_cert_der(const mutable_raw_buffer &, const socket &);
|
||||
}
|
||||
|
||||
namespace ircd
|
||||
|
|
|
@ -70,11 +70,11 @@ struct ircd::net::socket
|
|||
void handle(std::weak_ptr<socket>, handler, const error_code &, const size_t &) noexcept;
|
||||
|
||||
public:
|
||||
// Getters for boost socket struct
|
||||
operator const ip::tcp::socket &() const { return sd; }
|
||||
operator ip::tcp::socket &() { return sd; }
|
||||
operator const SSL &() const;
|
||||
operator SSL &();
|
||||
|
||||
// Observers
|
||||
ip::tcp::endpoint remote() const; // getpeername(); throws if not conn
|
||||
ip::tcp::endpoint local() const; // getsockname(); throws if not conn/bound
|
||||
bool connected() const noexcept; // false on any sock errs
|
||||
|
|
|
@ -25,20 +25,46 @@
|
|||
#pragma once
|
||||
#define HAVE_IRCD_OPENSSL_H
|
||||
|
||||
// Forward declarations for OpenSSL because it is not included here.
|
||||
struct ssl_st;
|
||||
struct x509_st;
|
||||
|
||||
/// OpenSSL library interface. Provides things we need to expose from OpenSSL
|
||||
/// to the rest of the project. Anything that employs forward declared types
|
||||
/// here is lower level meant for definition files which may or may not include
|
||||
/// OpenSSL directly but use this interface for DRY nonetheless. Otherwise
|
||||
/// higher level wrappers should be used if available here.
|
||||
namespace ircd::openssl
|
||||
{
|
||||
IRCD_EXCEPTION(ircd::error, error)
|
||||
|
||||
struct init;
|
||||
|
||||
using SSL = ::ssl_st;
|
||||
using X509 = ::x509_st;
|
||||
|
||||
string_view version();
|
||||
|
||||
// Observers
|
||||
string_view error_string(const mutable_buffer &buf, const ulong &);
|
||||
ulong peek_error();
|
||||
|
||||
// Using these will clobber other libraries (like boost); so don't
|
||||
ulong get_error();
|
||||
void clear_error();
|
||||
|
||||
// Convert PEM string to X509
|
||||
X509 &read(X509 *out, const string_view &cert);
|
||||
|
||||
// Convert X509 certificate to DER encoding
|
||||
const_raw_buffer i2d(const mutable_raw_buffer &out, const X509 &);
|
||||
|
||||
// Convert PEM string to DER
|
||||
const_raw_buffer cert2d(const mutable_raw_buffer &out, const string_view &cert);
|
||||
|
||||
// Get X509 certificate from struct SSL (get SSL from a socket)
|
||||
const X509 &get_peer_cert(const SSL &);
|
||||
X509 &get_peer_cert(SSL &);
|
||||
}
|
||||
|
||||
struct ircd::openssl::init
|
||||
|
|
120
ircd/net.cc
120
ircd/net.cc
|
@ -57,6 +57,62 @@ ircd::net::init::~init()
|
|||
// socket (public)
|
||||
//
|
||||
|
||||
ircd::const_raw_buffer
|
||||
ircd::net::peer_cert_der(const mutable_raw_buffer &buf,
|
||||
const socket &socket)
|
||||
{
|
||||
const SSL &ssl(socket);
|
||||
const X509 &cert(openssl::get_peer_cert(ssl));
|
||||
return openssl::i2d(buf, cert);
|
||||
}
|
||||
|
||||
std::shared_ptr<ircd::net::socket>
|
||||
ircd::net::connect(const net::remote &remote,
|
||||
const milliseconds &timeout)
|
||||
{
|
||||
const asio::ip::tcp::endpoint ep
|
||||
{
|
||||
is_v6(remote)? asio::ip::tcp::endpoint
|
||||
{
|
||||
asio::ip::address_v6 { std::get<remote.IP>(remote) }, port(remote)
|
||||
}
|
||||
: asio::ip::tcp::endpoint
|
||||
{
|
||||
asio::ip::address_v4 { host4(remote) }, port(remote)
|
||||
},
|
||||
};
|
||||
|
||||
return connect(ep, timeout);
|
||||
}
|
||||
|
||||
std::shared_ptr<ircd::net::socket>
|
||||
ircd::net::connect(const ip::tcp::endpoint &remote,
|
||||
const milliseconds &timeout)
|
||||
{
|
||||
const auto ret(std::make_shared<socket>());
|
||||
ret->connect(remote, timeout);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::net::disconnect(socket &socket,
|
||||
const dc &type)
|
||||
noexcept try
|
||||
{
|
||||
socket.disconnect(type);
|
||||
return true;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
/*
|
||||
log::error("socket(%p): disconnect: type: %d: %s",
|
||||
this,
|
||||
int(type),
|
||||
e.what());
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::net::read(socket &socket,
|
||||
iov<mutable_buffer> &bufs)
|
||||
|
@ -799,53 +855,6 @@ ircd::net::socket::scope_timeout::release()
|
|||
return s != nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<ircd::net::socket>
|
||||
ircd::net::connect(const net::remote &remote,
|
||||
const milliseconds &timeout)
|
||||
{
|
||||
const asio::ip::tcp::endpoint ep
|
||||
{
|
||||
is_v6(remote)? asio::ip::tcp::endpoint
|
||||
{
|
||||
asio::ip::address_v6 { std::get<remote.IP>(remote) }, port(remote)
|
||||
}
|
||||
: asio::ip::tcp::endpoint
|
||||
{
|
||||
asio::ip::address_v4 { host4(remote) }, port(remote)
|
||||
},
|
||||
};
|
||||
|
||||
return connect(ep, timeout);
|
||||
}
|
||||
|
||||
std::shared_ptr<ircd::net::socket>
|
||||
ircd::net::connect(const ip::tcp::endpoint &remote,
|
||||
const milliseconds &timeout)
|
||||
{
|
||||
const auto ret(std::make_shared<socket>());
|
||||
ret->connect(remote, timeout);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::net::disconnect(socket &socket,
|
||||
const dc &type)
|
||||
noexcept try
|
||||
{
|
||||
socket.disconnect(type);
|
||||
return true;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
/*
|
||||
log::error("socket(%p): disconnect: type: %d: %s",
|
||||
this,
|
||||
int(type),
|
||||
e.what());
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// socket
|
||||
//
|
||||
|
@ -1392,6 +1401,23 @@ ircd::net::socket::set_timeout(const milliseconds &t,
|
|||
timer.async_wait(std::move(h));
|
||||
}
|
||||
|
||||
ircd::net::socket::operator
|
||||
SSL &()
|
||||
{
|
||||
assert(ssl.native_handle());
|
||||
return *ssl.native_handle();
|
||||
}
|
||||
|
||||
ircd::net::socket::operator
|
||||
const SSL &()
|
||||
const
|
||||
{
|
||||
using type = typename std::remove_const<decltype(socket::ssl)>::type;
|
||||
auto &ssl(const_cast<type &>(this->ssl));
|
||||
assert(ssl.native_handle());
|
||||
return *ssl.native_handle();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// net/asio.h
|
||||
|
|
289
ircd/openssl.cc
289
ircd/openssl.cc
|
@ -21,16 +21,199 @@
|
|||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/x509.h>
|
||||
|
||||
namespace ircd::openssl
|
||||
{
|
||||
template<class exception = ircd::error,
|
||||
template<class exception = openssl::error>
|
||||
static void throw_error(const ulong &);
|
||||
|
||||
template<class exception = openssl::error>
|
||||
static void throw_error();
|
||||
|
||||
template<class exception = openssl::error,
|
||||
int ERR_CODE = 0,
|
||||
class function,
|
||||
class... args>
|
||||
static int call(function&& f, args&&... a);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// openssl.h
|
||||
//
|
||||
|
||||
ircd::const_raw_buffer
|
||||
ircd::openssl::cert2d(const mutable_raw_buffer &out,
|
||||
const string_view &cert)
|
||||
{
|
||||
X509 x509 {0};
|
||||
return i2d(out, read(&x509, cert));
|
||||
}
|
||||
|
||||
X509 &
|
||||
ircd::openssl::read(X509 *out,
|
||||
const string_view &cert)
|
||||
{
|
||||
const custom_ptr<BIO> bp
|
||||
{
|
||||
BIO_new_mem_buf(data(cert), size(cert)),
|
||||
[](BIO *const bp) { BIO_free(bp); }
|
||||
};
|
||||
|
||||
X509 *const ret
|
||||
{
|
||||
PEM_read_bio_X509(bp.get(), &out, nullptr, nullptr)
|
||||
};
|
||||
|
||||
if(unlikely(ret != out))
|
||||
{
|
||||
throw_error();
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
return *ret;
|
||||
}
|
||||
|
||||
ircd::const_raw_buffer
|
||||
ircd::openssl::i2d(const mutable_raw_buffer &buf,
|
||||
const X509 &_cert)
|
||||
{
|
||||
auto &cert
|
||||
{
|
||||
const_cast<X509 &>(_cert)
|
||||
};
|
||||
|
||||
const int len
|
||||
{
|
||||
i2d_X509(&cert, nullptr)
|
||||
};
|
||||
|
||||
if(unlikely(len < 0))
|
||||
{
|
||||
throw_error();
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
if(unlikely(size(buf) < size_t(len)))
|
||||
throw error
|
||||
{
|
||||
"DER requires a %zu byte buffer, you supplied %zu bytes", len, size(buf)
|
||||
};
|
||||
|
||||
uint8_t *out(data(buf));
|
||||
const const_raw_buffer ret
|
||||
{
|
||||
data(buf),
|
||||
size_t(i2d_X509(&cert, &out))
|
||||
};
|
||||
|
||||
if(unlikely(size(ret) != size_t(len)))
|
||||
throw error();
|
||||
|
||||
assert(out - data(buf) == len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
X509 &
|
||||
ircd::openssl::get_peer_cert(SSL &ssl)
|
||||
{
|
||||
auto *const ret
|
||||
{
|
||||
SSL_get_peer_certificate(&ssl)
|
||||
};
|
||||
|
||||
assert(ret);
|
||||
return *ret;
|
||||
}
|
||||
|
||||
const X509 &
|
||||
ircd::openssl::get_peer_cert(const SSL &ssl)
|
||||
{
|
||||
const auto *const ret
|
||||
{
|
||||
SSL_get_peer_certificate(&ssl)
|
||||
};
|
||||
|
||||
assert(ret);
|
||||
return *ret;
|
||||
}
|
||||
|
||||
void
|
||||
ircd::openssl::clear_error()
|
||||
{
|
||||
ERR_clear_error();
|
||||
}
|
||||
|
||||
ulong
|
||||
ircd::openssl::get_error()
|
||||
{
|
||||
return ERR_get_error();
|
||||
}
|
||||
|
||||
ulong
|
||||
ircd::openssl::peek_error()
|
||||
{
|
||||
return ERR_peek_error();
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::openssl::error_string(const mutable_buffer &buf,
|
||||
const ulong &e)
|
||||
{
|
||||
ERR_error_string_n(e, data(buf), size(buf));
|
||||
return { data(buf), strnlen(data(buf), size(buf)) };
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::openssl::version()
|
||||
{
|
||||
return SSLeay_version(SSLEAY_VERSION);
|
||||
}
|
||||
|
||||
//
|
||||
// init
|
||||
//
|
||||
|
||||
ircd::openssl::init::init()
|
||||
{
|
||||
OPENSSL_init();
|
||||
ERR_load_crypto_strings();
|
||||
ERR_load_ERR_strings();
|
||||
|
||||
/*
|
||||
const auto their_id_callback
|
||||
{
|
||||
CRYPTO_THREADID_get_callback()
|
||||
};
|
||||
|
||||
assert(their_id_callback == nullptr);
|
||||
CRYPTO_THREADID_set_callback(locking::id_callback);
|
||||
*/
|
||||
|
||||
/*
|
||||
const auto their_locking_callback
|
||||
{
|
||||
CRYPTO_get_locking_callback()
|
||||
};
|
||||
|
||||
if(their_locking_callback)
|
||||
throw error("Overwrite their locking callback @ %p ???",
|
||||
their_locking_callback);
|
||||
|
||||
CRYPTO_set_locking_callback(locking::callback);
|
||||
*/
|
||||
}
|
||||
|
||||
ircd::openssl::init::~init()
|
||||
{
|
||||
//assert(CRYPTO_get_locking_callback() == locking::callback);
|
||||
//assert(CRYPTO_THREADID_get_callback() == locking::id_callback);
|
||||
|
||||
ERR_free_strings();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// hash.h
|
||||
|
@ -243,85 +426,6 @@ ircd::openssl::locking::reflect(const int &mode)
|
|||
return "?????";
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Misc internal
|
||||
//
|
||||
|
||||
void
|
||||
ircd::openssl::clear_error()
|
||||
{
|
||||
ERR_clear_error();
|
||||
}
|
||||
|
||||
ulong
|
||||
ircd::openssl::get_error()
|
||||
{
|
||||
return ERR_get_error();
|
||||
}
|
||||
|
||||
ulong
|
||||
ircd::openssl::peek_error()
|
||||
{
|
||||
return ERR_peek_error();
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::openssl::error_string(const mutable_buffer &buf,
|
||||
const ulong &e)
|
||||
{
|
||||
ERR_error_string_n(e, data(buf), size(buf));
|
||||
return { data(buf), strnlen(data(buf), size(buf)) };
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::openssl::version()
|
||||
{
|
||||
return SSLeay_version(SSLEAY_VERSION);
|
||||
}
|
||||
|
||||
//
|
||||
// init
|
||||
//
|
||||
|
||||
ircd::openssl::init::init()
|
||||
{
|
||||
OPENSSL_init();
|
||||
ERR_load_crypto_strings();
|
||||
ERR_load_ERR_strings();
|
||||
|
||||
/*
|
||||
const auto their_id_callback
|
||||
{
|
||||
CRYPTO_THREADID_get_callback()
|
||||
};
|
||||
|
||||
assert(their_id_callback == nullptr);
|
||||
CRYPTO_THREADID_set_callback(locking::id_callback);
|
||||
*/
|
||||
|
||||
/*
|
||||
const auto their_locking_callback
|
||||
{
|
||||
CRYPTO_get_locking_callback()
|
||||
};
|
||||
|
||||
if(their_locking_callback)
|
||||
throw error("Overwrite their locking callback @ %p ???",
|
||||
their_locking_callback);
|
||||
|
||||
CRYPTO_set_locking_callback(locking::callback);
|
||||
*/
|
||||
}
|
||||
|
||||
ircd::openssl::init::~init()
|
||||
{
|
||||
//assert(CRYPTO_get_locking_callback() == locking::callback);
|
||||
//assert(CRYPTO_THREADID_get_callback() == locking::id_callback);
|
||||
|
||||
ERR_free_strings();
|
||||
}
|
||||
|
||||
//
|
||||
// call()
|
||||
//
|
||||
|
@ -340,11 +444,24 @@ ircd::openssl::call(function&& f,
|
|||
};
|
||||
|
||||
if(unlikely(ret == ERR_CODE))
|
||||
{
|
||||
const unsigned long code{ERR_get_error()};
|
||||
const auto &msg{ERR_reason_error_string(code)?: "UNKNOWN ERROR"};
|
||||
throw exception("OpenSSL #%lu: %s", code, msg);
|
||||
}
|
||||
throw_error<exception>();
|
||||
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
|
||||
template<class exception>
|
||||
static void
|
||||
ircd::openssl::throw_error(const unsigned long &code)
|
||||
{
|
||||
const auto &msg{ERR_reason_error_string(code)?: "UNKNOWN ERROR"};
|
||||
throw exception("OpenSSL #%lu: %s", code, msg);
|
||||
}
|
||||
|
||||
template<class exception>
|
||||
static void
|
||||
ircd::openssl::throw_error()
|
||||
{
|
||||
const auto code{get_error()};
|
||||
throw_error(code);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue