]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/m_haproxy.cpp
Add support for blocking tag messages with the deaf mode.
[user/henk/code/inspircd.git] / src / modules / m_haproxy.cpp
index 8272c298013f39a9ac3838184f4f0a4c3927f292..cf551d5455cf19d385fd705c80401cbfb37d5593 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
- *   Copyright (C) 2018 Peter Powell <petpow@saberuk.com>
+ *   Copyright (C) 2019-2020 Matt Schatz <genius3000@g3k.solutions>
+ *   Copyright (C) 2019 linuxdaemon <linuxdaemon.irc@gmail.com>
+ *   Copyright (C) 2018-2019 Sadie Powell <sadie@witchery.services>
  *
  * This file is part of InspIRCd.  InspIRCd is free software: you can
  * redistribute it and/or modify it under the terms of the GNU General Public
 
 #include "inspircd.h"
 #include "iohook.h"
+#include "modules/ssl.h"
 
 enum
 {
+       // The SSL TLV flag for a client being connected over SSL.
+       PP2_CLIENT_SSL = 0x01,
+
        // The family for TCP over IPv4.
        PP2_FAMILY_IPV4 = 0x11,
 
@@ -46,6 +52,15 @@ enum
        // The length of the PROXY protocol header.
        PP2_HEADER_LENGTH = 16,
 
+       // The minimum length of a Type-Length-Value entry.
+       PP2_TLV_LENGTH = 3,
+
+       // The identifier for a SSL TLV entry.
+       PP2_TYPE_SSL = 0x20,
+
+       // The minimum length of a PP2_TYPE_SSL TLV entry.
+       PP2_TYPE_SSL_LENGTH = 5,
+
        // The length of the PROXY protocol signature.
        PP2_SIGNATURE_LENGTH = 12,
 
@@ -94,9 +109,13 @@ struct HAProxyHeader
 
 class HAProxyHookProvider : public IOHookProvider
 {
+ private:
+        UserCertificateAPI sslapi;
+
  public:
        HAProxyHookProvider(Module* mod)
                : IOHookProvider(mod, "haproxy", IOHookProvider::IOH_UNKNOWN, true)
+               , sslapi(mod)
        {
        }
 
@@ -126,10 +145,92 @@ class HAProxyHook : public IOHookMiddle
        // The endpoint the client is connected to.
        irc::sockets::sockaddrs server;
 
+       // The API for interacting with user SSL internals.
+       UserCertificateAPI& sslapi;
+
        // The current state of the PROXY parser.
        HAProxyState state;
 
-       int ReadProxyAddress(StreamSocket* sock)
+       size_t ReadProxyTLV(StreamSocket* sock, size_t start_index, uint16_t buffer_length)
+       {
+               // A TLV must at least consist of a type (uint8_t) and a length (uint16_t).
+               if (buffer_length < PP2_TLV_LENGTH)
+               {
+                       sock->SetError("Truncated HAProxy PROXY TLV type and/or length");
+                       return 0;
+               }
+
+               // Check that the length can actually contain the TLV value.
+               std::string& recvq = GetRecvQ();
+               uint16_t length = ntohs(recvq[start_index + 1] | (recvq[start_index + 2] << 8));
+               if (buffer_length < PP2_TLV_LENGTH + length)
+               {
+                       sock->SetError("Truncated HAProxy PROXY TLV value");
+                       return 0;
+               }
+
+               // What type of TLV are we parsing?
+               switch (recvq[start_index])
+               {
+                       case PP2_TYPE_SSL:
+                               if (!ReadProxyTLVSSL(sock, start_index + PP2_TLV_LENGTH, length))
+                                       return 0;
+                               break;
+               }
+
+               return PP2_TLV_LENGTH + length;
+       }
+
+       bool ReadProxyTLVSSL(StreamSocket* sock, size_t start_index, uint16_t buffer_length)
+       {
+               // A SSL TLV must at least consist of client info (uint8_t) and verification info (uint32_t).
+               if (buffer_length < PP2_TYPE_SSL_LENGTH)
+               {
+                       sock->SetError("Truncated HAProxy PROXY SSL TLV");
+                       return false;
+               }
+
+               // If the socket is not a user socket we don't have to do
+               // anything with this TLVs information.
+               if (sock->type != StreamSocket::SS_USER)
+                       return true;
+
+               // If the sslinfo module is not loaded we can't
+               // do anything with this TLV.
+               if (!sslapi)
+                       return true;
+
+               // If the client is not connecting via TLS (SSL) the rest of this TLV is irrelevant.
+               std::string& recvq = GetRecvQ();
+               if ((recvq[start_index] & PP2_CLIENT_SSL) == 0)
+                       return true;
+
+               // Create a fake ssl_cert for the user. Ideally we should use the user's
+               // TLS (SSL) client certificate here but as of 2018-10-16 this is not forwarded
+               // by HAProxy.
+               ssl_cert* cert = new ssl_cert;
+               cert->error = "HAProxy does not forward client TLS (SSL) certificates";
+               cert->invalid = true;
+               cert->revoked = true;
+               cert->trusted = false;
+               cert->unknownsigner = true;
+
+               // Extract the user for this socket and set their certificate.
+               LocalUser* luser = static_cast<UserIOHandler*>(sock)->user;
+               sslapi->SetCertificate(luser, cert);
+               return true;
+       }
+
+       int ReadData(std::string& destrecvq)
+       {
+               // Once connected we handle no special data.
+               std::string& recvq = GetRecvQ();
+               destrecvq.append(recvq);
+               recvq.clear();
+               return 1;
+       }
+
+       int ReadProxyAddress(StreamSocket* sock, std::string& destrecvq)
        {
                // Block until we have the entire address.
                std::string& recvq = GetRecvQ();
@@ -145,13 +246,15 @@ class HAProxyHook : public IOHookMiddle
 
                        case HPC_PROXY:
                                // Store the endpoint information.
+                               size_t tlv_index = 0;
                                switch (client.family())
                                {
                                        case AF_INET:
                                                memcpy(&client.in4.sin_addr.s_addr, &recvq[0], 4);
-                                               memcpy(&server.in4.sin_addr.s_addr, &recvq[4], 8);
+                                               memcpy(&server.in4.sin_addr.s_addr, &recvq[4], 4);
                                                memcpy(&client.in4.sin_port, &recvq[8], 2);
                                                memcpy(&server.in4.sin_port, &recvq[10], 2);
+                                               tlv_index = 12;
                                                break;
 
                                        case AF_INET6:
@@ -159,28 +262,40 @@ class HAProxyHook : public IOHookMiddle
                                                memcpy(server.in6.sin6_addr.s6_addr, &recvq[16], 16);
                                                memcpy(&client.in6.sin6_port, &recvq[32], 2);
                                                memcpy(&server.in6.sin6_port, &recvq[34], 2);
+                                               tlv_index = 36;
                                                break;
 
                                        case AF_UNIX:
                                                memcpy(client.un.sun_path, &recvq[0], 108);
-                                               memcpy(client.un.sun_path, &recvq[108], 108);
+                                               memcpy(server.un.sun_path, &recvq[108], 108);
+                                               tlv_index = 216;
                                                break;
                                }
 
-                               sock->OnSetEndPoint(server, client);
+                               if (!sock->OnSetEndPoint(server, client))
+                                       return -1;
 
-                               // XXX: HAProxy's PROXY v2 specification defines Type-Length-Values that
-                               // could appear here but as of 2018-07-25 it does not send anything. We
-                               // should revisit this in the future to see if they actually send them.
+                               // Parse any available TLVs.
+                               while (tlv_index < address_length)
+                               {
+                                       size_t length = ReadProxyTLV(sock, tlv_index, address_length - tlv_index);
+                                       if (!length)
+                                               return -1;
+
+                                       tlv_index += length;
+                               }
+
+                               // Erase the processed proxy information from the receive queue.
                                recvq.erase(0, address_length);
+                               break;
                }
 
                // We're done!
                state = HPS_CONNECTED;
-               return 1;
+               return ReadData(destrecvq);
        }
 
-       int ReadProxyHeader(StreamSocket* sock)
+       int ReadProxyHeader(StreamSocket* sock, std::string& destrecvq)
        {
                // Block until we have a header.
                std::string& recvq = GetRecvQ();
@@ -256,12 +371,14 @@ class HAProxyHook : public IOHookMiddle
                }
 
                state = HPS_WAITING_FOR_ADDRESS;
-               return ReadProxyAddress(sock);
+               return ReadProxyAddress(sock, destrecvq);
        }
 
  public:
