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