changeset 234:3c631fb06ccf

Irccd: support SSL transport part 1
author David Demelier <markand@malikania.fr>
date Fri, 12 Aug 2016 13:16:59 +0200
parents f8094e852dd5
children 5566cf772073
files lib/irccd/config.cpp lib/irccd/net.hpp lib/irccd/transport-client.cpp lib/irccd/transport-client.hpp lib/irccd/transport-server.cpp lib/irccd/transport-server.hpp
diffstat 6 files changed, 202 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/lib/irccd/config.cpp	Thu Aug 11 12:05:26 2016 +0200
+++ b/lib/irccd/config.cpp	Fri Aug 12 13:16:59 2016 +0200
@@ -171,10 +171,29 @@
         }
     }
 
+    // Optional SSL.
+    std::string pkey;
+    std::string cert;
+
+    if ((it = sc.find("ssl")) != sc.end() && util::isBoolean(it->value())) {
+        if ((it = sc.find("certificate")) == sc.end())
+            throw std::invalid_argument("transport: missing 'certificate' parameter");
+
+        cert = it->value();
+
+        if ((it = sc.find("key")) == sc.end())
+            throw std::invalid_argument("transport: missing 'key' parameter");
+
+        pkey = it->value();
+    }
+
     if (mode == 0)
         throw std::invalid_argument("transport: domain must at least have ipv4 or ipv6");
 
-    return std::make_shared<TransportServerIp>(address, port, mode);
+    if (pkey.empty())
+        return std::make_shared<TransportServerIp>(address, port, mode);
+
+    return std::make_shared<TransportServerTls>(pkey, cert, address, port, mode);
 }
 
 std::shared_ptr<TransportServer> loadTransportUnix(const ini::Section &sc)
--- a/lib/irccd/net.hpp	Thu Aug 11 12:05:26 2016 +0200
+++ b/lib/irccd/net.hpp	Fri Aug 12 13:16:59 2016 +0200
@@ -2203,7 +2203,7 @@
      */
     void handshake()
     {
-        wrap([&] () -> int {
+        wrap([this] () -> int {
             return SSL_do_handshake(m_ssl.get());
         });
     }
@@ -2221,7 +2221,7 @@
         int nbread = 0;
 
         wrap([&] () -> int {
-            return (nbread = SSL_read(m_ssl.get(), data, max));
+            return nbread = SSL_read(m_ssl.get(), data, max);
         });
 
         return static_cast<unsigned>(nbread < 0 ? 0 : nbread);
@@ -2240,7 +2240,7 @@
         int nbsent = 0;
 
         wrap([&] () -> int {
-            return (nbsent = SSL_write(m_ssl.get(), data, max));
+            return nbsent = SSL_write(m_ssl.get(), data, max);
         });
 
         return static_cast<unsigned>(nbsent < 0 ? 0 : nbsent);
@@ -2497,7 +2497,7 @@
     length = sizeof (sun);
 #endif
 
-    return Address(reinterpret_cast<const sockaddr *>(&sun), length); 
+    return Address(reinterpret_cast<const sockaddr *>(&sun), length);
 }
 
 /**
--- a/lib/irccd/transport-client.cpp	Thu Aug 11 12:05:26 2016 +0200
+++ b/lib/irccd/transport-client.cpp	Fri Aug 12 13:16:59 2016 +0200
@@ -30,7 +30,7 @@
         onCommand(document);
 }
 
-void TransportClient::receive()
+void TransportClient::syncInput()
 {
     try {
         std::string buffer;
@@ -47,7 +47,7 @@
     }
 }
 
-void TransportClient::send()
+void TransportClient::syncOutput()
 {
     try {
         auto ns = m_socket.send(&m_output[0], m_output.size());
@@ -77,11 +77,11 @@
     // Do some I/O.
     if (FD_ISSET(m_socket.handle(), &in)) {
         log::debug() << "transport: receiving to input buffer" << std::endl;
-        receive();
+        syncInput();
     }
     if (FD_ISSET(m_socket.handle(), &out)) {
         log::debug() << "transport: sending outgoing buffer" << std::endl;
-        send();
+        syncOutput();
     }
 
     // Flush the queue.
@@ -102,4 +102,96 @@
     m_output += "\r\n\r\n";
 }
 
+/*
+ * TransportClientTls
+ * ------------------------------------------------------------------
+ */
+
+void TransportClientTls::handshake()
+{
+    try {
+        m_ssl.handshake();
+        m_handshake = HandshakeReady;
+    } catch (const net::WantReadError &) {
+        m_handshake = HandshakeRead;
+    } catch (const net::WantWriteError &) {
+        m_handshake = HandshakeWrite;
+    } catch (const std::exception &) {
+        onDie();
+    }
+}
+
+TransportClientTls::TransportClientTls(const std::string &pkey,
+                                       const std::string &cert,
+                                       net::TcpSocket socket)
+    : TransportClient(std::move(socket))
+    , m_ssl(m_socket)
+{
+    m_ssl.setPrivateKey(pkey);
+    m_ssl.setCertificate(cert);
+
+    handshake();
+}
+
+void TransportClientTls::syncInput()
+{
+    try {
+        std::string buffer;
+
+        buffer.resize(512);
+        buffer.resize(m_ssl.recv(&buffer[0], buffer.size()));
+
+        if (buffer.empty())
+            onDie();
+
+        m_input += std::move(buffer);
+    } catch (const std::exception &) {
+        onDie();
+    }
+}
+
+void TransportClientTls::syncOutput()
+{
+    try {
+        auto ns = m_ssl.send(&m_output[0], m_output.size());
+
+        if (ns == 0)
+            onDie();
+
+        m_output.erase(0, ns);
+    } catch (const std::exception &ex) {
+        onDie();
+    }
+}
+
+void TransportClientTls::prepare(fd_set &in, fd_set &out, net::Handle &max)
+{
+    if (m_socket.handle() > max)
+        max = m_socket.handle();
+
+    switch (m_handshake) {
+    case HandshakeRead:
+        FD_SET(m_socket.handle(), &in);
+        break;
+    case HandshakeWrite:
+        FD_SET(m_socket.handle(), &out);
+        break;
+    default:
+        TransportClient::prepare(in, out, max);
+        break;
+    }
+}
+
+void TransportClientTls::sync(fd_set &in, fd_set &out)
+{
+    switch (m_handshake) {
+    case HandshakeRead:
+    case HandshakeWrite:
+        handshake();
+        break;
+    default:
+        TransportClient::sync(in, out);
+    }
+}
+
 } // !irccd