-       HAProxyHook(IOHookProvider* Prov, StreamSocket* sock)
+       HAProxyHook(IOHookProvider* Prov, StreamSocket* sock, UserCertificateAPI& api)
                : IOHookMiddle(Prov)
+               , address_length(0)
+               , sslapi(api)
                , state(HPS_WAITING_FOR_HEADER)
        {
                sock->AddIOHook(this);
@@ -279,16 +396,13 @@ class HAProxyHook : public IOHookMiddle
                switch (state)
                {
                        case HPS_WAITING_FOR_HEADER:
-                               return ReadProxyHeader(sock);
+                               return ReadProxyHeader(sock, destrecvq);
 
                        case HPS_WAITING_FOR_ADDRESS:
-                               return ReadProxyAddress(sock);
+                               return ReadProxyAddress(sock, destrecvq);
 
                        case HPS_CONNECTED:
-                               std::string& recvq = GetRecvQ();
-                               destrecvq.append(recvq);
-                               recvq.clear();
-                               return 1;
+                               return ReadData(destrecvq);
                }
 
                // We should never reach this point.
@@ -303,7 +417,7 @@ class HAProxyHook : public IOHookMiddle
 
 void HAProxyHookProvider::OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
 {
-       new HAProxyHook(this, sock);
+       new HAProxyHook(this, sock, sslapi);
 }
 
 class ModuleHAProxy : public Module
@@ -319,7 +433,7 @@ class ModuleHAProxy : public Module
 
        Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides support for the HAProxy PROXY protocol", VF_VENDOR);
+               return Version("Allows IRC connections to be made using reverse proxies that implement the HAProxy PROXY protocol.", VF_VENDOR);
        }
 };