Move Proxy-Protocol impl to separate srcfile
[exim.git] / src / src / proxy.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) The Exim Maintainers 2020 - 2023 */
6 /* Copyright (c) University of Cambridge 1995 - 2018 */
7 /* See the file NOTICE for conditions of use and distribution. */
8 /* SPDX-License-Identifier: GPL-2.0-or-later */
9
10 /************************************************
11 *            Proxy-Protocol support             *
12 ************************************************/
13
14 #include "exim.h"
15
16 #ifdef SUPPORT_PROXY
17 /*************************************************
18 *       Check if host is required proxy host     *
19 *************************************************/
20 /* The function determines if inbound host will be a regular smtp host
21 or if it is configured that it must use Proxy Protocol.  A local
22 connection cannot.
23
24 Arguments: none
25 Returns:   boolean for Proxy Protocol needed
26 */
27
28 BOOL
29 proxy_protocol_host(void)
30 {
31 int rc;
32
33 if (  sender_host_address
34    && (rc = verify_check_this_host(CUSS &hosts_proxy, NULL, NULL,
35                            sender_host_address, NULL)) == OK)
36   {
37   DEBUG(D_receive)
38     debug_printf("Detected proxy protocol configured host\n");
39   proxy_session = TRUE;
40   }
41 return proxy_session;
42 }
43
44
45 /*************************************************
46 *    Read data until newline or end of buffer    *
47 *************************************************/
48 /* While SMTP is server-speaks-first, TLS is client-speaks-first, so we can't
49 read an entire buffer and assume there will be nothing past a proxy protocol
50 header.  Our approach normally is to use stdio, but again that relies upon
51 "STARTTLS\r\n" and a server response before the client starts TLS handshake, or
52 reading _nothing_ before client TLS handshake.  So we don't want to use the
53 usual buffering reads which may read enough to block TLS starting.
54
55 So unfortunately we're down to "read one byte at a time, with a syscall each,
56 and expect a little overhead", for all proxy-opened connections which are v1,
57 just to handle the TLS-on-connect case.  Since SSL functions wrap the
58 underlying fd, we can't assume that we can feed them any already-read content.
59
60 We need to know where to read to, the max capacity, and we'll read until we
61 get a CR and one more character.  Let the caller scream if it's CR+!LF.
62
63 Return the amount read.
64 */
65
66 static int
67 swallow_until_crlf(int fd, uschar *base, int already, int capacity)
68 {
69 uschar *to = base + already;
70 uschar *cr;
71 int have = 0;
72 int ret;
73 int last = 0;
74
75 /* For "PROXY UNKNOWN\r\n" we, at time of writing, expect to have read
76 up through the \r; for the _normal_ case, we haven't yet seen the \r. */
77
78 cr = memchr(base, '\r', already);
79 if (cr != NULL)
80   {
81   if ((cr - base) < already - 1)
82     {
83     /* \r and presumed \n already within what we have; probably not
84     actually proxy protocol, but abort cleanly. */
85     return 0;
86     }
87   /* \r is last character read, just need one more. */
88   last = 1;
89   }
90
91 while (capacity > 0)
92   {
93   do { ret = read(fd, to, 1); } while (ret == -1 && errno == EINTR && !had_command_timeout);
94   if (ret == -1)
95     return -1;
96   have++;
97   if (last)
98     return have;
99   if (*to == '\r')
100     last = 1;
101   capacity--;
102   to++;
103   }
104
105 /* reached end without having room for a final newline, abort */
106 errno = EOVERFLOW;
107 return -1;
108 }
109
110
111 static void
112 proxy_debug(uschar * buf, unsigned start, unsigned end)
113 {
114 debug_printf("PROXY<<");
115 while (start < end) debug_printf(" %02x", buf[start++]);
116 debug_printf("\n");
117 }
118
119
120 /*************************************************
121 *         Setup host for proxy protocol          *
122 *************************************************/
123 /* The function configures the connection based on a header from the
124 inbound host to use Proxy Protocol. The specification is very exact
125 so exit with an error if do not find the exact required pieces. This
126 includes an incorrect number of spaces separating args.
127
128 Arguments: none
129 Returns:   Boolean success
130 */
131
132 void
133 proxy_protocol_setup(void)
134 {
135 union {
136   struct {
137     uschar line[108];
138   } v1;
139   struct {
140     uschar sig[12];
141     uint8_t ver_cmd;
142     uint8_t fam;
143     uint16_t len;
144     union {
145       struct { /* TCP/UDP over IPv4, len = 12 */
146         uint32_t src_addr;
147         uint32_t dst_addr;
148         uint16_t src_port;
149         uint16_t dst_port;
150       } ip4;
151       struct { /* TCP/UDP over IPv6, len = 36 */
152         uint8_t  src_addr[16];
153         uint8_t  dst_addr[16];
154         uint16_t src_port;
155         uint16_t dst_port;
156       } ip6;
157       struct { /* AF_UNIX sockets, len = 216 */
158         uschar   src_addr[108];
159         uschar   dst_addr[108];
160       } unx;
161     } addr;
162   } v2;
163 } hdr;
164
165 /* Temp variables used in PPv2 address:port parsing */
166 uint16_t tmpport;
167 char tmpip[INET_ADDRSTRLEN];
168 struct sockaddr_in tmpaddr;
169 char tmpip6[INET6_ADDRSTRLEN];
170 struct sockaddr_in6 tmpaddr6;
171
172 /* We can't read "all data until end" because while SMTP is
173 server-speaks-first, the TLS handshake is client-speaks-first, so for
174 TLS-on-connect ports the proxy protocol header will usually be immediately
175 followed by a TLS handshake, and with N TLS libraries, we can't reliably
176 reinject data for reading by those.  So instead we first read "enough to be
177 safely read within the header, and figure out how much more to read".
178 For v1 we will later read to the end-of-line, for v2 we will read based upon
179 the stated length.
180
181 The v2 sig is 12 octets, and another 4 gets us the length, so we know how much
182 data is needed total.  For v1, where the line looks like:
183 PROXY TCPn L3src L3dest SrcPort DestPort \r\n
184
185 However, for v1 there's also `PROXY UNKNOWN\r\n` which is only 15 octets.
186 We seem to support that.  So, if we read 14 octets then we can tell if we're
187 v2 or v1.  If we're v1, we can continue reading as normal.
188
189 If we're v2, we can't slurp up the entire header.  We need the length in the
190 15th & 16th octets, then to read everything after that.
191
192 So to safely handle v1 and v2, with client-sent-first supported correctly,
193 we have to do a minimum of 3 read calls, not 1.  Eww.
194 */
195
196 # define PROXY_INITIAL_READ 14
197 # define PROXY_V2_HEADER_SIZE 16
198 # if PROXY_INITIAL_READ > PROXY_V2_HEADER_SIZE
199 #  error Code bug in sizes of data to read for proxy usage
200 # endif
201
202 int get_ok = 0;
203 int size, ret;
204 int fd = fileno(smtp_in);
205 const char v2sig[12] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
206 uschar * iptype;  /* To display debug info */
207 socklen_t vslen = sizeof(struct timeval);
208 BOOL yield = FALSE;
209
210 ALARM(proxy_protocol_timeout);
211
212 do
213   {
214   /* The inbound host was declared to be a Proxy Protocol host, so
215   don't do a PEEK into the data, actually slurp up enough to be
216   "safe". Can't take it all because TLS-on-connect clients follow
217   immediately with TLS handshake. */
218   ret = read(fd, &hdr, PROXY_INITIAL_READ);
219   } while (ret == -1 && errno == EINTR && !had_command_timeout);
220
221 if (ret == -1)
222   goto proxyfail;
223 DEBUG(D_receive) proxy_debug(US &hdr, 0, ret);
224
225 /* For v2, handle reading the length, and then the rest. */
226 if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0))
227   {
228   int retmore;
229   uint8_t ver;
230
231   DEBUG(D_receive) debug_printf("v2\n");
232
233   /* First get the length fields. */
234   do
235     {
236     retmore = read(fd, (uschar*)&hdr + ret, PROXY_V2_HEADER_SIZE - PROXY_INITIAL_READ);
237     } while (retmore == -1 && errno == EINTR && !had_command_timeout);
238   if (retmore == -1)
239     goto proxyfail;
240   DEBUG(D_receive) proxy_debug(US &hdr, ret, ret + retmore);
241
242   ret += retmore;
243
244   ver = (hdr.v2.ver_cmd & 0xf0) >> 4;
245
246   /* May 2014: haproxy combined the version and command into one byte to
247   allow two full bytes for the length field in order to proxy SSL
248   connections.  SSL Proxy is not supported in this version of Exim, but
249   must still separate values here. */
250
251   if (ver != 0x02)
252     {
253     DEBUG(D_receive) debug_printf("Invalid Proxy Protocol version: %d\n", ver);
254     goto proxyfail;
255     }
256
257   /* The v2 header will always be 16 bytes per the spec. */
258   size = 16 + ntohs(hdr.v2.len);
259   DEBUG(D_receive) debug_printf("Detected PROXYv2 header, size %d (limit %d)\n",
260       size, (int)sizeof(hdr));
261
262   /* We should now have 16 octets (PROXY_V2_HEADER_SIZE), and we know the total
263   amount that we need.  Double-check that the size is not unreasonable, then
264   get the rest. */
265   if (size > sizeof(hdr))
266     {
267     DEBUG(D_receive) debug_printf("PROXYv2 header size unreasonably large; security attack?\n");
268     goto proxyfail;
269     }
270
271   do
272     {
273     do
274       {
275       retmore = read(fd, (uschar*)&hdr + ret, size-ret);
276       } while (retmore == -1 && errno == EINTR && !had_command_timeout);
277     if (retmore == -1)
278       goto proxyfail;
279     DEBUG(D_receive) proxy_debug(US &hdr, ret, ret + retmore);
280     ret += retmore;
281     DEBUG(D_receive) debug_printf("PROXYv2: have %d/%d required octets\n", ret, size);
282     } while (ret < size);
283
284   } /* end scope for getting rest of data for v2 */
285
286 /* At this point: if PROXYv2, we've read the exact size required for all data;
287 if PROXYv1 then we've read "less than required for any valid line" and should
288 read the rest". */
289
290 if (ret >= 16 && memcmp(&hdr.v2, v2sig, 12) == 0)
291   {
292   uint8_t cmd = (hdr.v2.ver_cmd & 0x0f);
293
294   switch (cmd)
295     {
296     case 0x01: /* PROXY command */
297       switch (hdr.v2.fam)
298         {
299         case 0x11:  /* TCPv4 address type */
300           iptype = US"IPv4";
301           tmpaddr.sin_addr.s_addr = hdr.v2.addr.ip4.src_addr;
302           inet_ntop(AF_INET, &tmpaddr.sin_addr, CS &tmpip, sizeof(tmpip));
303           if (!string_is_ip_address(US tmpip, NULL))
304             {
305             DEBUG(D_receive) debug_printf("Invalid %s source IP\n", iptype);
306             goto proxyfail;
307             }
308           proxy_local_address = sender_host_address;
309           sender_host_address = string_copy(US tmpip);
310           tmpport             = ntohs(hdr.v2.addr.ip4.src_port);
311           proxy_local_port    = sender_host_port;
312           sender_host_port    = tmpport;
313           /* Save dest ip/port */
314           tmpaddr.sin_addr.s_addr = hdr.v2.addr.ip4.dst_addr;
315           inet_ntop(AF_INET, &tmpaddr.sin_addr, CS &tmpip, sizeof(tmpip));
316           if (!string_is_ip_address(US tmpip, NULL))
317             {
318             DEBUG(D_receive) debug_printf("Invalid %s dest port\n", iptype);
319             goto proxyfail;
320             }
321           proxy_external_address = string_copy(US tmpip);
322           tmpport              = ntohs(hdr.v2.addr.ip4.dst_port);
323           proxy_external_port  = tmpport;
324           goto done;
325         case 0x21:  /* TCPv6 address type */
326           iptype = US"IPv6";
327           memmove(tmpaddr6.sin6_addr.s6_addr, hdr.v2.addr.ip6.src_addr, 16);
328           inet_ntop(AF_INET6, &tmpaddr6.sin6_addr, CS &tmpip6, sizeof(tmpip6));
329           if (!string_is_ip_address(US tmpip6, NULL))
330             {
331             DEBUG(D_receive) debug_printf("Invalid %s source IP\n", iptype);
332             goto proxyfail;
333             }
334           proxy_local_address = sender_host_address;
335           sender_host_address = string_copy(US tmpip6);
336           tmpport             = ntohs(hdr.v2.addr.ip6.src_port);
337           proxy_local_port    = sender_host_port;
338           sender_host_port    = tmpport;
339           /* Save dest ip/port */
340           memmove(tmpaddr6.sin6_addr.s6_addr, hdr.v2.addr.ip6.dst_addr, 16);
341           inet_ntop(AF_INET6, &tmpaddr6.sin6_addr, CS &tmpip6, sizeof(tmpip6));
342           if (!string_is_ip_address(US tmpip6, NULL))
343             {
344             DEBUG(D_receive) debug_printf("Invalid %s dest port\n", iptype);
345             goto proxyfail;
346             }
347           proxy_external_address = string_copy(US tmpip6);
348           tmpport              = ntohs(hdr.v2.addr.ip6.dst_port);
349           proxy_external_port  = tmpport;
350           goto done;
351         default:
352           DEBUG(D_receive)
353             debug_printf("Unsupported PROXYv2 connection type: 0x%02x\n",
354                          hdr.v2.fam);
355           goto proxyfail;
356         }
357       /* Unsupported protocol, keep local connection address */
358       break;
359     case 0x00: /* LOCAL command */
360       /* Keep local connection address for LOCAL */
361       iptype = US"local";
362       break;
363     default:
364       DEBUG(D_receive)
365         debug_printf("Unsupported PROXYv2 command: 0x%x\n", cmd);
366       goto proxyfail;
367     }
368   }
369 else if (ret >= 8 && memcmp(hdr.v1.line, "PROXY", 5) == 0)
370   {
371   uschar *p;
372   uschar *end;
373   uschar *sp;     /* Utility variables follow */
374   int     tmp_port;
375   int     r2;
376   char   *endc;
377
378   /* get the rest of the line */
379   r2 = swallow_until_crlf(fd, (uschar*)&hdr, ret, sizeof(hdr)-ret);
380   if (r2 == -1)
381     goto proxyfail;
382   ret += r2;
383
384   p = string_copy(hdr.v1.line);
385   end = memchr(p, '\r', ret - 1);
386
387   if (!end || (end == (uschar*)&hdr + ret) || end[1] != '\n')
388     {
389     DEBUG(D_receive) debug_printf("Partial or invalid PROXY header\n");
390     goto proxyfail;
391     }
392   *end = '\0'; /* Terminate the string */
393   size = end + 2 - p; /* Skip header + CRLF */
394   DEBUG(D_receive) debug_printf("Detected PROXYv1 header\n");
395   DEBUG(D_receive) debug_printf("Bytes read not within PROXY header: %d\n", ret - size);
396   /* Step through the string looking for the required fields. Ensure
397   strict adherence to required formatting, exit for any error. */
398   p += 5;
399   if (!isspace(*(p++)))
400     {
401     DEBUG(D_receive) debug_printf("Missing space after PROXY command\n");
402     goto proxyfail;
403     }
404   if (!Ustrncmp(p, CCS"TCP4", 4))
405     iptype = US"IPv4";
406   else if (!Ustrncmp(p,CCS"TCP6", 4))
407     iptype = US"IPv6";
408   else if (!Ustrncmp(p,CCS"UNKNOWN", 7))
409     {
410     iptype = US"Unknown";
411     goto done;
412     }
413   else
414     {
415     DEBUG(D_receive) debug_printf("Invalid TCP type\n");
416     goto proxyfail;
417     }
418
419   p += Ustrlen(iptype);
420   if (!isspace(*(p++)))
421     {
422     DEBUG(D_receive) debug_printf("Missing space after TCP4/6 command\n");
423     goto proxyfail;
424     }
425   /* Find the end of the arg */
426   if ((sp = Ustrchr(p, ' ')) == NULL)
427     {
428     DEBUG(D_receive)
429       debug_printf("Did not find proxied src %s\n", iptype);
430     goto proxyfail;
431     }
432   *sp = '\0';
433   if(!string_is_ip_address(p, NULL))
434     {
435     DEBUG(D_receive)
436       debug_printf("Proxied src arg is not an %s address\n", iptype);
437     goto proxyfail;
438     }
439   proxy_local_address = sender_host_address;
440   sender_host_address = p;
441   p = sp + 1;
442   if ((sp = Ustrchr(p, ' ')) == NULL)
443     {
444     DEBUG(D_receive)
445       debug_printf("Did not find proxy dest %s\n", iptype);
446     goto proxyfail;
447     }
448   *sp = '\0';
449   if(!string_is_ip_address(p, NULL))
450     {
451     DEBUG(D_receive)
452       debug_printf("Proxy dest arg is not an %s address\n", iptype);
453     goto proxyfail;
454     }
455   proxy_external_address = p;
456   p = sp + 1;
457   if ((sp = Ustrchr(p, ' ')) == NULL)
458     {
459     DEBUG(D_receive) debug_printf("Did not find proxied src port\n");
460     goto proxyfail;
461     }
462   *sp = '\0';
463   tmp_port = strtol(CCS p, &endc, 10);
464   if (*endc || tmp_port == 0)
465     {
466     DEBUG(D_receive)
467       debug_printf("Proxied src port '%s' not an integer\n", p);
468     goto proxyfail;
469     }
470   proxy_local_port = sender_host_port;
471   sender_host_port = tmp_port;
472   p = sp + 1;
473   if ((sp = Ustrchr(p, '\0')) == NULL)
474     {
475     DEBUG(D_receive) debug_printf("Did not find proxy dest port\n");
476     goto proxyfail;
477     }
478   tmp_port = strtol(CCS p, &endc, 10);
479   if (*endc || tmp_port == 0)
480     {
481     DEBUG(D_receive)
482       debug_printf("Proxy dest port '%s' not an integer\n", p);
483     goto proxyfail;
484     }
485   proxy_external_port = tmp_port;
486   /* Already checked for /r /n above. Good V1 header received. */
487   }
488 else
489   {
490   /* Wrong protocol */
491   DEBUG(D_receive) debug_printf("Invalid proxy protocol version negotiation\n");
492   (void) swallow_until_crlf(fd, (uschar*)&hdr, ret, sizeof(hdr)-ret);
493   goto proxyfail;
494   }
495
496 done:
497   DEBUG(D_receive)
498     debug_printf("Valid %s sender from Proxy Protocol header\n", iptype);
499   yield = proxy_session;
500
501 /* Don't flush any potential buffer contents. Any input on proxyfail
502 should cause a synchronization failure */
503
504 proxyfail:
505   DEBUG(D_receive) if (had_command_timeout)
506     debug_printf("Timeout while reading proxy header\n");
507
508 bad:
509   if (yield)
510     {
511     sender_host_name = NULL;
512     (void) host_name_lookup();
513     host_build_sender_fullhost();
514     }
515   else
516     {
517     f.proxy_session_failed = TRUE;
518     DEBUG(D_receive)
519       debug_printf("Failure to extract proxied host, only QUIT allowed\n");
520     }
521
522 ALARM(0);
523 return;
524 }
525 #endif  /*SUPPORT_PROXY*/
526
527 /* vi: aw ai sw=2
528 */
529 /* End of proxy.c */