]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_haproxy.cpp
Ignore clients on ulined servers when reporting stats in LUSERS.
[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 ReadData(std::string& destrecvq)
223         {
224                 // Once connected we handle no special data.
225                 std::string& recvq = GetRecvQ();
226                 destrecvq.append(recvq);
227                 recvq.clear();
228                 return 1;
229         }
230
231         int ReadProxyAddress(StreamSocket* sock, std::string& destrecvq)
232         {
233                 // Block until we have the entire address.
234                 std::string& recvq = GetRecvQ();
235                 if (recvq.length() < address_length)
236                         return 0;
237
238                 switch (command)
239                 {
240                         case HPC_LOCAL:
241                                 // Skip the address completely.
242                                 recvq.erase(0, address_length);
243                                 break;
244
245                         case HPC_PROXY:
246                                 // Store the endpoint information.
247                                 size_t tlv_index = 0;
248                                 switch (client.family())
249                                 {
250                                         case AF_INET:
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);
255                                                 tlv_index = 12;
256                                                 break;
257
258                                         case AF_INET6:
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);
263                                                 tlv_index = 36;
264                                                 break;
265
266                                         case AF_UNIX:
267                                                 memcpy(client.un.sun_path, &recvq[0], 108);
268                                                 memcpy(server.un.sun_path, &recvq[108], 108);
269                                                 tlv_index = 216;
270                                                 break;
271                                 }
272
273                                 if (!sock->OnSetEndPoint(server, client))
274                                         return -1;
275
276                                 // Parse any available TLVs.
277                                 while (tlv_index < address_length)
278                                 {
279                                         size_t length = ReadProxyTLV(sock, tlv_index, address_length - tlv_index);
280                                         if (!length)
281                                                 return -1;
282
283                                         tlv_index += length;
284                                 }
285
286                                 // Erase the processed proxy information from the receive queue.
287                                 recvq.erase(0, address_length);
288                                 break;
289                 }
290
291                 // We're done!
292                 state = HPS_CONNECTED;
293                 return ReadData(destrecvq);
294         }
295
296         int ReadProxyHeader(StreamSocket* sock, std::string& destrecvq)
297         {
298                 // Block until we have a header.
299                 std::string& recvq = GetRecvQ();
300                 if (recvq.length() < PP2_HEADER_LENGTH)
301                         return 0;
302
303                 // Read the header.
304                 HAProxyHeader header;
305                 memcpy(&header, recvq.c_str(), PP2_HEADER_LENGTH);
306                 recvq.erase(0, PP2_HEADER_LENGTH);
307
308                 // Check we are actually parsing a HAProxy header.
309                 if (memcmp(&header.signature, proxy_signature, PP2_SIGNATURE_LENGTH) != 0)
310                 {
311                         // If we've reached this point the proxy server did not send a proxy information.
312                         sock->SetError("Invalid HAProxy PROXY signature");
313                         return -1;
314                 }
315
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)
319                 {
320                         sock->SetError("Unsupported HAProxy PROXY protocol version");
321                         return -1;
322                 }
323
324                 // We only support the LOCAL and PROXY commands.
325                 command = static_cast<HAProxyCommand>(header.version_command & PP2_COMMAND_MASK);
326                 switch (command)
327                 {
328                         case HPC_LOCAL:
329                                 // Intentionally left blank.
330                                 break;
331
332                         case HPC_PROXY:
333                                 // Check the protocol support and initialise the sockaddrs.
334                                 uint16_t shortest_length;
335                                 switch (header.family)
336                                 {
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;
340                                                 break;
341
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;
345                                                 break;
346
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;
350                                                 break;
351
352                                         default: // Unknown protocol.
353                                                 sock->SetError("Invalid HAProxy PROXY protocol type");
354                                                 return -1;
355                                 }
356
357                                 // Check that the length can actually contain the addresses.
358                                 address_length = ntohs(header.length);
359                                 if (address_length < shortest_length)
360                                 {
361                                         sock->SetError("Truncated HAProxy PROXY address section");
362                                         return -1;
363                                 }
364                                 break;
365
366                         default:
367                                 sock->SetError("Unsupported HAProxy PROXY command");
368                                 return -1;
369                 }
370
371                 state = HPS_WAITING_FOR_ADDRESS;
372                 return ReadProxyAddress(sock, destrecvq);
373         }
374
375  public:
376         HAProxyHook(IOHookProvider* Prov, StreamSocket* sock, UserCertificateAPI& api)
377                 : IOHookMiddle(Prov)
378                 , address_length(0)
379                 , sslapi(api)
380                 , state(HPS_WAITING_FOR_HEADER)
381         {
382                 sock->AddIOHook(this);
383         }
384
385         int OnStreamSocketWrite(StreamSocket* sock, StreamSocket::SendQueue& uppersendq) CXX11_OVERRIDE
386         {
387                 // We don't need to implement this.
388                 GetSendQ().moveall(uppersendq);
389                 return 1;
390         }
391
392         int OnStreamSocketRead(StreamSocket* sock, std::string& destrecvq) CXX11_OVERRIDE
393         {
394                 switch (state)
395                 {
396                         case HPS_WAITING_FOR_HEADER:
397                                 return ReadProxyHeader(sock, destrecvq);
398
399                         case HPS_WAITING_FOR_ADDRESS:
400                                 return ReadProxyAddress(sock, destrecvq);
401
402                         case HPS_CONNECTED:
403                                 return ReadData(destrecvq);
404                 }
405
406                 // We should never reach this point.
407                 return -1;
408         }
409
410         void OnStreamSocketClose(StreamSocket* sock) CXX11_OVERRIDE
411         {
412                 // We don't need to implement this.
413         }
414 };
415
416 void HAProxyHookProvider::OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
417 {
418         new HAProxyHook(this, sock, sslapi);
419 }
420
421 class ModuleHAProxy : public Module
422 {
423  private:
424         reference<HAProxyHookProvider> hookprov;
425
426  public:
427         ModuleHAProxy()
428                 : hookprov(new HAProxyHookProvider(this))
429         {
430         }
431
432         Version GetVersion() CXX11_OVERRIDE
433         {
434                 return Version("Provides support for the HAProxy PROXY protocol", VF_VENDOR);
435         }
436 };
437
438 MODULE_INIT(ModuleHAProxy)