]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_haproxy.cpp
m_haproxy: Initialize address length to 0
[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], 4);
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(server.un.sun_path, &recvq[108], 108);
260                                                 tlv_index = 216;
261                                                 break;
262                                 }
263
264                                 if (!sock->OnSetEndPoint(server, client))
265                                         return -1;
266
267                                 // Parse any available TLVs.
268                                 while (tlv_index < address_length)
269                                 {
270                                         size_t length = ReadProxyTLV(sock, tlv_index, address_length - tlv_index);
271                                         if (!length)
272                                                 return -1;
273
274                                         tlv_index += length;
275                                 }
276
277                                 // Erase the processed proxy information from the receive queue.
278                                 recvq.erase(0, address_length);
279                 }
280
281                 // We're done!
282                 state = HPS_CONNECTED;
283                 return 1;
284         }
285
286         int ReadProxyHeader(StreamSocket* sock)
287         {
288                 // Block until we have a header.
289                 std::string& recvq = GetRecvQ();
290                 if (recvq.length() < PP2_HEADER_LENGTH)
291                         return 0;
292
293                 // Read the header.
294                 HAProxyHeader header;
295                 memcpy(&header, recvq.c_str(), PP2_HEADER_LENGTH);
296                 recvq.erase(0, PP2_HEADER_LENGTH);
297
298                 // Check we are actually parsing a HAProxy header.
299                 if (memcmp(&header.signature, proxy_signature, PP2_SIGNATURE_LENGTH) != 0)
300                 {
301                         // If we've reached this point the proxy server did not send a proxy information.
302                         sock->SetError("Invalid HAProxy PROXY signature");
303                         return -1;
304                 }
305
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)
309                 {
310                         sock->SetError("Unsupported HAProxy PROXY protocol version");
311                         return -1;
312                 }
313
314                 // We only support the LOCAL and PROXY commands.
315                 command = static_cast<HAProxyCommand>(header.version_command & PP2_COMMAND_MASK);
316                 switch (command)
317                 {
318                         case HPC_LOCAL:
319                                 // Intentionally left blank.
320                                 break;
321
322                         case HPC_PROXY:
323                                 // Check the protocol support and initialise the sockaddrs.
324                                 uint16_t shortest_length;
325                                 switch (header.family)
326                                 {
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;
330                                                 break;
331
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;
335                                                 break;
336
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;
340                                                 break;
341
342                                         default: // Unknown protocol.
343                                                 sock->SetError("Invalid HAProxy PROXY protocol type");
344                                                 return -1;
345                                 }
346
347                                 // Check that the length can actually contain the addresses.
348                                 address_length = ntohs(header.length);
349                                 if (address_length < shortest_length)
350                                 {
351                                         sock->SetError("Truncated HAProxy PROXY address section");
352                                         return -1;
353                                 }
354                                 break;
355
356                         default:
357                                 sock->SetError("Unsupported HAProxy PROXY command");
358                                 return -1;
359                 }
360
361                 state = HPS_WAITING_FOR_ADDRESS;
362                 return ReadProxyAddress(sock);
363         }
364
365  public:
366         HAProxyHook(IOHookProvider* Prov, StreamSocket* sock, UserCertificateAPI& api)
367                 : IOHookMiddle(Prov)
368                 , sslapi(api)
369                 , state(HPS_WAITING_FOR_HEADER)
370                 , address_length(0)
371         {
372                 sock->AddIOHook(this);
373         }
374
375         int OnStreamSocketWrite(StreamSocket* sock, StreamSocket::SendQueue& uppersendq) CXX11_OVERRIDE
376         {
377                 // We don't need to implement this.
378                 GetSendQ().moveall(uppersendq);
379                 return 1;
380         }
381
382         int OnStreamSocketRead(StreamSocket* sock, std::string& destrecvq) CXX11_OVERRIDE
383         {
384                 switch (state)
385                 {
386                         case HPS_WAITING_FOR_HEADER:
387                                 return ReadProxyHeader(sock);
388
389                         case HPS_WAITING_FOR_ADDRESS:
390                                 return ReadProxyAddress(sock);
391
392                         case HPS_CONNECTED:
393                                 std::string& recvq = GetRecvQ();
394                                 destrecvq.append(recvq);
395                                 recvq.clear();
396                                 return 1;
397                 }
398
399                 // We should never reach this point.
400                 return -1;
401         }
402
403         void OnStreamSocketClose(StreamSocket* sock) CXX11_OVERRIDE
404         {
405                 // We don't need to implement this.
406         }
407 };
408
409 void HAProxyHookProvider::OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
410 {
411         new HAProxyHook(this, sock, sslapi);
412 }
413
414 class ModuleHAProxy : public Module
415 {
416  private:
417         reference<HAProxyHookProvider> hookprov;
418
419  public:
420         ModuleHAProxy()
421                 : hookprov(new HAProxyHookProvider(this))
422         {
423         }
424
425         Version GetVersion() CXX11_OVERRIDE
426         {
427                 return Version("Provides support for the HAProxy PROXY protocol", VF_VENDOR);
428         }
429 };
430
431 MODULE_INIT(ModuleHAProxy)