GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/nc/socks.c Lines: 0 188 0.0 %
Date: 2017-11-07 Branches: 0 120 0.0 %

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