2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2019 linuxdaemon <linuxdaemon.irc@gmail.com>
5 * Copyright (C) 2019 Matt Schatz <genius3000@g3k.solutions>
6 * Copyright (C) 2018-2019 Sadie Powell <sadie@witchery.services>
8 * This file is part of InspIRCd. InspIRCd is free software: you can
9 * redistribute it and/or modify it under the terms of the GNU General Public
10 * License as published by the Free Software Foundation, version 2.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "modules/ssl.h"
28 // The SSL TLV flag for a client being connected over SSL.
29 PP2_CLIENT_SSL = 0x01,
31 // The family for TCP over IPv4.
32 PP2_FAMILY_IPV4 = 0x11,
34 // The length of the PP2_FAMILY_IPV4 endpoints.
35 PP2_FAMILY_IPV4_LENGTH = 12,
37 // The family for TCP over IPv6.
38 PP2_FAMILY_IPV6 = 0x21,
40 // The length of the PP2_FAMILY_IPV6 endpoints.
41 PP2_FAMILY_IPV6_LENGTH = 36,
43 // The family for UNIX sockets.
44 PP2_FAMILY_UNIX = 0x31,
46 // The length of the PP2_FAMILY_UNIX endpoints.
47 PP2_FAMILY_UNIX_LENGTH = 216,
49 // The bitmask we apply to extract the command.
50 PP2_COMMAND_MASK = 0x0F,
52 // The length of the PROXY protocol header.
53 PP2_HEADER_LENGTH = 16,
55 // The minimum length of a Type-Length-Value entry.
58 // The identifier for a SSL TLV entry.
61 // The minimum length of a PP2_TYPE_SSL TLV entry.
62 PP2_TYPE_SSL_LENGTH = 5,
64 // The length of the PROXY protocol signature.
65 PP2_SIGNATURE_LENGTH = 12,
67 // The PROXY protocol version we support.
70 // The bitmask we apply to extract the protocol version.
71 PP2_VERSION_MASK = 0xF0
76 // We are waiting for the PROXY header section.
77 HPS_WAITING_FOR_HEADER,
79 // We are waiting for the PROXY address section.
80 HPS_WAITING_FOR_ADDRESS,
82 // The client is fully connected.
97 // The signature used to identify the HAProxy protocol.
98 uint8_t signature[PP2_SIGNATURE_LENGTH];
100 // The version of the PROXY protocol and command being sent.
101 uint8_t version_command;
103 // The family for the address.
106 // The length of the address section.
110 class HAProxyHookProvider : public IOHookProvider
113 UserCertificateAPI sslapi;
116 HAProxyHookProvider(Module* mod)
117 : IOHookProvider(mod, "haproxy", IOHookProvider::IOH_UNKNOWN, true)
122 void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE;
124 void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
126 // We don't need to implement this.
130 // The signature for a HAProxy PROXY protocol header.
131 static const char proxy_signature[13] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
133 class HAProxyHook : public IOHookMiddle
136 // The length of the address section.
137 uint16_t address_length;
139 // The endpoint the client is connecting from.
140 irc::sockets::sockaddrs client;
142 // The command sent by the proxy server.
143 HAProxyCommand command;
145 // The endpoint the client is connected to.
146 irc::sockets::sockaddrs server;
148 // The API for interacting with user SSL internals.
149 UserCertificateAPI& sslapi;
151 // The current state of the PROXY parser.
154 size_t ReadProxyTLV(StreamSocket* sock, size_t start_index, uint16_t buffer_length)
156 // A TLV must at least consist of a type (uint8_t) and a length (uint16_t).
157 if (buffer_length < PP2_TLV_LENGTH)
159 sock->SetError("Truncated HAProxy PROXY TLV type and/or length");
163 // Check that the length can actually contain the TLV value.
164 std::string& recvq = GetRecvQ();
165 uint16_t length = ntohs(recvq[start_index + 1] | (recvq[start_index + 2] << 8));
166 if (buffer_length < PP2_TLV_LENGTH + length)
168 sock->SetError("Truncated HAProxy PROXY TLV value");
172 // What type of TLV are we parsing?
173 switch (recvq[start_index])
176 if (!ReadProxyTLVSSL(sock, start_index + PP2_TLV_LENGTH, length))
181 return PP2_TLV_LENGTH + length;
184 bool ReadProxyTLVSSL(StreamSocket* sock, size_t start_index, uint16_t buffer_length)
186 // A SSL TLV must at least consist of client info (uint8_t) and verification info (uint32_t).
187 if (buffer_length < PP2_TYPE_SSL_LENGTH)
189 sock->SetError("Truncated HAProxy PROXY SSL TLV");
193 // If the socket is not a user socket we don't have to do
194 // anything with this TLVs information.
195 if (sock->type != StreamSocket::SS_USER)
198 // If the sslinfo module is not loaded we can't
199 // do anything with this TLV.
203 // If the client is not connecting via SSL the rest of this TLV is irrelevant.
204 std::string& recvq = GetRecvQ();
205 if ((recvq[start_index] & PP2_CLIENT_SSL) == 0)
208 // Create a fake ssl_cert for the user. Ideally we should use the user's
209 // SSL client certificate here but as of 2018-10-16 this is not forwarded
211 ssl_cert* cert = new ssl_cert;
212 cert->error = "HAProxy does not forward client SSL certificates";
213 cert->invalid = true;
214 cert->revoked = true;
215 cert->trusted = false;
216 cert->unknownsigner = true;
218 // Extract the user for this socket and set their certificate.
219 LocalUser* luser = static_cast<UserIOHandler*>(sock)->user;
220 sslapi->SetCertificate(luser, cert);
224 int ReadData(std::string& destrecvq)
226 // Once connected we handle no special data.
227 std::string& recvq = GetRecvQ();
228 destrecvq.append(recvq);
233 int ReadProxyAddress(StreamSocket* sock, std::string& destrecvq)
235 // Block until we have the entire address.
236 std::string& recvq = GetRecvQ();
237 if (recvq.length() < address_length)
243 // Skip the address completely.
244 recvq.erase(0, address_length);
248 // Store the endpoint information.
249 size_t tlv_index = 0;
250 switch (client.family())
253 memcpy(&client.in4.sin_addr.s_addr, &recvq[0], 4);
254 memcpy(&server.in4.sin_addr.s_addr, &recvq[4], 4);
255 memcpy(&client.in4.sin_port, &recvq[8], 2);
256 memcpy(&server.in4.sin_port, &recvq[10], 2);
261 memcpy(client.in6.sin6_addr.s6_addr, &recvq[0], 16);
262 memcpy(server.in6.sin6_addr.s6_addr, &recvq[16], 16);
263 memcpy(&client.in6.sin6_port, &recvq[32], 2);
264 memcpy(&server.in6.sin6_port, &recvq[34], 2);
269 memcpy(client.un.sun_path, &recvq[0], 108);
270 memcpy(server.un.sun_path, &recvq[108], 108);
275 if (!sock->OnSetEndPoint(server, client))
278 // Parse any available TLVs.
279 while (tlv_index < address_length)
281 size_t length = ReadProxyTLV(sock, tlv_index, address_length - tlv_index);
288 // Erase the processed proxy information from the receive queue.
289 recvq.erase(0, address_length);
294 state = HPS_CONNECTED;
295 return ReadData(destrecvq);
298 int ReadProxyHeader(StreamSocket* sock, std::string& destrecvq)
300 // Block until we have a header.
301 std::string& recvq = GetRecvQ();
302 if (recvq.length() < PP2_HEADER_LENGTH)
306 HAProxyHeader header;
307 memcpy(&header, recvq.c_str(), PP2_HEADER_LENGTH);
308 recvq.erase(0, PP2_HEADER_LENGTH);
310 // Check we are actually parsing a HAProxy header.
311 if (memcmp(&header.signature, proxy_signature, PP2_SIGNATURE_LENGTH) != 0)
313 // If we've reached this point the proxy server did not send a proxy information.
314 sock->SetError("Invalid HAProxy PROXY signature");
318 // We only support this version of the protocol.
319 const uint8_t version = (header.version_command & PP2_VERSION_MASK);
320 if (version != PP2_VERSION)
322 sock->SetError("Unsupported HAProxy PROXY protocol version");
326 // We only support the LOCAL and PROXY commands.
327 command = static_cast<HAProxyCommand>(header.version_command & PP2_COMMAND_MASK);
331 // Intentionally left blank.
335 // Check the protocol support and initialise the sockaddrs.
336 uint16_t shortest_length;
337 switch (header.family)
339 case PP2_FAMILY_IPV4: // TCP over IPv4.
340 client.sa.sa_family = server.sa.sa_family = AF_INET;
341 shortest_length = PP2_FAMILY_IPV4_LENGTH;
344 case PP2_FAMILY_IPV6: // TCP over IPv6.
345 client.sa.sa_family = server.sa.sa_family = AF_INET6;
346 shortest_length = PP2_FAMILY_IPV6_LENGTH;
349 case PP2_FAMILY_UNIX: // UNIX stream.
350 client.sa.sa_family = server.sa.sa_family = AF_UNIX;
351 shortest_length = PP2_FAMILY_UNIX_LENGTH;
354 default: // Unknown protocol.
355 sock->SetError("Invalid HAProxy PROXY protocol type");
359 // Check that the length can actually contain the addresses.
360 address_length = ntohs(header.length);
361 if (address_length < shortest_length)
363 sock->SetError("Truncated HAProxy PROXY address section");
369 sock->SetError("Unsupported HAProxy PROXY command");
373 state = HPS_WAITING_FOR_ADDRESS;
374 return ReadProxyAddress(sock, destrecvq);
378 HAProxyHook(IOHookProvider* Prov, StreamSocket* sock, UserCertificateAPI& api)
382 , state(HPS_WAITING_FOR_HEADER)
384 sock->AddIOHook(this);
387 int OnStreamSocketWrite(StreamSocket* sock, StreamSocket::SendQueue& uppersendq) CXX11_OVERRIDE
389 // We don't need to implement this.
390 GetSendQ().moveall(uppersendq);
394 int OnStreamSocketRead(StreamSocket* sock, std::string& destrecvq) CXX11_OVERRIDE
398 case HPS_WAITING_FOR_HEADER:
399 return ReadProxyHeader(sock, destrecvq);
401 case HPS_WAITING_FOR_ADDRESS:
402 return ReadProxyAddress(sock, destrecvq);
405 return ReadData(destrecvq);
408 // We should never reach this point.
412 void OnStreamSocketClose(StreamSocket* sock) CXX11_OVERRIDE
414 // We don't need to implement this.
418 void HAProxyHookProvider::OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
420 new HAProxyHook(this, sock, sslapi);
423 class ModuleHAProxy : public Module
426 reference<HAProxyHookProvider> hookprov;
430 : hookprov(new HAProxyHookProvider(this))
434 Version GetVersion() CXX11_OVERRIDE
436 return Version("Provides support for the HAProxy PROXY protocol", VF_VENDOR);
440 MODULE_INIT(ModuleHAProxy)