blob: aafadc6ef8b9676894a78b6916d863d99759af1e [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <ignite/network/ssl/ssl_connection.h>
#include <ignite/network/ssl/ssl_gateway.h>
#include <ignite/network/ssl/secure_utils.h>
#include <ignite/common/detail/defer.h>
namespace ignite::network
{
ssl_connection::ssl_connection(void* ctx, const char* hostname)
{
ssl_gateway& gw = ssl_gateway::get_instance();
m_ssl = gw.SSL_new_(static_cast<SSL_CTX*>(ctx));
if (!m_ssl)
throw_last_secure_error("Can not create secure connection");
auto ssl_guard = detail::defer([&] {
gw.SSL_free_(static_cast<SSL*>(m_ssl));
m_ssl = nullptr;
});
m_bio_in = gw.BIO_new_(gw.BIO_s_mem_());
if (!m_bio_in)
throw_last_secure_error("Can not create input BIO");
auto bio_in_guard = detail::defer([&] {
gw.BIO_free_all_(static_cast<BIO*>(m_bio_in));
m_bio_in = nullptr;
});
m_bio_out = gw.BIO_new_(gw.BIO_s_mem_());
if (!m_bio_out)
throw_last_secure_error("Can not create output BIO");
// SSL now owns both BIOs; ssl_free will free them.
gw.SSL_set_bio_(static_cast<SSL*>(m_ssl), static_cast<BIO*>(m_bio_in), static_cast<BIO*>(m_bio_out));
bio_in_guard.release();
if (hostname)
{
int res = gw.SSL_set_tlsext_host_name_(static_cast<SSL*>(m_ssl), hostname);
if (res != SSL_OPERATION_SUCCESS)
throw_last_secure_error("Can not set host name for secure connection");
}
gw.SSL_set_connect_state_(static_cast<SSL*>(m_ssl));
ssl_guard.release();
}
ssl_connection::~ssl_connection()
{
ssl_gateway& gw = ssl_gateway::get_instance();
if (gw.is_loaded() && m_ssl)
gw.SSL_free_(static_cast<SSL*>(m_ssl)); // also frees bio_in and bio_out
}
bool ssl_connection::do_handshake()
{
ssl_gateway& gw = ssl_gateway::get_instance();
SSL* ssl = static_cast<SSL*>(m_ssl);
int res = gw.SSL_connect_(ssl);
if (res == SSL_OPERATION_SUCCESS)
return true;
if (is_actual_error(gw.SSL_get_error_(ssl, res)))
throw_last_secure_error("Can not establish secure connection");
return false;
}
int ssl_connection::write(const std::byte* data, int len)
{
ssl_gateway& gw = ssl_gateway::get_instance();
return gw.SSL_write_(static_cast<SSL*>(m_ssl), data, len);
}
int ssl_connection::read(std::byte* buf, int len)
{
ssl_gateway& gw = ssl_gateway::get_instance();
return gw.SSL_read_(static_cast<SSL*>(m_ssl), buf, len);
}
int ssl_connection::pending_decrypted() const
{
ssl_gateway& gw = ssl_gateway::get_instance();
return gw.SSL_pending_(static_cast<const SSL*>(m_ssl));
}
void ssl_connection::feed_input(const void* data, int len)
{
ssl_gateway& gw = ssl_gateway::get_instance();
if (gw.BIO_write_(static_cast<BIO*>(m_bio_in), data, len) <= 0)
throw_last_secure_error("Failed to process SSL data");
}
std::vector<std::byte> ssl_connection::drain_output()
{
ssl_gateway& gw = ssl_gateway::get_instance();
BIO* bio_out = static_cast<BIO*>(m_bio_out);
int available = gw.BIO_pending_(bio_out);
if (available <= 0)
return {};
std::vector<std::byte> buf(available);
int res = gw.BIO_read_(bio_out, buf.data(), available);
if (res <= 0)
return {};
buf.resize(res);
return buf;
}
const char* ssl_connection::version() const
{
ssl_gateway& gw = ssl_gateway::get_instance();
return gw.SSL_get_version_(static_cast<const SSL*>(m_ssl));
}
bool ssl_connection::is_fatal_error(int ssl_op_result) const
{
ssl_gateway& gw = ssl_gateway::get_instance();
return is_actual_error(gw.SSL_get_error_(static_cast<SSL*>(m_ssl), ssl_op_result));
}
bool ssl_connection::wants_read_input() const
{
ssl_gateway& gw = ssl_gateway::get_instance();
return gw.SSL_want_(static_cast<SSL*>(m_ssl)) == SSL_READING;
}
void ssl_connection::verify_peer() const
{
ssl_gateway& gw = ssl_gateway::get_instance();
SSL* ssl = static_cast<SSL*>(m_ssl);
X509* cert = gw.SSL_get_peer_certificate_(ssl);
if (cert)
gw.X509_free_(cert);
else
throw_last_secure_error("Remote host did not provide certificate");
long res = gw.SSL_get_verify_result_(ssl);
if (X509_V_OK != res)
throw_last_secure_error("Certificate chain verification failed");
}
} // namespace ignite::network