[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

HTTP proxy auth for nc(1)



Hi,

This adds to netcat support for authenticating against HTTP CONNECT 
proxies (e.g. squid) using HTTP Basic proxy authentication. 

This allows you to use it as a ssh proxy command - e.g. by adding
the following to your .ssh/config:

Host outside
        ProxyCommand nc -x proxy.yourdomain:8080 -P username -X connect %h %p

Proxy passwords are read from the tty using readpassphrase().

Please test.

-d


Index: nc.1
===================================================================
RCS file: /cvs/src/usr.bin/nc/nc.1,v
retrieving revision 1.41
diff -u -p -r1.41 nc.1
--- nc.1	25 Oct 2005 03:51:06 -0000	1.41
+++ nc.1	26 Dec 2005 01:05:55 -0000
@@ -36,6 +36,7 @@
 .Bk -words
 .Op Fl 46DdhklnrStUuvz
 .Op Fl i Ar interval
+.Op Fl P Ar proxy_username
 .Op Fl p Ar source_port
 .Op Fl s Ar source_ip_address
 .Op Fl T Ar ToS
@@ -128,6 +129,10 @@ option are ignored.
 .It Fl n
 Do not do any DNS or service lookups on any specified addresses,
 hostnames or ports.
+.It Fl P Ar proxy_username
+Specifies a username to present to a proxy server that requires authentication.
+If no username is specified then authentication will not be attempted.
+Proxy authentication is only supported for HTTP CONNECT proxies at present.
 .It Fl p Ar source_port
 Specifies the source port
 .Nm
@@ -386,6 +391,12 @@ directive in
 for more information.
 .Pp
 .Dl $ nc -x10.2.3.4:8080 -Xconnect host.example.com 42
+.Pp
+The same example again, this time enabling proxy authentication with username
+.Dq ruser
+if the proxy requires it:
+.Pp
+.Dl $ nc -x10.2.3.4:8080 -Xconnect -Pruser host.example.com 42
 .Sh SEE ALSO
 .Xr cat 1 ,
 .Xr ssh 1
Index: netcat.c
===================================================================
RCS file: /cvs/src/usr.bin/nc/netcat.c,v
retrieving revision 1.84
diff -u -p -r1.84 netcat.c
--- netcat.c	25 Oct 2005 06:51:37 -0000	1.84
+++ netcat.c	26 Dec 2005 01:05:55 -0000
@@ -69,6 +69,7 @@ int	jflag;					/* use jumbo frames if we
 int	kflag;					/* More than one connect */
 int	lflag;					/* Bind to local port */
 int	nflag;					/* Don't do name look up */
+char   *Pflag;					/* Proxy username */
 char   *pflag;					/* Localport flag */
 int	rflag;					/* Random ports flag */
 char   *sflag;					/* Source Address */
@@ -91,8 +92,8 @@ void	help(void);
 int	local_listen(char *, char *, struct addrinfo);
 void	readwrite(int);
 int	remote_connect(const char *, const char *, struct addrinfo);
-int	socks_connect(const char *, const char *, struct addrinfo, const char *, const char *,
-	struct addrinfo, int);
+int	socks_connect(const char *, const char *, struct addrinfo,
+	    const char *, const char *, struct addrinfo, int, const char *);
 int	udptest(int);
 int	unix_connect(char *);
 int	unix_listen(char *);
@@ -122,7 +123,7 @@ main(int argc, char *argv[])
 	sv = NULL;
 
 	while ((ch = getopt(argc, argv,
-	    "46Ddhi:jklnp:rSs:tT:Uuvw:X:x:z")) != -1) {
+	    "46Ddhi:jklnP:p:rSs:tT:Uuvw:X:x:z")) != -1) {
 		switch (ch) {
 		case '4':
 			family = AF_INET;
@@ -166,6 +167,9 @@ main(int argc, char *argv[])
 		case 'n':
 			nflag = 1;
 			break;
+		case 'P':
+			Pflag = optarg;
+			break;
 		case 'p':
 			pflag = optarg;
 			break;
@@ -353,7 +357,8 @@ main(int argc, char *argv[])
 
 			if (xflag)
 				s = socks_connect(host, portlist[i], hints,
-				    proxyhost, proxyport, proxyhints, socksv);
+				    proxyhost, proxyport, proxyhints, socksv,
+				    Pflag);
 			else
 				s = remote_connect(host, portlist[i], hints);
 
@@ -817,6 +822,7 @@ help(void)
 	\t-k		Keep inbound sockets open for multiple connects\n\
 	\t-l		Listen mode, for inbound connects\n\
 	\t-n		Suppress name/port resolutions\n\
+	\t-P proxyuser\tUsername for proxy authentication\n\
 	\t-p port\t	Specify local port for remote connects\n\
 	\t-r		Randomize remote ports\n\
 	\t-S		Enable the TCP MD5 signature option\n\
Index: socks.c
===================================================================
RCS file: /cvs/src/usr.bin/nc/socks.c,v
retrieving revision 1.15
diff -u -p -r1.15 socks.c
--- socks.c	24 May 2005 20:13:28 -0000	1.15
+++ socks.c	26 Dec 2005 01:05:55 -0000
@@ -37,6 +37,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <resolv.h>
+#include <readpassphrase.h>
 #include "atomicio.h"
 
 #define SOCKS_PORT	"1080"
@@ -52,9 +54,9 @@
 #define SOCKS_IPV6	4
 
 int	remote_connect(const char *, const char *, struct addrinfo);
-int	socks_connect(const char *host, const char *port, struct addrinfo hints,
-	    const char *proxyhost, const char *proxyport, struct addrinfo proxyhints,
-	    int socksv);
+int	socks_connect(const char *, const char *, struct addrinfo,
+	    const char *, const char *, struct addrinfo, int,
+	    const char *);
 
 static int
 decode_addrport(const char *h, const char *p, struct sockaddr *addr,
@@ -107,13 +109,26 @@ proxy_read_line(int fd, char *buf, size_
 	return (off);
 }
 
+static const char *
+getproxypass(const char *proxyuser, const char *proxyhost)
+{
+	char prompt[512];
+	static char pw[256];
+
+	snprintf(prompt, sizeof(prompt), "Proxy password for %s_(_at_)_%s: ",
+	   proxyuser, proxyhost);
+	if (readpassphrase(prompt, pw, sizeof(pw), RPP_REQUIRE_TTY) == NULL)
+		errx(1, "Unable to read proxy passphrase");
+	return (pw);
+}
+
 int
 socks_connect(const char *host, const char *port,
     struct addrinfo hints __attribute__ ((__unused__)),
     const char *proxyhost, const char *proxyport, struct addrinfo proxyhints,
-    int socksv)
+    int socksv, const char *proxyuser)
 {
-	int proxyfd, r;
+	int proxyfd, r, authretry = 0;
 	size_t hlen, wlen;
 	unsigned char buf[1024];
 	size_t cnt;
@@ -121,21 +136,26 @@ socks_connect(const char *host, const ch
 	struct sockaddr_in *in4 = (struct sockaddr_in *)&addr;
 	struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&addr;
 	in_port_t serverport;
+	const char *proxypass = NULL;
 
 	if (proxyport == NULL)
 		proxyport = (socksv == -1) ? HTTP_PROXY_PORT : SOCKS_PORT;
 
-	proxyfd = remote_connect(proxyhost, proxyport, proxyhints);
-
-	if (proxyfd < 0)
-		return (-1);
-
 	/* Abuse API to lookup port */
 	if (decode_addrport("0.0.0.0", port, (struct sockaddr *)&addr,
 	    sizeof(addr), 1, 1) == -1)
 		errx(1, "unknown port \"%.64s\"", port);
 	serverport = in4->sin_port;
 
+ again:
+	if (authretry++ > 3)
+		errx(1, "Too many authentication failures");
+
+	proxyfd = remote_connect(proxyhost, proxyport, proxyhints);
+
+	if (proxyfd < 0)
+		return (-1);
+
 	if (socksv == 5) {
 		if (decode_addrport(host, port, (struct sockaddr *)&addr,
 		    sizeof(addr), 0, 1) == -1)
@@ -239,11 +259,11 @@ socks_connect(const char *host, const ch
 		/* Try to be sane about numeric IPv6 addresses */
 		if (strchr(host, ':') != NULL) {
 			r = snprintf(buf, sizeof(buf),
-			    "CONNECT [%s]:%d HTTP/1.0\r\n\r\n",
+			    "CONNECT [%s]:%d HTTP/1.0\r\n",
 			    host, ntohs(serverport));
 		} else {
 			r = snprintf(buf, sizeof(buf),
-			    "CONNECT %s:%d HTTP/1.0\r\n\r\n",
+			    "CONNECT %s:%d HTTP/1.0\r\n",
 			    host, ntohs(serverport));
 		}
 		if (r == -1 || (size_t)r >= sizeof(buf))
@@ -254,15 +274,50 @@ socks_connect(const char *host, const ch
 		if (cnt != r)
 			err(1, "write failed (%d/%d)", cnt, r);
 
-		/* Read reply */
+		if (authretry > 1) {
+			char resp[1024];
+
+			proxypass = getproxypass(proxyuser, proxyhost);
+			r = snprintf(buf, sizeof(buf), "%s:%s",
+			    proxyuser, proxypass);
+			if (r == -1 || (size_t)r >= sizeof(buf) ||
+			    b64_ntop(buf, strlen(buf), resp,
+			    sizeof(resp)) == -1)
+				errx(1, "Proxy username/password too long");
+			r = snprintf(buf, sizeof(buf), "Proxy-Authorization: "
+			    "Basic %s\r\n", resp);
+			if (r == -1 || (size_t)r >= sizeof(buf))
+				errx(1, "Proxy auth response too long");
+			r = strlen(buf);
+			if ((cnt = atomicio(vwrite, proxyfd, buf, r)) != r)
+				err(1, "write failed (%d/%d)", cnt, r);
+		}
+
+		/* Terminate headers */
+		if ((r = atomicio(vwrite, proxyfd, "\r\n", 2)) != 2)
+			err(1, "write failed (2/%d)", r);
+
+		/* Read status reply */
+		proxy_read_line(proxyfd, buf, sizeof(buf));
+		if (proxyuser != NULL &&
+		    strncmp(buf, "HTTP/1.0 407 ", 12) == 0) {
+			if (authretry > 1) {
+				fprintf(stderr, "Proxy authentication "
+				    "failed\n");
+			}
+			close(proxyfd);
+			goto again;
+		} else if (strncmp(buf, "HTTP/1.0 200 ", 12) != 0)
+			errx(1, "Proxy error: \"%s\"", buf);
+
+		/* Headers continue until we hit an empty line */
 		for (r = 0; r < HTTP_MAXHDRS; r++) {
 			proxy_read_line(proxyfd, buf, sizeof(buf));
-			if (r == 0 && strncmp(buf, "HTTP/1.0 200 ", 12) != 0)
-				errx(1, "Proxy error: \"%s\"", buf);
-			/* Discard headers until we hit an empty line */
 			if (*buf == '\0')
 				break;
 		}
+		if (*buf != '\0')
+			errx(1, "Too many proxy headers received");
 	} else
 		errx(1, "Unknown proxy protocol %d", socksv);



Visit your host, monkey.org