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