X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Fmodules%2Fm_haproxy.cpp;h=cf551d5455cf19d385fd705c80401cbfb37d5593;hb=e2b0f3dc9ef4d56c71d7abda13e6139ca092e387;hp=8272c298013f39a9ac3838184f4f0a4c3927f292;hpb=09c5439c02f31e9875083e51966dad535af005a9;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/modules/m_haproxy.cpp b/src/modules/m_haproxy.cpp index 8272c2980..cf551d545 100644 --- a/src/modules/m_haproxy.cpp +++ b/src/modules/m_haproxy.cpp @@ -1,7 +1,9 @@ /* * InspIRCd -- Internet Relay Chat Daemon * - * Copyright (C) 2018 Peter Powell + * Copyright (C) 2019-2020 Matt Schatz + * Copyright (C) 2019 linuxdaemon + * Copyright (C) 2018-2019 Sadie Powell * * 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 @@ -19,9 +21,13 @@ #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(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); } };