#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
+#include <stdexcept>
#include <system_error>
#include <seastar/core/loop.hh>
#include <seastar/net/stack.hh>
#include <seastar/util/std-compat.hh>
#include <seastar/util/variant_utils.hh>
+#include <seastar/core/fsnotify.hh>
#include <boost/range/iterator_range.hpp>
#include <boost/range/adaptor/map.hpp>
-#include "../core/fsnotify.hh"
namespace seastar {
static std::unique_ptr<connected_socket_impl> get(connected_socket s) {
return std::move(s._csi);
}
+
+ static connected_socket_impl* maybe_get_ptr(connected_socket& s) {
+ if (s._csi) {
+ return s._csi.get();
+ }
+ return nullptr;
+ }
};
class blob_wrapper: public gnutls_datum_t {
_timer.cancel();
}
private:
+ using fsnotifier = experimental::fsnotifier;
+
// called from seastar::thread
void rebuild(const std::vector<fsnotifier::event>& events) {
for (auto& e : events) {
// version requested by the client is not supported, the
// "protocol_version" alert is sent.
auto alert = gnutls_alert_description_t(gnutls_error_to_alert(res, NULL));
- return send_alert(GNUTLS_AL_FATAL, alert).then_wrapped([res] (future<> f) {
- // Return to the caller the original handshake error.
- // If send_alert() *also* failed, ignore that.
- f.ignore_ready_future();
- return make_exception_future<>(std::system_error(res, glts_errorc));
+ return handle_output_error(res).then_wrapped([this, alert = std::move(alert)] (future<> output_future) {
+ return send_alert(GNUTLS_AL_FATAL, alert).then_wrapped([output_future = std::move(output_future)] (future<> f) mutable {
+ // Return to the caller the original handshake error.
+ // If send_alert() *also* failed, ignore that.
+ f.ignore_ready_future();
+ return std::move(output_future);
+ });
});
}
}
// if the user registered a DN (Distinguished Name) callback
// then extract subject and issuer from the (leaf) peer certificate and invoke the callback
- unsigned int list_size;
- const gnutls_datum_t* client_cert_list = gnutls_certificate_get_peers(*this, &list_size);
- assert(list_size > 0); // otherwise we couldn't have gotten here
-
- gnutls_x509_crt_t peer_leaf_cert;
- gtls_chk(gnutls_x509_crt_init(&peer_leaf_cert));
- gtls_chk(gnutls_x509_crt_import(peer_leaf_cert, &(client_cert_list[0]), GNUTLS_X509_FMT_DER));
-
- // we need get_string to be noexcept because we need to manually de-init peer_leaf_cert afterwards
- auto [ec, subject] = get_gtls_string(gnutls_x509_crt_get_dn, peer_leaf_cert);
- auto [ec2, issuer] = get_gtls_string(gnutls_x509_crt_get_issuer_dn, peer_leaf_cert);
-
- gnutls_x509_crt_deinit(peer_leaf_cert);
-
- if (ec || ec2) {
- throw std::runtime_error("error while extracting certificate DN strings");
- }
+ auto dn = extract_dn_information();
+ assert(dn.has_value()); // otherwise we couldn't have gotten here
// a switch here might look overelaborate, however,
// the compiler will warn us if someone alters the definition of type
break;
}
- _creds->_dn_callback(t, std::move(subject), std::move(issuer));
+ _creds->_dn_callback(t, std::move(dn->subject), std::move(dn->issuer));
}
}
return -1;
}
try {
- scattered_message<char> msg;
- for (int i = 0; i < iovcnt; ++i) {
- msg.append(std::string_view(reinterpret_cast<const char *>(iov[i].iov_base), iov[i].iov_len));
+ ssize_t n; // Set on the good path and unused on the bad path
+
+ if (!_output_pending.failed()) {
+ scattered_message<char> msg;
+ for (int i = 0; i < iovcnt; ++i) {
+ msg.append(std::string_view(reinterpret_cast<const char *>(iov[i].iov_base), iov[i].iov_len));
+ }
+ n = msg.size();
+ _output_pending = _out.put(std::move(msg).release());
+ }
+ if (_output_pending.failed()) {
+ // exception is copied back into _output_pending
+ // by the catch handlers below
+ std::rethrow_exception(_output_pending.get_exception());
}
- auto n = msg.size();
- _output_pending = _out.put(std::move(msg).release());
return n;
+ } catch (const std::system_error& e) {
+ gnutls_transport_set_errno(*this, e.code().value());
+ _output_pending = make_exception_future<>(std::current_exception());
} catch (...) {
gnutls_transport_set_errno(*this, EIO);
_output_pending = make_exception_future<>(std::current_exception());
// below, get pre-empted, have "close()" finish, get freed, and
// then call wait_for_eof on stale pointer.
}
- void close() {
+ void close() noexcept {
// only do once.
if (!std::exchange(_shutdown, true)) {
auto me = shared_from_this();
return *_sock;
}
+ future<std::optional<session_dn>> get_distinguished_name() {
+ using result_t = std::optional<session_dn>;
+ if (_error) {
+ return make_exception_future<result_t>(_error);
+ }
+ if (_shutdown) {
+ return make_exception_future<result_t>(std::system_error(ENOTCONN, std::system_category()));
+ }
+ if (!_connected) {
+ return handshake().then([this]() mutable {
+ return get_distinguished_name();
+ });
+ }
+ result_t dn = extract_dn_information();
+ return make_ready_future<result_t>(std::move(dn));
+ }
+
struct session_ref;
private:
+
+ std::optional<session_dn> extract_dn_information() const {
+ unsigned int list_size;
+ const gnutls_datum_t* client_cert_list = gnutls_certificate_get_peers(*this, &list_size);
+ if (list_size == 0) {
+ return std::nullopt;
+ }
+ gnutls_x509_crt_t peer_leaf_cert;
+ gtls_chk(gnutls_x509_crt_init(&peer_leaf_cert));
+ gtls_chk(gnutls_x509_crt_import(peer_leaf_cert, &(client_cert_list[0]), GNUTLS_X509_FMT_DER));
+ auto [ec, subject] = get_gtls_string(gnutls_x509_crt_get_dn, peer_leaf_cert);
+ auto [ec2, issuer] = get_gtls_string(gnutls_x509_crt_get_issuer_dn, peer_leaf_cert);
+ if (ec || ec2) {
+ throw std::runtime_error("error while extracting certificate DN strings");
+ }
+ gnutls_x509_crt_deinit(peer_leaf_cert);
+ return session_dn{.subject=subject, .issuer=issuer};
+ }
+
type _type;
std::unique_ptr<net::connected_socket_impl> _sock;
socket_address local_address() const noexcept override {
return _session->socket().local_address();
}
-
+ future<std::optional<session_dn>> get_distinguished_name() {
+ return _session->get_distinguished_name();
+ }
+ future<> wait_input_shutdown() override {
+ return _session->socket().wait_input_shutdown();
+ }
};
return _sock.local_address();
}
private:
+
shared_ptr<server_credentials> _creds;
server_socket _sock;
};
return server_socket(std::move(ssls));
}
+future<std::optional<session_dn>> tls::get_dn_information(connected_socket& socket) {
+ auto impl = net::get_impl::maybe_get_ptr(socket);
+ if (impl == nullptr) {
+ // the socket is not yet created or moved from
+ throw std::system_error(ENOTCONN, std::system_category());
+ }
+ auto tls_impl = dynamic_cast<tls_connected_socket_impl*>(impl);
+ if (!tls_impl) {
+ // bad cast here means that we're dealing with wrong socket type
+ throw std::invalid_argument("Not a TLS socket");
+ }
+ return tls_impl->get_distinguished_name();
+}
+
}