]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/seastar/src/net/tls.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / seastar / src / net / tls.cc
index e90853feedbc5fcfd98b3456bf65ea8461b2794c..e3df02939909b8a3f6d91ad44cc6de4e329f6515 100644 (file)
@@ -21,6 +21,7 @@
 
 #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 {
 
@@ -50,6 +51,13 @@ public:
     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 {
@@ -759,6 +767,8 @@ public:
             _timer.cancel();
         }
     private:
+        using fsnotifier = experimental::fsnotifier;
+
         // called from seastar::thread
         void rebuild(const std::vector<fsnotifier::event>& events) {
             for (auto& e : events) {
@@ -1099,11 +1109,13 @@ public:
                     // 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);
+                        });
                     });
                 }
             }
@@ -1202,23 +1214,8 @@ public:
             // 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
@@ -1232,7 +1229,7 @@ public:
                 break;
             }
 
-            _creds->_dn_callback(t, std::move(subject), std::move(issuer));
+            _creds->_dn_callback(t, std::move(dn->subject), std::move(dn->issuer));
         }
     }
 
@@ -1366,13 +1363,25 @@ public:
             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());
@@ -1460,7 +1469,7 @@ public:
         // 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();
@@ -1497,8 +1506,44 @@ public:
         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;
@@ -1589,7 +1634,12 @@ public:
     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();
+    }
 };
 
 
@@ -1649,6 +1699,7 @@ public:
         return _sock.local_address();
     }
 private:
+
     shared_ptr<server_credentials> _creds;
     server_socket _sock;
 };
@@ -1725,4 +1776,18 @@ server_socket tls::listen(shared_ptr<server_credentials> creds, server_socket ss
     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();
+}
+
 }