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 ReadData(std::string& destrecvq)
224 // Once connected we handle no special data.
225 std::string& recvq = GetRecvQ();
226 destrecvq.append(recvq);
231 int ReadProxyAddress(StreamSocket* sock, std::string& destrecvq)
233 // Block until we have the entire address.
234 std::string& recvq = GetRecvQ();
235 if (recvq.length() < address_length)
241 // Skip the address completely.
242 recvq.erase(0, address_length);
246 // Store the endpoint information.
247 size_t tlv_index = 0;
248 switch (client.family())
251 memcpy(&client.in4.sin_addr.s_addr, &recvq[0], 4);
252 memcpy(&server.in4.sin_addr.s_addr, &recvq[4], 4);
253 memcpy(&client.in4.sin_port, &recvq[8], 2);
254 memcpy(&server.in4.sin_port, &recvq[10], 2);
259 memcpy(client.in6.sin6_addr.s6_addr, &recvq[0], 16);
260 memcpy(server.in6.sin6_addr.s6_addr, &recvq[16], 16);
261 memcpy(&client.in6.sin6_port, &recvq[32], 2);
262 memcpy(&server.in6.sin6_port, &recvq[34], 2);
267 memcpy(client.un.sun_path, &recvq[0], 108);
268 memcpy(server.un.sun_path, &recvq[108], 108);
273 if (!sock->OnSetEndPoint(server, client))
276 // Parse any available TLVs.
277 while (tlv_index < address_length)
279 size_t length = ReadProxyTLV(sock, tlv_index, address_length - tlv_index);
286 // Erase the processed proxy information from the receive queue.
287 recvq.erase(0, address_length);
292 state = HPS_CONNECTED;
293 return ReadData(destrecvq);
296 int ReadProxyHeader(StreamSocket* sock, std::string& destrecvq)
298 // Block until we have a header.
299 std::string& recvq = GetRecvQ();
300 if (recvq.length() < PP2_HEADER_LENGTH)
304 HAProxyHeader header;
305 memcpy(&header, recvq.c_str(), PP2_HEADER_LENGTH);
306 recvq.erase(0, PP2_HEADER_LENGTH);
308 // Check we are actually parsing a HAProxy header.
309 if (memcmp(&header.signature, proxy_signature, PP2_SIGNATURE_LENGTH) != 0)
311 // If we've reached this point the proxy server did not send a proxy information.
312 sock->SetError("Invalid HAProxy PROXY signature");
316 // We only support this version of the protocol.
317 const uint8_t version = (header.version_command & PP2_VERSION_MASK);
318 if (version != PP2_VERSION)
320 sock->SetError("Unsupported HAProxy PROXY protocol version");
324 // We only support the LOCAL and PROXY commands.
325 command = static_cast<HAProxyCommand>(header.version_command & PP2_COMMAND_MASK);
329 // Intentionally left blank.
333 // Check the protocol support and initialise the sockaddrs.
334 uint16_t shortest_length;
335 switch (header.family)
337 case PP2_FAMILY_IPV4: // TCP over IPv4.
338 client.sa.sa_family = server.sa.sa_family = AF_INET;
339 shortest_length = PP2_FAMILY_IPV4_LENGTH;
342 case PP2_FAMILY_IPV6: // TCP over IPv6.
343 client.sa.sa_family = server.sa.sa_family = AF_INET6;
344 shortest_length = PP2_FAMILY_IPV6_LENGTH;
347 case PP2_FAMILY_UNIX: // UNIX stream.
348 client.sa.sa_family = server.sa.sa_family = AF_UNIX;
349 shortest_length = PP2_FAMILY_UNIX_LENGTH;
352 default: // Unknown protocol.
353 sock->SetError("Invalid HAProxy PROXY protocol type");
357 // Check that the length can actually contain the addresses.
358 address_length = ntohs(header.length);
359 if (address_length < shortest_length)
361 sock->SetError("Truncated HAProxy PROXY address section");
367 sock->SetError("Unsupported HAProxy PROXY command");
371 state = HPS_WAITING_FOR_ADDRESS;
372 return ReadProxyAddress(sock, destrecvq);
376 HAProxyHook(IOHookProvider* Prov, StreamSocket* sock, UserCertificateAPI& api)
380 , state(HPS_WAITING_FOR_HEADER)
382 sock->AddIOHook(this);
385 int OnStreamSocketWrite(StreamSocket* sock, StreamSocket::SendQueue& uppersendq) CXX11_OVERRIDE
387 // We don't need to implement this.
388 GetSendQ().moveall(uppersendq);
392 int OnStreamSocketRead(StreamSocket* sock, std::string& destrecvq) CXX11_OVERRIDE
396 case HPS_WAITING_FOR_HEADER:
397 return ReadProxyHeader(sock, destrecvq);
399 case HPS_WAITING_FOR_ADDRESS:
400 return ReadProxyAddress(sock, destrecvq);
403 return ReadData(destrecvq);
406 // We should never reach this point.
410 void OnStreamSocketClose(StreamSocket* sock) CXX11_OVERRIDE
412 // We don't need to implement this.
416 void HAProxyHookProvider::OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
418 new HAProxyHook(this, sock, sslapi);
421 class ModuleHAProxy : public Module
424 reference<HAProxyHookProvider> hookprov;
428 : hookprov(new HAProxyHookProvider(this))
432 Version GetVersion() CXX11_OVERRIDE
434 return Version("Provides support for the HAProxy PROXY protocol", VF_VENDOR);
438 MODULE_INIT(ModuleHAProxy)