--- a/lib/irccd/transport-client.hpp	Thu Aug 11 12:05:26 2016 +0200
+++ b/lib/irccd/transport-client.hpp	Fri Aug 12 13:16:59 2016 +0200
@@ -75,14 +75,15 @@
      */
     void parse(const std::string &buffer);
 
-private:
-    void receive();
-    void send();
+protected:
+    virtual void syncInput();
+    virtual void syncOutput();
 
 public:
     inline TransportClient(net::TcpSocket socket)
         : m_socket(std::move(socket))
     {
+        m_socket.set(net::option::SockBlockMode(false));
     }
 
     /**
@@ -97,6 +98,32 @@
     IRCCD_EXPORT virtual void sync(fd_set &in, fd_set &out);
 };
 
+class TransportClientTls : public TransportClient {
+private:
+    enum {
+        HandshakeWrite,
+        HandshakeRead,
+        HandshakeReady
+    } m_handshake{HandshakeReady};
+
+    net::TlsSocket m_ssl;
+
+    void handshake();
+
+protected:
+    void syncInput() override;
+    void syncOutput() override;
+
+public:
+    IRCCD_EXPORT TransportClientTls(const std::string &pkey,
+                                    const std::string &cert,
+                                    net::TcpSocket socket);
+
+    IRCCD_EXPORT virtual void prepare(fd_set &in, fd_set &out, net::Handle &max);
+
+    IRCCD_EXPORT virtual void sync(fd_set &in, fd_set &out);
+};
+
 } // !irccd
 
 #endif // !IRCCD_TRANSPORT_CLIENT_HPP
--- a/lib/irccd/transport-server.cpp	Thu Aug 11 12:05:26 2016 +0200
+++ b/lib/irccd/transport-server.cpp	Fri Aug 12 13:16:59 2016 +0200
@@ -30,7 +30,7 @@
 namespace irccd {
 
 /*
- * TransportServerIpv6
+ * TransportServerIp
  * ------------------------------------------------------------------
  */
 
@@ -65,6 +65,29 @@
 }
 
 /*
+ * TransportServerTls
+ * ------------------------------------------------------------------
+ */
+
+TransportServerTls::TransportServerTls(const std::string &pkey,
+                                       const std::string &cert,
+                                       const std::string &address,
+                                       std::uint16_t port,
+                                       std::uint8_t mode)
+    : TransportServerIp(address, port, mode)
+    , m_privatekey(pkey)
+    , m_cert(cert)
+{
+    log::info() << "transport: listening on " << address << ", port " << port
+                << " (using SSL)" << std::endl;
+}
+
+std::unique_ptr<TransportClient> TransportServerTls::accept()
+{
+    return std::make_unique<TransportClientTls>(m_privatekey, m_cert, m_socket.accept());
+}
+
+/*
  * TransportServerUnix
  * ------------------------------------------------------------------
  */
--- a/lib/irccd/transport-server.hpp	Thu Aug 11 12:05:26 2016 +0200
+++ b/lib/irccd/transport-server.hpp	Fri Aug 12 13:16:59 2016 +0200
@@ -121,6 +121,34 @@
                                    std::uint8_t mode = v4);
 };
 
+/**
+ * \brief TLS over IP transport.
+ */
+class TransportServerTls : public TransportServerIp {
+private:
+    std::string m_privatekey;
+    std::string m_cert;
+
+public:
+    /**
+     * Constructor.
+     * \pre mode > 0
+     * \param address the address (* for any)
+     * \param port the port number
+     * \param mode the domains to use (can be OR'ed)
+     */
+    IRCCD_EXPORT TransportServerTls(const std::string &pkey,
+                                    const std::string &cert,
+                                    const std::string &address,
+                                    std::uint16_t port,
+                                    std::uint8_t mode = v4);
+
+    /**
+     * \copydoc TransportServer::accept
+     */
+    IRCCD_EXPORT std::unique_ptr<TransportClient> accept() override;
+};
+
 #if !defined(IRCCD_SYSTEM_WINDOWS)
 
 /**