2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2018 Peter Powell <petpow@saberuk.com>
6 * This file is part of InspIRCd. InspIRCd is free software: you can
7 * redistribute it and/or modify it under the terms of the GNU General Public
8 * License as published by the Free Software Foundation, version 2.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "modules/ssl.h"
26 // The SSL TLV flag for a client being connected over SSL.
27 PP2_CLIENT_SSL = 0x01,
29 // The family for TCP over IPv4.
30 PP2_FAMILY_IPV4 = 0x11,
32 // The length of the PP2_FAMILY_IPV4 endpoints.
33 PP2_FAMILY_IPV4_LENGTH = 12,
35 // The family for TCP over IPv6.
36 PP2_FAMILY_IPV6 = 0x21,
38 // The length of the PP2_FAMILY_IPV6 endpoints.
39 PP2_FAMILY_IPV6_LENGTH = 36,
41 // The family for UNIX sockets.
42 PP2_FAMILY_UNIX = 0x31,
44 // The length of the PP2_FAMILY_UNIX endpoints.
45 PP2_FAMILY_UNIX_LENGTH = 216,
47 // The bitmask we apply to extract the command.
48 PP2_COMMAND_MASK = 0x0F,
50 // The length of the PROXY protocol header.
51 PP2_HEADER_LENGTH = 16,
53 // The minimum length of a Type-Length-Value entry.
56 // The identifier for a SSL TLV entry.
59 // The minimum length of a PP2_TYPE_SSL TLV entry.
60 PP2_TYPE_SSL_LENGTH = 5,
62 // The length of the PROXY protocol signature.
63 PP2_SIGNATURE_LENGTH = 12,
65 // The PROXY protocol version we support.
68 // The bitmask we apply to extract the protocol version.
69 PP2_VERSION_MASK = 0xF0
74 // We are waiting for the PROXY header section.
75 HPS_WAITING_FOR_HEADER,
77 // We are waiting for the PROXY address section.
78 HPS_WAITING_FOR_ADDRESS,
80 // The client is fully connected.
95 // The signature used to identify the HAProxy protocol.
96 uint8_t signature[PP2_SIGNATURE_LENGTH];
98 // The version of the PROXY protocol and command being sent.
99 uint8_t version_command;
101 // The family for the address.
104 // The length of the address section.
108 class HAProxyHookProvider : public IOHookProvider
111 UserCertificateAPI sslapi;
114 HAProxyHookProvider(Module* mod)
115 : IOHookProvider(mod, "haproxy", IOHookProvider::IOH_UNKNOWN, true)
120 void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE;
122 void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
124 // We don't need to implement this.
128 // The signature for a HAProxy PROXY protocol header.
129 static const char proxy_signature[13] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
131 class HAProxyHook : public IOHookMiddle
134 // The length of the address section.
135 uint16_t address_length;
137 // The endpoint the client is connecting from.
138 irc::sockets::sockaddrs client;
140 // The command sent by the proxy server.
141 HAProxyCommand command;
143 // The endpoint the client is connected to.
144 irc::sockets::sockaddrs server;
146 // The API for interacting with user SSL internals.
147 UserCertificateAPI& sslapi;
149 // The current state of the PROXY parser.
152 size_t ReadProxyTLV(StreamSocket* sock, size_t start_index, uint16_t buffer_length)
154 // A TLV must at least consist of a type (uint8_t) and a length (uint16_t).
155 if (buffer_length < PP2_TLV_LENGTH)
157 sock->SetError("Truncated HAProxy PROXY TLV type and/or length");
161 // Check that the length can actually contain the TLV value.
162 std::string& recvq = GetRecvQ();
163 uint16_t length = ntohs(recvq[start_index + 1] | (recvq[start_index + 2] << 8));
164 if (buffer_length < PP2_TLV_LENGTH + length)
166 sock->SetError("Truncated HAProxy PROXY TLV value");
170 // What type of TLV are we parsing?
171 switch (recvq[start_index])
174 if (!ReadProxyTLVSSL(sock, start_index + PP2_TLV_LENGTH, length))
179 return PP2_TLV_LENGTH + length;
182 bool ReadProxyTLVSSL(StreamSocket* sock, size_t start_index, uint16_t buffer_length)
184 // A SSL TLV must at least consist of client info (uint8_t) and verification info (uint32_t).
185 if (buffer_length < PP2_TYPE_SSL_LENGTH)
187 sock->SetError("Truncated HAProxy PROXY SSL TLV");
191 // If the socket is not a user socket we don't have to do
192 // anything with this TLVs information.
193 if (sock->type != StreamSocket::SS_USER)
196 // If the sslinfo module is not loaded we can't
197 // do anything with this TLV.
201 // If the client is not connecting via SSL the rest of this TLV is irrelevant.
202 std::string& recvq = GetRecvQ();
203 if ((recvq[start_index] & PP2_CLIENT_SSL) == 0)
206 // Create a fake ssl_cert for the user. Ideally we should use the user's
207 // SSL client certificate here but as of 2018-10-16 this is not forwarded
209 ssl_cert* cert = new ssl_cert;
210 cert->error = "HAProxy does not forward client SSL certificates";
211 cert->invalid = true;
212 cert->revoked = true;
213 cert->trusted = false;
214 cert->unknownsigner = true;
216 // Extract the user for this socket and set their certificate.
217 LocalUser* luser = static_cast<UserIOHandler*>(sock)->user;
218 sslapi->SetCertificate(luser, cert);
222 int ReadProxyAddress(StreamSocket* sock)
224 // Block until we have the entire address.
225 std::string& recvq = GetRecvQ();
226 if (recvq.length() < address_length)
232 // Skip the address completely.
233 recvq.erase(0, address_length);
237 // Store the endpoint information.
238 size_t tlv_index = 0;
239 switch (client.family())
242 memcpy(&client.in4.sin_addr.s_addr, &recvq[0], 4);
243 memcpy(&server.in4.sin_addr.s_addr, &recvq[4], 4);
244 memcpy(&client.in4.sin_port, &recvq[8], 2);
245 memcpy(&server.in4.sin_port, &recvq[10], 2);
250 memcpy(client.in6.sin6_addr.s6_addr, &recvq[0], 16);
251 memcpy(server.in6.sin6_addr.s6_addr, &recvq[16], 16);
252 memcpy(&client.in6.sin6_port, &recvq[32], 2);
253 memcpy(&server.in6.sin6_port, &recvq[34], 2);
258 memcpy(client.un.sun_path, &recvq[0], 108);
259 memcpy(server.un.sun_path, &recvq[108], 108);
264 if (!sock->OnSetEndPoint(server, client))
267 // Parse any available TLVs.
268 while (tlv_index < address_length)
270 size_t length = ReadProxyTLV(sock, tlv_index, address_length - tlv_index);
277 // Erase the processed proxy information from the receive queue.
278 recvq.erase(0, address_length);
282 state = HPS_CONNECTED;
286 int ReadProxyHeader(StreamSocket* sock)
288 // Block until we have a header.
289 std::string& recvq = GetRecvQ();
290 if (recvq.length() < PP2_HEADER_LENGTH)
294 HAProxyHeader header;
295 memcpy(&header, recvq.c_str(), PP2_HEADER_LENGTH);
296 recvq.erase(0, PP2_HEADER_LENGTH);
298 // Check we are actually parsing a HAProxy header.
299 if (memcmp(&header.signature, proxy_signature, PP2_SIGNATURE_LENGTH) != 0)
301 // If we've reached this point the proxy server did not send a proxy information.
302 sock->SetError("Invalid HAProxy PROXY signature");
306 // We only support this version of the protocol.
307 const uint8_t version = (header.version_command & PP2_VERSION_MASK);
308 if (version != PP2_VERSION)
310 sock->SetError("Unsupported HAProxy PROXY protocol version");
314 // We only support the LOCAL and PROXY commands.
315 command = static_cast<HAProxyCommand>(header.version_command & PP2_COMMAND_MASK);
319 // Intentionally left blank.
323 // Check the protocol support and initialise the sockaddrs.
324 uint16_t shortest_length;
325 switch (header.family)
327 case PP2_FAMILY_IPV4: // TCP over IPv4.
328 client.sa.sa_family = server.sa.sa_family = AF_INET;
329 shortest_length = PP2_FAMILY_IPV4_LENGTH;
332 case PP2_FAMILY_IPV6: // TCP over IPv6.
333 client.sa.sa_family = server.sa.sa_family = AF_INET6;
334 shortest_length = PP2_FAMILY_IPV6_LENGTH;
337 case PP2_FAMILY_UNIX: // UNIX stream.
338 client.sa.sa_family = server.sa.sa_family = AF_UNIX;
339 shortest_length = PP2_FAMILY_UNIX_LENGTH;
342 default: // Unknown protocol.
343 sock->SetError("Invalid HAProxy PROXY protocol type");
347 // Check that the length can actually contain the addresses.
348 address_length = ntohs(header.length);
349 if (address_length < shortest_length)
351 sock->SetError("Truncated HAProxy PROXY address section");
357 sock->SetError("Unsupported HAProxy PROXY command");
361 state = HPS_WAITING_FOR_ADDRESS;
362 return ReadProxyAddress(sock);
366 HAProxyHook(IOHookProvider* Prov, StreamSocket* sock, UserCertificateAPI& api)
369 , state(HPS_WAITING_FOR_HEADER)
371 sock->AddIOHook(this);
374 int OnStreamSocketWrite(StreamSocket* sock, StreamSocket::SendQueue& uppersendq) CXX11_OVERRIDE
376 // We don't need to implement this.
377 GetSendQ().moveall(uppersendq);
381 int OnStreamSocketRead(StreamSocket* sock, std::string& destrecvq) CXX11_OVERRIDE
385 case HPS_WAITING_FOR_HEADER:
386 return ReadProxyHeader(sock);
388 case HPS_WAITING_FOR_ADDRESS:
389 return ReadProxyAddress(sock);
392 std::string& recvq = GetRecvQ();
393 destrecvq.append(recvq);
398 // We should never reach this point.
402 void OnStreamSocketClose(StreamSocket* sock) CXX11_OVERRIDE
404 // We don't need to implement this.
408 void HAProxyHookProvider::OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
410 new HAProxyHook(this, sock, sslapi);
413 class ModuleHAProxy : public Module
416 reference<HAProxyHookProvider> hookprov;
420 : hookprov(new HAProxyHookProvider(this))
424 Version GetVersion() CXX11_OVERRIDE
426 return Version("Provides support for the HAProxy PROXY protocol", VF_VENDOR);
430 MODULE_INIT(ModuleHAProxy)