1 /*
2  * Copyright (c) 2017 Antoine Kaufmann <toni@famkaufmann.info>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include "includes.h"
18 
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <sys/tree.h>
22 #include <sys/un.h>
23 
24 #include <err.h>
25 #include <errno.h>
26 #include <event.h>
27 #include <imsg.h>
28 #include <inttypes.h>
29 #include <limits.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 
35 #include "log.h"
36 #include "smtpd.h"
37 
38 
39 /*
40  * The PROXYv2 protocol is described here:
41  * http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
42  */
43 
44 #define PROXY_CLOCAL 0x0
45 #define PROXY_CPROXY 0x1
46 #define PROXY_AF_UNSPEC 0x0
47 #define PROXY_AF_INET 0x1
48 #define PROXY_AF_INET6 0x2
49 #define PROXY_AF_UNIX 0x3
50 #define PROXY_TF_UNSPEC 0x0
51 #define PROXY_TF_STREAM 0x1
52 #define PROXY_TF_DGRAM 0x2
53 
54 #define PROXY_SESSION_TIMEOUT	300
55 
56 static const uint8_t pv2_signature[] = {
57 	0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D,
58 	0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A
59 };
60 
61 struct proxy_hdr_v2 {
62 	uint8_t sig[12];
63 	uint8_t ver_cmd;
64 	uint8_t fam;
65 	uint16_t len;
66 } __attribute__((packed));
67 
68 
69 struct proxy_addr_ipv4 {
70 	uint32_t src_addr;
71 	uint32_t dst_addr;
72 	uint16_t src_port;
73 	uint16_t dst_port;
74 } __attribute__((packed));
75 
76 struct proxy_addr_ipv6 {
77 	uint8_t src_addr[16];
78 	uint8_t dst_addr[16];
79 	uint16_t src_port;
80 	uint16_t dst_port;
81 } __attribute__((packed));
82 
83 struct proxy_addr_unix {
84 	uint8_t src_addr[108];
85 	uint8_t dst_addr[108];
86 } __attribute__((packed));
87 
88 union proxy_addr {
89 	struct proxy_addr_ipv4 ipv4;
90 	struct proxy_addr_ipv6 ipv6;
91 	struct proxy_addr_unix un;
92 } __attribute__((packed));
93 
94 struct proxy_session {
95 	struct listener	*l;
96 	struct io	*io;
97 
98 	uint64_t	id;
99 	int		fd;
100 	uint16_t	header_len;
101 	uint16_t	header_total;
102 	uint16_t	addr_len;
103 	uint16_t	addr_total;
104 
105 	struct sockaddr_storage	ss;
106 	struct proxy_hdr_v2	hdr;
107 	union proxy_addr	addr;
108 
109 	void (*cb_accepted)(struct listener *, int,
110 	    const struct sockaddr_storage *, struct io *);
111 	void (*cb_dropped)(struct listener *, int,
112 	    const struct sockaddr_storage *);
113 };
114 
115 static void proxy_io(struct io *, int, void *);
116 static void proxy_error(struct proxy_session *, const char *, const char *);
117 static int proxy_header_validate(struct proxy_session *);
118 static int proxy_translate_ss(struct proxy_session *);
119 
120 int
121 proxy_session(struct listener *listener, int sock,
122     const struct sockaddr_storage *ss,
123     void (*accepted)(struct listener *, int,
124 	const struct sockaddr_storage *, struct io *),
125     void (*dropped)(struct listener *, int,
126 	const struct sockaddr_storage *));
127 
128 int
proxy_session(struct listener * listener,int sock,const struct sockaddr_storage * ss,void (* accepted)(struct listener *,int,const struct sockaddr_storage *,struct io *),void (* dropped)(struct listener *,int,const struct sockaddr_storage *))129 proxy_session(struct listener *listener, int sock,
130     const struct sockaddr_storage *ss,
131     void (*accepted)(struct listener *, int,
132 	const struct sockaddr_storage *, struct io *),
133     void (*dropped)(struct listener *, int,
134 	const struct sockaddr_storage *))
135 {
136 	struct proxy_session *s;
137 
138 	if ((s = calloc(1, sizeof(*s))) == NULL)
139 		return (-1);
140 
141 	if ((s->io = io_new()) == NULL) {
142 		free(s);
143 		return (-1);
144 	}
145 
146 	s->id = generate_uid();
147 	s->l = listener;
148 	s->fd = sock;
149 	s->header_len = 0;
150 	s->addr_len = 0;
151 	s->ss = *ss;
152 	s->cb_accepted = accepted;
153 	s->cb_dropped = dropped;
154 
155 	io_set_callback(s->io, proxy_io, s);
156 	io_set_fd(s->io, sock);
157 	io_set_timeout(s->io, PROXY_SESSION_TIMEOUT * 1000);
158 	io_set_read(s->io);
159 
160 	log_info("%016"PRIx64" smtp event=proxy address=%s",
161 	    s->id, ss_to_text(&s->ss));
162 
163 	return 0;
164 }
165 
166 static void
proxy_io(struct io * io,int evt,void * arg)167 proxy_io(struct io *io, int evt, void *arg)
168 {
169 	struct proxy_session	*s = arg;
170 	struct proxy_hdr_v2	*h = &s->hdr;
171 	uint8_t *buf;
172 	size_t len, off;
173 
174 	switch (evt) {
175 
176 	case IO_DATAIN:
177 		buf = io_data(io);
178 		len = io_datalen(io);
179 
180 		if (s->header_len < sizeof(s->hdr)) {
181 			/* header is incomplete */
182 			off = sizeof(s->hdr) - s->header_len;
183 			off = (len < off ? len : off);
184 			memcpy((uint8_t *) &s->hdr + s->header_len, buf, off);
185 
186 			s->header_len += off;
187 			buf += off;
188 			len -= off;
189 			io_drop(s->io, off);
190 
191 			if (s->header_len < sizeof(s->hdr)) {
192 				/* header is still not complete */
193 				return;
194 			}
195 
196 			if (proxy_header_validate(s) != 0)
197 				return;
198 		}
199 
200 		if (s->addr_len < s->addr_total) {
201 			/* address is incomplete */
202 			off = s->addr_total - s->addr_len;
203 			off = (len < off ? len : off);
204 			memcpy((uint8_t *) &s->addr + s->addr_len, buf, off);
205 
206 			s->header_len += off;
207 			s->addr_len += off;
208 			buf += off;
209 			len -= off;
210 			io_drop(s->io, off);
211 
212 			if (s->addr_len < s->addr_total) {
213 				/* address is still not complete */
214 				return;
215 			}
216 		}
217 
218 		if (s->header_len < s->header_total) {
219 			/* additional parameters not complete */
220 			/* these are ignored for now, but we still need to drop
221 			 * the bytes from the buffer */
222 			off = s->header_total - s->header_len;
223 			off = (len < off ? len : off);
224 
225 			s->header_len += off;
226 			io_drop(s->io, off);
227 
228 			if (s->header_len < s->header_total)
229 				/* not complete yet */
230 				return;
231 		}
232 
233 		switch(h->ver_cmd & 0xF) {
234 		case PROXY_CLOCAL:
235 			/* local address, no need to modify ss */
236 			break;
237 
238 		case PROXY_CPROXY:
239 			if (proxy_translate_ss(s) != 0)
240 				return;
241 			break;
242 
243 		default:
244 			proxy_error(s, "protocol error", "unknown command");
245 			return;
246 		}
247 
248 		s->cb_accepted(s->l, s->fd, &s->ss, s->io);
249 		/* we passed off s->io, so it does not need to be freed here */
250 		free(s);
251 		break;
252 
253 	case IO_TIMEOUT:
254 		proxy_error(s, "timeout", NULL);
255 		break;
256 
257 	case IO_DISCONNECTED:
258 		proxy_error(s, "disconnected", NULL);
259 		break;
260 
261 	case IO_ERROR:
262 		proxy_error(s, "IO error", io_error(io));
263 		break;
264 
265 	default:
266 		fatalx("proxy_io()");
267 	}
268 
269 }
270 
271 static void
proxy_error(struct proxy_session * s,const char * reason,const char * extra)272 proxy_error(struct proxy_session *s, const char *reason, const char *extra)
273 {
274 	if (extra)
275 		log_info("proxy %p event=closed address=%s reason=\"%s (%s)\"",
276 		    s, ss_to_text(&s->ss), reason, extra);
277 	else
278 		log_info("proxy %p event=closed address=%s reason=\"%s\"",
279 		    s, ss_to_text(&s->ss), reason);
280 
281 	s->cb_dropped(s->l, s->fd, &s->ss);
282 	io_free(s->io);
283 	free(s);
284 }
285 
286 static int
proxy_header_validate(struct proxy_session * s)287 proxy_header_validate(struct proxy_session *s)
288 {
289 	struct proxy_hdr_v2 *h = &s->hdr;
290 
291 	if (memcmp(h->sig, pv2_signature,
292 		sizeof(pv2_signature)) != 0) {
293 		proxy_error(s, "protocol error", "invalid signature");
294 		return (-1);
295 	}
296 
297 	if ((h->ver_cmd >> 4) != 2) {
298 		proxy_error(s, "protocol error", "invalid version");
299 		return (-1);
300 	}
301 
302 	switch (h->fam) {
303 	case (PROXY_AF_UNSPEC << 4 | PROXY_TF_UNSPEC):
304 		s->addr_total = 0;
305 		break;
306 
307 	case (PROXY_AF_INET << 4 | PROXY_TF_STREAM):
308 		s->addr_total = sizeof(s->addr.ipv4);
309 		break;
310 
311 	case (PROXY_AF_INET6 << 4 | PROXY_TF_STREAM):
312 		s->addr_total = sizeof(s->addr.ipv6);
313 		break;
314 
315 	case (PROXY_AF_UNIX << 4 | PROXY_TF_STREAM):
316 		s->addr_total = sizeof(s->addr.un);
317 		break;
318 
319 	default:
320 		proxy_error(s, "protocol error", "unsupported address family");
321 		return (-1);
322 	}
323 
324 	s->header_total = ntohs(h->len);
325 	if (s->header_total > UINT16_MAX - sizeof(struct proxy_hdr_v2)) {
326 		proxy_error(s, "protocol error", "header too long");
327 		return (-1);
328 	}
329 	s->header_total += sizeof(struct proxy_hdr_v2);
330 
331 	if (s->header_total < sizeof(struct proxy_hdr_v2) + s->addr_total) {
332 		proxy_error(s, "protocol error", "address info too short");
333 		return (-1);
334 	}
335 
336 	return 0;
337 }
338 
339 static int
proxy_translate_ss(struct proxy_session * s)340 proxy_translate_ss(struct proxy_session *s)
341 {
342 	struct sockaddr_in *sin = (struct sockaddr_in *) &s->ss;
343 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &s->ss;
344 	struct sockaddr_un *sun = (struct sockaddr_un *) &s->ss;
345 	size_t sun_len;
346 
347 	switch (s->hdr.fam) {
348 	case (PROXY_AF_UNSPEC << 4 | PROXY_TF_UNSPEC):
349 		/* unspec: only supported for local */
350 		proxy_error(s, "address translation", "UNSPEC family not "
351 		    "supported for PROXYing");
352 		return (-1);
353 
354 	case (PROXY_AF_INET << 4 | PROXY_TF_STREAM):
355 		memset(&s->ss, 0, sizeof(s->ss));
356 		sin->sin_family = AF_INET;
357 		sin->sin_port = s->addr.ipv4.src_port;
358 		sin->sin_addr.s_addr = s->addr.ipv4.src_addr;
359 		break;
360 
361 	case (PROXY_AF_INET6 << 4 | PROXY_TF_STREAM):
362 		memset(&s->ss, 0, sizeof(s->ss));
363 		sin6->sin6_family = AF_INET6;
364 		sin6->sin6_port = s->addr.ipv6.src_port;
365 		memcpy(sin6->sin6_addr.s6_addr, s->addr.ipv6.src_addr,
366 		    sizeof(s->addr.ipv6.src_addr));
367 		break;
368 
369 	case (PROXY_AF_UNIX << 4 | PROXY_TF_STREAM):
370 		memset(&s->ss, 0, sizeof(s->ss));
371 		sun_len = strnlen(s->addr.un.src_addr,
372 		    sizeof(s->addr.un.src_addr));
373 		if (sun_len > sizeof(sun->sun_path)) {
374 			proxy_error(s, "address translation", "Unix socket path"
375 			    " longer than supported");
376 			return (-1);
377 		}
378 		sun->sun_family = AF_UNIX;
379 		memcpy(sun->sun_path, s->addr.un.src_addr, sun_len);
380 		break;
381 
382 	default:
383 		fatalx("proxy_translate_ss()");
384 	}
385 
386 	return 0;
387 }
388