1/*	$OpenBSD: socks.c,v 1.24 2016/06/27 14:43:04 deraadt Exp $	*/
2
3/*
4 * Copyright (c) 1999 Niklas Hallqvist.  All rights reserved.
5 * Copyright (c) 2004, 2005 Damien Miller.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27#include <sys/cdefs.h>
28__RCSID("$NetBSD: socks.c,v 1.3 2017/02/09 20:37:58 joerg Exp $");
29
30#include <sys/types.h>
31#include <sys/socket.h>
32#include <netinet/in.h>
33#include <arpa/inet.h>
34
35#include <err.h>
36#include <errno.h>
37#include <netdb.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <unistd.h>
42#include <resolv.h>
43#ifdef __OpenBSD__
44#include <readpassphrase.h>
45#endif
46#include "atomicio.h"
47
48#define SOCKS_PORT	"1080"
49#define HTTP_PROXY_PORT	"3128"
50#define HTTP_MAXHDRS	64
51#define SOCKS_V5	5
52#define SOCKS_V4	4
53#define SOCKS_NOAUTH	0
54#define SOCKS_NOMETHOD	0xff
55#define SOCKS_CONNECT	1
56#define SOCKS_IPV4	1
57#define SOCKS_DOMAIN	3
58#define SOCKS_IPV6	4
59
60int	remote_connect(const char *, const char *, struct addrinfo);
61int	socks_connect(const char *, const char *, struct addrinfo,
62	    const char *, const char *, struct addrinfo, int,
63	    const char *);
64
65static int
66decode_addrport(const char *h, const char *p, struct sockaddr *addr,
67    socklen_t addrlen, int v4only, int numeric)
68{
69	int r;
70	struct addrinfo hints, *res;
71
72	bzero(&hints, sizeof(hints));
73	hints.ai_family = v4only ? PF_INET : PF_UNSPEC;
74	hints.ai_flags = numeric ? AI_NUMERICHOST : 0;
75	hints.ai_socktype = SOCK_STREAM;
76	r = getaddrinfo(h, p, &hints, &res);
77	/* Don't fatal when attempting to convert a numeric address */
78	if (r != 0) {
79		if (!numeric) {
80			errx(1, "getaddrinfo(\"%.64s\", \"%.64s\"): %s", h, p,
81			    gai_strerror(r));
82		}
83		return (-1);
84	}
85	if (addrlen < res->ai_addrlen) {
86		freeaddrinfo(res);
87		errx(1, "internal error: addrlen < res->ai_addrlen");
88	}
89	memcpy(addr, res->ai_addr, res->ai_addrlen);
90	freeaddrinfo(res);
91	return (0);
92}
93
94static int
95proxy_read_line(int fd, char *buf, size_t bufsz)
96{
97	size_t off;
98
99	for(off = 0;;) {
100		if (off >= bufsz)
101			errx(1, "proxy read too long");
102		if (atomicio(read, fd, buf + off, 1) != 1)
103			err(1, "proxy read");
104		/* Skip CR */
105		if (buf[off] == '\r')
106			continue;
107		if (buf[off] == '\n') {
108			buf[off] = '\0';
109			break;
110		}
111		off++;
112	}
113	return (off);
114}
115
116static const char *
117getproxypass(const char *proxyuser, const char *proxyhost)
118{
119	char prompt[512];
120	static char pw[256];
121
122	snprintf(prompt, sizeof(prompt), "Proxy password for %s@%s: ",
123	   proxyuser, proxyhost);
124#ifdef __NetBSD__
125     	if (getpassfd(prompt, pw, sizeof(pw), NULL, GETPASS_NEED_TTY, 0)
126	    == NULL)
127#else
128	if (readpassphrase(prompt, pw, sizeof(pw), RPP_REQUIRE_TTY) == NULL)
129#endif
130		err(1, "Unable to read proxy passphrase");
131	return (pw);
132}
133
134/*
135 * Error strings adapted from the generally accepted SOCKSv4 spec:
136 *
137 * http://ftp.icm.edu.pl/packages/socks/socks4/SOCKS4.protocol
138 */
139static const char *
140socks4_strerror(int e)
141{
142	switch (e) {
143	case 90:
144		return "Succeeded";
145	case 91:
146		return "Request rejected or failed";
147	case 92:
148		return "SOCKS server cannot connect to identd on the client";
149	case 93:
150		return "Client program and identd report different user-ids";
151	default:
152		return "Unknown error";
153	}
154}
155
156/*
157 * Error strings taken almost directly from RFC 1928.
158 */
159static const char *
160socks5_strerror(int e)
161{
162	switch (e) {
163	case 0:
164		return "Succeeded";
165	case 1:
166		return "General SOCKS server failure";
167	case 2:
168		return "Connection not allowed by ruleset";
169	case 3:
170		return "Network unreachable";
171	case 4:
172		return "Host unreachable";
173	case 5:
174		return "Connection refused";
175	case 6:
176		return "TTL expired";
177	case 7:
178		return "Command not supported";
179	case 8:
180		return "Address type not supported";
181	default:
182		return "Unknown error";
183	}
184}
185
186int
187socks_connect(const char *host, const char *port,
188    struct addrinfo hints __unused,
189    const char *proxyhost, const char *proxyport, struct addrinfo proxyhints,
190    int socksv, const char *proxyuser)
191{
192	int proxyfd, r, authretry = 0;
193	size_t hlen, wlen;
194	unsigned char buf[1024];
195	char *cbuf = (char *)buf;
196	size_t cnt;
197	struct sockaddr_storage addr;
198	struct sockaddr_in *in4 = (struct sockaddr_in *)&addr;
199	struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&addr;
200	in_port_t serverport;
201	const char *proxypass = NULL;
202
203	if (proxyport == NULL)
204		proxyport = (socksv == -1) ? HTTP_PROXY_PORT : SOCKS_PORT;
205
206	/* Abuse API to lookup port */
207	if (decode_addrport("0.0.0.0", port, (struct sockaddr *)&addr,
208	    sizeof(addr), 1, 1) == -1)
209		errx(1, "unknown port \"%.64s\"", port);
210	serverport = in4->sin_port;
211
212 again:
213	if (authretry++ > 3)
214		errx(1, "Too many authentication failures");
215
216	proxyfd = remote_connect(proxyhost, proxyport, proxyhints);
217
218	if (proxyfd < 0)
219		return (-1);
220
221	if (socksv == 5) {
222		if (decode_addrport(host, port, (struct sockaddr *)&addr,
223		    sizeof(addr), 0, 1) == -1)
224			addr.ss_family = 0; /* used in switch below */
225
226		/* Version 5, one method: no authentication */
227		buf[0] = SOCKS_V5;
228		buf[1] = 1;
229		buf[2] = SOCKS_NOAUTH;
230		cnt = atomicio(vwrite, proxyfd, buf, 3);
231		if (cnt != 3)
232			err(1, "write failed (%zu/3)", cnt);
233
234		cnt = atomicio(read, proxyfd, buf, 2);
235		if (cnt != 2)
236			err(1, "read failed (%zu/3)", cnt);
237
238		if (buf[1] == SOCKS_NOMETHOD)
239			errx(1, "authentication method negotiation failed");
240
241		switch (addr.ss_family) {
242		case 0:
243			/* Version 5, connect: domain name */
244
245			/* Max domain name length is 255 bytes */
246			hlen = strlen(host);
247			if (hlen > 255)
248				errx(1, "host name too long for SOCKS5");
249			buf[0] = SOCKS_V5;
250			buf[1] = SOCKS_CONNECT;
251			buf[2] = 0;
252			buf[3] = SOCKS_DOMAIN;
253			buf[4] = hlen;
254			memcpy(buf + 5, host, hlen);
255			memcpy(buf + 5 + hlen, &serverport, sizeof serverport);
256			wlen = 7 + hlen;
257			break;
258		case AF_INET:
259			/* Version 5, connect: IPv4 address */
260			buf[0] = SOCKS_V5;
261			buf[1] = SOCKS_CONNECT;
262			buf[2] = 0;
263			buf[3] = SOCKS_IPV4;
264			memcpy(buf + 4, &in4->sin_addr, sizeof in4->sin_addr);
265			memcpy(buf + 8, &in4->sin_port, sizeof in4->sin_port);
266			wlen = 10;
267			break;
268		case AF_INET6:
269			/* Version 5, connect: IPv6 address */
270			buf[0] = SOCKS_V5;
271			buf[1] = SOCKS_CONNECT;
272			buf[2] = 0;
273			buf[3] = SOCKS_IPV6;
274			memcpy(buf + 4, &in6->sin6_addr, sizeof in6->sin6_addr);
275			memcpy(buf + 20, &in6->sin6_port,
276			    sizeof in6->sin6_port);
277			wlen = 22;
278			break;
279		default:
280			errx(1, "internal error: silly AF");
281		}
282
283		cnt = atomicio(vwrite, proxyfd, buf, wlen);
284		if (cnt != wlen)
285			err(1, "write failed (%zu/%zu)", cnt, wlen);
286
287		cnt = atomicio(read, proxyfd, buf, 4);
288		if (cnt != 4)
289			err(1, "read failed (%zu/4)", cnt);
290		if (buf[1] != 0) {
291			errx(1, "connection failed, SOCKSv5 error: %s",
292			    socks5_strerror(buf[1]));
293		}
294		switch (buf[3]) {
295		case SOCKS_IPV4:
296			cnt = atomicio(read, proxyfd, buf + 4, 6);
297			if (cnt != 6)
298				err(1, "read failed (%zu/6)", cnt);
299			break;
300		case SOCKS_IPV6:
301			cnt = atomicio(read, proxyfd, buf + 4, 18);
302			if (cnt != 18)
303				err(1, "read failed (%zu/18)", cnt);
304			break;
305		default:
306			errx(1, "connection failed, unsupported address type");
307		}
308	} else if (socksv == 4) {
309		/* This will exit on lookup failure */
310		decode_addrport(host, port, (struct sockaddr *)&addr,
311		    sizeof(addr), 1, 0);
312
313		/* Version 4 */
314		buf[0] = SOCKS_V4;
315		buf[1] = SOCKS_CONNECT;	/* connect */
316		memcpy(buf + 2, &in4->sin_port, sizeof in4->sin_port);
317		memcpy(buf + 4, &in4->sin_addr, sizeof in4->sin_addr);
318		buf[8] = 0;	/* empty username */
319		wlen = 9;
320
321		cnt = atomicio(vwrite, proxyfd, buf, wlen);
322		if (cnt != wlen)
323			err(1, "write failed (%zu/%zu)", cnt, wlen);
324
325		cnt = atomicio(read, proxyfd, buf, 8);
326		if (cnt != 8)
327			err(1, "read failed (%zu/8)", cnt);
328		if (buf[1] != 90) {
329			errx(1, "connection failed, SOCKSv4 error: %s",
330			    socks4_strerror(buf[1]));
331		}
332	} else if (socksv == -1) {
333		/* HTTP proxy CONNECT */
334
335		/* Disallow bad chars in hostname */
336		if (strcspn(host, "\r\n\t []:") != strlen(host))
337			errx(1, "Invalid hostname");
338
339		/* Try to be sane about numeric IPv6 addresses */
340		if (strchr(host, ':') != NULL) {
341			r = snprintf(cbuf, sizeof(buf),
342			    "CONNECT [%s]:%d HTTP/1.0\r\n",
343			    host, ntohs(serverport));
344		} else {
345			r = snprintf(cbuf, sizeof(buf),
346			    "CONNECT %s:%d HTTP/1.0\r\n",
347			    host, ntohs(serverport));
348		}
349		if (r == -1 || (size_t)r >= sizeof(buf))
350			errx(1, "hostname too long");
351		r = strlen(cbuf);
352
353		cnt = atomicio(vwrite, proxyfd, buf, r);
354		if (cnt != (size_t)r)
355			err(1, "write failed (%zu/%d)", cnt, r);
356
357		if (authretry > 1) {
358			char resp[1024];
359
360			proxypass = getproxypass(proxyuser, proxyhost);
361			r = snprintf(cbuf, sizeof(buf), "%s:%s",
362			    proxyuser, proxypass);
363			if (r == -1 || (size_t)r >= sizeof(buf) ||
364			    b64_ntop(buf, strlen(cbuf), resp,
365			    sizeof(resp)) == -1)
366				errx(1, "Proxy username/password too long");
367			r = snprintf(cbuf, sizeof(buf), "Proxy-Authorization: "
368			    "Basic %s\r\n", resp);
369			if (r == -1 || (size_t)r >= sizeof(buf))
370				errx(1, "Proxy auth response too long");
371			r = strlen(cbuf);
372			if ((ssize_t)(cnt = atomicio(vwrite, proxyfd, buf, r)) != r)
373				err(1, "write failed (%zu/%d)", cnt, r);
374		}
375
376		/* Terminate headers */
377		if ((cnt = atomicio(vwrite, proxyfd, __UNCONST("\r\n"), 2)) != 2)
378			err(1, "write failed (%zu/2)", cnt);
379
380		/* Read status reply */
381		proxy_read_line(proxyfd, cbuf, sizeof(buf));
382		if (proxyuser != NULL &&
383		    strncmp(cbuf, "HTTP/1.0 407 ", 12) == 0) {
384			if (authretry > 1) {
385				fprintf(stderr, "Proxy authentication "
386				    "failed\n");
387			}
388			close(proxyfd);
389			goto again;
390		} else if (strncmp(cbuf, "HTTP/1.0 200 ", 12) != 0 &&
391		    strncmp(cbuf, "HTTP/1.1 200 ", 12) != 0)
392			errx(1, "Proxy error: \"%s\"", buf);
393
394		/* Headers continue until we hit an empty line */
395		for (r = 0; r < HTTP_MAXHDRS; r++) {
396			proxy_read_line(proxyfd, cbuf, sizeof(buf));
397			if (*buf == '\0')
398				break;
399		}
400		if (*buf != '\0')
401			errx(1, "Too many proxy headers received");
402	} else
403		errx(1, "Unknown proxy protocol %d", socksv);
404
405	return (proxyfd);
406}
407