Home | History | Annotate | Line # | Download | only in nc
      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 
     60 int	remote_connect(const char *, const char *, struct addrinfo);
     61 int	socks_connect(const char *, const char *, struct addrinfo,
     62 	    const char *, const char *, struct addrinfo, int,
     63 	    const char *);
     64 
     65 static int
     66 decode_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 
     94 static int
     95 proxy_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 
    116 static const char *
    117 getproxypass(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  */
    139 static const char *
    140 socks4_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  */
    159 static const char *
    160 socks5_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 
    186 int
    187 socks_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