]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_haproxy.cpp
cf551d5455cf19d385fd705c80401cbfb37d5593
[user/henk/code/inspircd.git] / src / modules / m_haproxy.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2019-2020 Matt Schatz <genius3000@g3k.solutions>
5  *   Copyright (C) 2019 linuxdaemon <linuxdaemon.irc@gmail.com>
6  *   Copyright (C) 2018-2019 Sadie Powell <sadie@witchery.services>
7  *
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.
11  *
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
15  * details.
16  *
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/>.
19  */
20
21
22 #include "inspircd.h"
23 #include "iohook.h"
24 #include "modules/ssl.h"
25
26 enum
27 {
28         // The SSL TLV flag for a client being connected over SSL.
29         PP2_CLIENT_SSL = 0x01,
30
31         // The family for TCP over IPv4.
32         PP2_FAMILY_IPV4 = 0x11,
33
34         // The length of the PP2_FAMILY_IPV4 endpoints.
35         PP2_FAMILY_IPV4_LENGTH = 12,
36
37         // The family for TCP over IPv6.
38         PP2_FAMILY_IPV6 = 0x21,
39
40         // The length of the PP2_FAMILY_IPV6 endpoints.
41         PP2_FAMILY_IPV6_LENGTH = 36,
42
43         // The family for UNIX sockets.
44         PP2_FAMILY_UNIX = 0x31,
45
46         // The length of the PP2_FAMILY_UNIX endpoints.
47         PP2_FAMILY_UNIX_LENGTH = 216,
48
49         // The bitmask we apply to extract the command.
50         PP2_COMMAND_MASK = 0x0F,
51
52         // The length of the PROXY protocol header.
53         PP2_HEADER_LENGTH = 16,
54
55         // The minimum length of a Type-Length-Value entry.
56         PP2_TLV_LENGTH = 3,
57
58         // The identifier for a SSL TLV entry.
59         PP2_TYPE_SSL = 0x20,
60
61         // The minimum length of a PP2_TYPE_SSL TLV entry.
62         PP2_TYPE_SSL_LENGTH = 5,
63
64         // The length of the PROXY protocol signature.
65         PP2_SIGNATURE_LENGTH = 12,
66
67         // The PROXY protocol version we support.
68         PP2_VERSION = 0x20,
69
70         // The bitmask we apply to extract the protocol version.
71         PP2_VERSION_MASK = 0xF0
72 };
73
74 enum HAProxyState
75 {
76         // We are waiting for the PROXY header section.
77         HPS_WAITING_FOR_HEADER,
78
79         // We are waiting for the PROXY address section.
80         HPS_WAITING_FOR_ADDRESS,
81
82         // The client is fully connected.
83         HPS_CONNECTED
84 };
85
86 enum HAProxyCommand
87 {
88         // LOCAL command.
89         HPC_LOCAL = 0x00,
90
91         // PROXY command.
92         HPC_PROXY = 0x01
93 };
94
95 struct HAProxyHeader
96 {
97         // The signature used to identify the HAProxy protocol.
98         uint8_t signature[PP2_SIGNATURE_LENGTH];
99
100         // The version of the PROXY protocol and command being sent.
101         uint8_t version_command;
102
103         // The family for the address.
104         uint8_t family;
105
106         // The length of the address section.
107         uint16_t length;
108 };
109
110 class HAProxyHookProvider : public IOHookProvider
111 {
112  private:
113          UserCertificateAPI sslapi;
114
115  public:
116         HAProxyHookProvider(Module* mod)
117                 : IOHookProvider(mod, "haproxy", IOHookProvider::IOH_UNKNOWN, true)
118                 , sslapi(mod)
119         {
120         }
121
122         void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE;
123
124         void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
125         {
126                 // We don't need to implement this.
127         }
128 };
129
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";
132
133 class HAProxyHook : public IOHookMiddle
134 {
135  private:
136         // The length of the address section.
137         uint16_t address_length;
138
139         // The endpoint the client is connecting from.
140         irc::sockets::sockaddrs client;
141
142         // The command sent by the proxy server.
143         HAProxyCommand command;
144
145         // The endpoint the client is connected to.
146         irc::sockets::sockaddrs server;
147
148         // The API for interacting with user SSL internals.
149         UserCertificateAPI& sslapi;
150
151         // The current state of the PROXY parser.
152         HAProxyState state;
153
154         size_t ReadProxyTLV(StreamSocket* sock, size_t start_index, uint16_t buffer_length)
155         {
156                 // A TLV must at least consist of a type (uint8_t) and a length (uint16_t).
157                 if (buffer_length < PP2_TLV_LENGTH)
158                 {
159                         sock->SetError("Truncated HAProxy PROXY TLV type and/or length");
160                         return 0;
161                 }
162
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)
167                 {
168                         sock->SetError("Truncated HAProxy PROXY TLV value");
169                         return 0;
170                 }
171
172                 // What type of TLV are we parsing?
173                 switch (recvq[start_index])
174                 {
175                         case PP2_TYPE_SSL:
176                                 if (!ReadProxyTLVSSL(sock, start_index + PP2_TLV_LENGTH, length))
177                                         return 0;
178                                 break;
179                 }
180
181                 return PP2_TLV_LENGTH + length;
182         }
183
184         bool ReadProxyTLVSSL(StreamSocket* sock, size_t start_index, uint16_t buffer_length)
185         {
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)
188                 {
189                         sock->SetError("Truncated HAProxy PROXY SSL TLV");
190                         return false;
191                 }
192
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)
196                         return true;
197
198                 // If the sslinfo module is not loaded we can't
199                 // do anything with this TLV.
200                 if (!sslapi)
201                         return true;
202
203                 // If the client is not connecting via TLS (SSL) the rest of this TLV is irrelevant.
204                 std::string& recvq = GetRecvQ();
205                 if ((recvq[start_index] & PP2_CLIENT_SSL) == 0)
206                         return true;
207
208                 // Create a fake ssl_cert for the user. Ideally we should use the user's
209                 // TLS (SSL) client certificate here but as of 2018-10-16 this is not forwarded
210                 // by HAProxy.
211                 ssl_cert* cert = new ssl_cert;
212                 cert->error = "HAProxy does not forward client TLS (SSL) certificates";
213                 cert->invalid = true;
214                 cert->revoked = true;
215                 cert->trusted = false;
216                 cert->unknownsigner = true;
217
218                 // Extract the user for this socket and set their certificate.
219                 LocalUser* luser = static_cast<UserIOHandler*>(sock)->user;
220                 sslapi->SetCertificate(luser, cert);
221                 return true;
222         }
223
224         int ReadData(std::string& destrecvq)
225         {
226                 // Once connected we handle no special data.
227                 std::string& recvq = GetRecvQ();
228                 destrecvq.append(recvq);
229                 recvq.clear();
230                 return 1;
231         }
232
233         int ReadProxyAddress(StreamSocket* sock, std::string& destrecvq)
234         {
235                 // Block until we have the entire address.
236                 std::string& recvq = GetRecvQ();
237                 if (recvq.length() < address_length)
238                         return 0;
239
240                 switch (command)
241                 {
242                         case HPC_LOCAL:
243                                 // Skip the address completely.
244                                 recvq.erase(0, address_length);
245                                 break;
246
247                         case HPC_PROXY:
248                                 // Store the endpoint information.
249                                 size_t tlv_index = 0;
250                                 switch (client.family())
251                                 {
252                                         case AF_INET:
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);
257                                                 tlv_index = 12;
258                                                 break;
259
260                                         case AF_INET6:
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);
265                                                 tlv_index = 36;
266                                                 break;
267
268                                         case AF_UNIX:
269                                                 memcpy(client.un.sun_path, &recvq[0], 108);
270                                                 memcpy(server.un.sun_path, &recvq[108], 108);
271                                                 tlv_index = 216;
272                                                 break;
273                                 }
274
275                                 if (!sock->OnSetEndPoint(server, client))
276                                         return -1;
277
278                                 // Parse any available TLVs.
279                                 while (tlv_index < address_length)
280                                 {
281                                         size_t length = ReadProxyTLV(sock, tlv_index, address_length - tlv_index);
282                                         if (!length)
283                                                 return -1;
284
285                                         tlv_index += length;
286                                 }
287
288                                 // Erase the processed proxy information from the receive queue.
289                                 recvq.erase(0, address_length);
290                                 break;
291                 }
292
293                 // We're done!
294                 state = HPS_CONNECTED;
295                 return ReadData(destrecvq);
296         }
297
298         int ReadProxyHeader(StreamSocket* sock, std::string& destrecvq)
299         {
300                 // Block until we have a header.
301                 std::string& recvq = GetRecvQ();
302                 if (recvq.length() < PP2_HEADER_LENGTH)
303                         return 0;
304
305                 // Read the header.
306                 HAProxyHeader header;
307                 memcpy(&header, recvq.c_str(), PP2_HEADER_LENGTH);
308                 recvq.erase(0, PP2_HEADER_LENGTH);
309
310                 // Check we are actually parsing a HAProxy header.
311                 if (memcmp(&header.signature, proxy_signature, PP2_SIGNATURE_LENGTH) != 0)
312                 {
313                         // If we've reached this point the proxy server did not send a proxy information.
314                         sock->SetError("Invalid HAProxy PROXY signature");
315                         return -1;
316                 }
317
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)
321                 {
322                         sock->SetError("Unsupported HAProxy PROXY protocol version");
323                         return -1;
324                 }
325
326                 // We only support the LOCAL and PROXY commands.
327                 command = static_cast<HAProxyCommand>(header.version_command & PP2_COMMAND_MASK);
328                 switch (command)
329                 {
330                         case HPC_LOCAL:
331                                 // Intentionally left blank.
332                                 break;
333
334                         case HPC_PROXY:
335                                 // Check the protocol support and initialise the sockaddrs.
336                                 uint16_t shortest_length;
337                                 switch (header.family)
338                                 {
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;
342                                                 break;
343
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;
347                                                 break;
348
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;
352                                                 break;
353
354                                         default: // Unknown protocol.
355                                                 sock->SetError("Invalid HAProxy PROXY protocol type");
356                                                 return -1;
357                                 }
358
359                                 // Check that the length can actually contain the addresses.
360                                 address_length = ntohs(header.length);
361                                 if (address_length < shortest_length)
362                                 {
363                                         sock->SetError("Truncated HAProxy PROXY address section");
364                                         return -1;
365                                 }
366                                 break;
367
368                         default:
369                                 sock->SetError("Unsupported HAProxy PROXY command");
370                                 return -1;
371                 }
372
373                 state = HPS_WAITING_FOR_ADDRESS;
374                 return ReadProxyAddress(sock, destrecvq);
375         }
376
377  public:
378         HAProxyHook(IOHookProvider* Prov, StreamSocket* sock, UserCertificateAPI& api)
379                 : IOHookMiddle(Prov)
380                 , address_length(0)
381                 , sslapi(api)
382                 , state(HPS_WAITING_FOR_HEADER)
383         {
384                 sock->AddIOHook(this);
385         }
386
387         int OnStreamSocketWrite(StreamSocket* sock, StreamSocket::SendQueue& uppersendq) CXX11_OVERRIDE
388         {
389                 // We don't need to implement this.
390                 GetSendQ().moveall(uppersendq);
391                 return 1;
392         }
393
394         int OnStreamSocketRead(StreamSocket* sock, std::string& destrecvq) CXX11_OVERRIDE
395         {
396                 switch (state)
397                 {
398                         case HPS_WAITING_FOR_HEADER:
399                                 return ReadProxyHeader(sock, destrecvq);
400
401                         case HPS_WAITING_FOR_ADDRESS:
402                                 return ReadProxyAddress(sock, destrecvq);
403
404                         case HPS_CONNECTED:
405                                 return ReadData(destrecvq);
406                 }
407
408                 // We should never reach this point.
409                 return -1;
410         }
411
412         void OnStreamSocketClose(StreamSocket* sock) CXX11_OVERRIDE
413         {
414                 // We don't need to implement this.
415         }
416 };
417
418 void HAProxyHookProvider::OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
419 {
420         new HAProxyHook(this, sock, sslapi);
421 }
422
423 class ModuleHAProxy : public Module
424 {
425  private:
426         reference<HAProxyHookProvider> hookprov;
427
428  public:
429         ModuleHAProxy()
430                 : hookprov(new HAProxyHookProvider(this))
431         {
432         }
433
434         Version GetVersion() CXX11_OVERRIDE
435         {
436                 return Version("Allows IRC connections to be made using reverse proxies that implement the HAProxy PROXY protocol.", VF_VENDOR);
437         }
438 };
439
440 MODULE_INIT(ModuleHAProxy)