summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin LI <delphij@FreeBSD.org>2016-04-24 22:12:47 +0000
committerXin LI <delphij@FreeBSD.org>2016-04-24 22:12:47 +0000
commit09ac48e1c32eeedc204f8543f59cf29a7459298b (patch)
treeece78a3a8bdb725e9c7e47f96ddd59e9e66a230f
parent70061f65353c351c2a0712f76d3e29a0164ce1dc (diff)
Notes
-rw-r--r--Makefile4
-rw-r--r--nc.175
-rw-r--r--netcat.c529
-rw-r--r--socks.c66
4 files changed, 525 insertions, 149 deletions
diff --git a/Makefile b/Makefile
index 150f8295bde0..5f20c40cf75c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,8 @@
-# $OpenBSD: Makefile,v 1.6 2001/09/02 18:45:41 jakob Exp $
+# $OpenBSD: Makefile,v 1.7 2015/09/11 21:07:01 beck Exp $
PROG= nc
SRCS= netcat.c atomicio.c socks.c
+LDADD+= -ltls -lssl -lcrypto
+DPADD+= ${LIBTLS} ${LIBSSL} ${LIBCRYPTO}
.include <bsd.prog.mk>
diff --git a/nc.1 b/nc.1
index d57a02e28767..537d6deb2dca 100644
--- a/nc.1
+++ b/nc.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: nc.1,v 1.68 2015/03/26 10:35:04 tobias Exp $
+.\" $OpenBSD: nc.1,v 1.71 2015/09/25 14:56:33 schwarze Exp $
.\"
.\" Copyright (c) 1996 David Sacerdote
.\" All rights reserved.
@@ -25,7 +25,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd $Mdocdate: February 26 2014 $
+.Dd $Mdocdate: September 12 2015 $
.Dt NC 1
.Os
.Sh NAME
@@ -33,25 +33,25 @@
.Nd arbitrary TCP and UDP connections and listens
.Sh SYNOPSIS
.Nm nc
-.Bk -words
-.Op Fl 46DdFhklNnrStUuvz
+.Op Fl 46cDdFhklNnrStUuvz
+.Op Fl C Ar certfile
+.Op Fl e Ar name
+.Op Fl H Ar hash
.Op Fl I Ar length
.Op Fl i Ar interval
+.Op Fl K Ar keyfile
.Op Fl O Ar length
.Op Fl P Ar proxy_username
.Op Fl p Ar source_port
+.Op Fl R Ar CAfile
.Op Fl s Ar source
-.Op Fl T Ar toskeyword
+.Op Fl T Ar keyword
.Op Fl V Ar rtable
.Op Fl w Ar timeout
.Op Fl X Ar proxy_protocol
-.Oo Xo
-.Fl x Ar proxy_address Ns Oo : Ns
-.Ar port Oc
-.Xc Oc
+.Op Fl x Ar proxy_address Ns Op : Ns Ar port
.Op Ar destination
.Op Ar port
-.Ek
.Sh DESCRIPTION
The
.Nm
@@ -98,10 +98,20 @@ to use IPv4 addresses only.
Forces
.Nm
to use IPv6 addresses only.
+.It Fl C Ar certfile
+Specifies the filename from which the public key part of the TLS
+certificate is loaded, in PEM format.
+May only be used with TLS.
+.It Fl c
+If using a TCP socket to connect or listen, use TLS.
+Illegal if not using TCP sockets.
.It Fl D
Enable debugging on the socket.
.It Fl d
Do not attempt to read from stdin.
+.It Fl e Ar name
+Specify the name that must be present in the peer certificate when using TLS.
+Illegal if not using TLS.
.It Fl F
Pass the first connected socket using
.Xr sendmsg 2
@@ -117,6 +127,11 @@ using the
.Xr ssh_config 5
.Cm ProxyUseFdpass
option).
+.It Fl H Ar hash
+Specifies the required hash string of the peer certificate when using TLS.
+The string format required is that used by
+.Xr tls_peer_cert_hash 3 .
+Illegal if not using TLS, and may not be used with -T noverify.
.It Fl h
Prints out
.Nm
@@ -126,6 +141,10 @@ Specifies the size of the TCP receive buffer.
.It Fl i Ar interval
Specifies a delay time interval between lines of text sent and received.
Also causes a delay time between connections to multiple ports.
+.It Fl K Ar keyfile
+Specifies the filename from which the private key
+is loaded in PEM format.
+May only be used with TLS.
.It Fl k
Forces
.Nm
@@ -172,6 +191,12 @@ should use, subject to privilege restrictions and availability.
It is an error to use this option in conjunction with the
.Fl l
option.
+.It Fl R Ar CAfile
+Specifies the filename from which the root CA bundle for certificate
+verification is loaded, in PEM format.
+Illegal if not using TLS.
+The default is
+.Pa /etc/ssl/cert.pem .
.It Fl r
Specifies that source and/or destination ports should be chosen randomly
instead of sequentially within a range or in the order that the system
@@ -187,9 +212,23 @@ to create and use so that datagrams can be received.
It is an error to use this option in conjunction with the
.Fl l
option.
-.It Fl T Ar toskeyword
-Change IPv4 TOS value.
-.Ar toskeyword
+.It Fl T Ar keyword
+Change IPv4 TOS value or TLS options.
+For TLS options
+.Ar keyword
+may be one of
+.Ar tlslegacy ,
+which allows legacy TLS protocols;
+.Ar noverify ,
+which disables certificate verification;
+.Ar noname ,
+which disables certificate name checking; or
+.Ar clientcert ,
+which requires a client certificate on incoming connections.
+It is illegal to specify TLS options if not using TLS.
+.Pp
+For IPv4 TOS value
+.Ar keyword
may be one of
.Ar critical ,
.Ar inetcontrol ,
@@ -258,10 +297,7 @@ and
.Dq connect
(HTTPS proxy).
If the protocol is not specified, SOCKS version 5 is used.
-.It Xo
-.Fl x Ar proxy_address Ns Oo : Ns
-.Ar port Oc
-.Xc
+.It Fl x Ar proxy_address Ns Op : Ns Ar port
Requests that
.Nm
should connect to
@@ -429,6 +465,11 @@ the source port, with a timeout of 5 seconds:
.Pp
.Dl $ nc -p 31337 -w 5 host.example.com 42
.Pp
+Open a TCP connection to port 443 of www.google.ca, and negotiate TLS.
+Check for a different name in the certificate for validation.
+.Pp
+.Dl $ nc -v -c -e adsf.au.doubleclick.net www.google.ca 443
+.Pp
Open a UDP connection to port 53 of host.example.com:
.Pp
.Dl $ nc -u host.example.com 53
diff --git a/netcat.c b/netcat.c
index 6358539418d5..8bbd183e6229 100644
--- a/netcat.c
+++ b/netcat.c
@@ -1,6 +1,7 @@
-/* $OpenBSD: netcat.c,v 1.130 2015/07/26 19:12:28 chl Exp $ */
+/* $OpenBSD: netcat.c,v 1.150 2016/01/04 02:18:31 bcook Exp $ */
/*
* Copyright (c) 2001 Eric Jackson <ericj@monkey.org>
+ * Copyright (c) 2015 Bob Beck. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,7 +34,6 @@
#include <sys/types.h>
#include <sys/socket.h>
-#include <sys/time.h>
#include <sys/uio.h>
#include <sys/un.h>
@@ -44,7 +44,6 @@
#include <err.h>
#include <errno.h>
-#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <poll.h>
@@ -53,16 +52,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include <unistd.h>
+#include <tls.h>
#include "atomicio.h"
-#ifndef SUN_LEN
-#define SUN_LEN(su) \
- (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
-#endif
-
#define PORT_MAX 65535
-#define PORT_MAX_LEN 6
#define UNIX_DG_TMP_SOCKET_SIZE 19
#define POLL_STDIN 0
@@ -70,6 +65,12 @@
#define POLL_NETIN 2
#define POLL_STDOUT 3
#define BUFSIZE 16384
+#define DEFAULT_CA_FILE "/etc/ssl/cert.pem"
+
+#define TLS_LEGACY (1 << 1)
+#define TLS_NOVERIFY (1 << 2)
+#define TLS_NONAME (1 << 3)
+#define TLS_CCERT (1 << 4)
/* Command Line Options */
int dflag; /* detached, no stdin */
@@ -95,6 +96,21 @@ int Sflag; /* TCP MD5 signature option */
int Tflag = -1; /* IP Type of Service */
int rtableid = -1;
+int usetls; /* use TLS */
+char *Cflag; /* Public cert file */
+char *Kflag; /* Private key file */
+char *Rflag = DEFAULT_CA_FILE; /* Root CA file */
+int tls_cachanged; /* Using non-default CA file */
+int TLSopt; /* TLS options */
+char *tls_expectname; /* required name in peer cert */
+char *tls_expecthash; /* required hash of peer cert */
+uint8_t *cacert;
+size_t cacertlen;
+uint8_t *privkey;
+size_t privkeylen;
+uint8_t *pubcert;
+size_t pubcertlen;
+
int timeout = -1;
int family = AF_UNSPEC;
char *portlist[PORT_MAX+1];
@@ -104,22 +120,26 @@ void atelnet(int, unsigned char *, unsigned int);
void build_ports(char *);
void help(void);
int local_listen(char *, char *, struct addrinfo);
-void readwrite(int);
+void readwrite(int, struct tls *);
void fdpass(int nfd) __attribute__((noreturn));
int remote_connect(const char *, const char *, struct addrinfo);
int timeout_connect(int, const struct sockaddr *, socklen_t);
int socks_connect(const char *, const char *, struct addrinfo,
const char *, const char *, struct addrinfo, int, const char *);
int udptest(int);
-int unix_bind(char *);
+int unix_bind(char *, int);
int unix_connect(char *);
int unix_listen(char *);
void set_common_sockopts(int, int);
int map_tos(char *, int *);
+int map_tls(char *, int *);
void report_connect(const struct sockaddr *, socklen_t);
+void report_tls(struct tls *tls_ctx, char * host, char *tls_expectname);
void usage(int);
-ssize_t drainbuf(int, unsigned char *, size_t *);
-ssize_t fillbuf(int, unsigned char *, size_t *);
+ssize_t drainbuf(int, unsigned char *, size_t *, struct tls *);
+ssize_t fillbuf(int, unsigned char *, size_t *, struct tls *);
+void tls_setup_client(struct tls *, int, char *);
+struct tls *tls_setup_server(struct tls *, int, char *);
int
main(int argc, char *argv[])
@@ -134,6 +154,8 @@ main(int argc, char *argv[])
const char *errstr, *proxyhost = "", *proxyport = NULL;
struct addrinfo proxyhints;
char unix_dg_tmp_socket_buf[UNIX_DG_TMP_SOCKET_SIZE];
+ struct tls_config *tls_cfg = NULL;
+ struct tls *tls_ctx = NULL;
ret = 1;
s = 0;
@@ -145,7 +167,7 @@ main(int argc, char *argv[])
signal(SIGPIPE, SIG_IGN);
while ((ch = getopt(argc, argv,
- "46DdFhI:i:klNnO:P:p:rSs:tT:UuV:vw:X:x:z")) != -1) {
+ "46C:cDde:FH:hI:i:K:klNnO:P:p:R:rSs:T:tUuV:vw:X:x:z")) != -1) {
switch (ch) {
case '4':
family = AF_INET;
@@ -166,12 +188,24 @@ main(int argc, char *argv[])
else
errx(1, "unsupported proxy protocol");
break;
+ case 'C':
+ Cflag = optarg;
+ break;
+ case 'c':
+ usetls = 1;
+ break;
case 'd':
dflag = 1;
break;
+ case 'e':
+ tls_expectname = optarg;
+ break;
case 'F':
Fflag = 1;
break;
+ case 'H':
+ tls_expecthash = optarg;
+ break;
case 'h':
help();
break;
@@ -180,6 +214,9 @@ main(int argc, char *argv[])
if (errstr)
errx(1, "interval %s: %s", errstr, optarg);
break;
+ case 'K':
+ Kflag = optarg;
+ break;
case 'k':
kflag = 1;
break;
@@ -198,6 +235,10 @@ main(int argc, char *argv[])
case 'p':
pflag = optarg;
break;
+ case 'R':
+ tls_cachanged = 1;
+ Rflag = optarg;
+ break;
case 'r':
rflag = 1;
break;
@@ -256,6 +297,8 @@ main(int argc, char *argv[])
errno = 0;
if (map_tos(optarg, &Tflag))
break;
+ if (map_tls(optarg, &TLSopt))
+ break;
if (strlen(optarg) > 1 && optarg[0] == '0' &&
optarg[1] == 'x')
Tflag = (int)strtol(optarg, NULL, 16);
@@ -263,7 +306,7 @@ main(int argc, char *argv[])
Tflag = (int)strtonum(optarg, 0, 255,
&errstr);
if (Tflag < 0 || Tflag > 255 || errstr || errno)
- errx(1, "illegal tos value %s", optarg);
+ errx(1, "illegal tos/tls value %s", optarg);
break;
default:
usage(1);
@@ -272,6 +315,22 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
+ if (rtableid >= 0)
+ if (setrtable(rtableid) == -1)
+ err(1, "setrtable");
+
+ if (family == AF_UNIX) {
+ if (pledge("stdio rpath wpath cpath tmppath unix", NULL) == -1)
+ err(1, "pledge");
+ } else if (Fflag) {
+ if (pledge("stdio inet dns sendfd", NULL) == -1)
+ err(1, "pledge");
+ } else if (usetls) {
+ if (pledge("stdio rpath inet dns", NULL) == -1)
+ err(1, "pledge");
+ } else if (pledge("stdio inet dns", NULL) == -1)
+ err(1, "pledge");
+
/* Cruft to make sure options are clean, and used properly. */
if (argv[0] && !argv[1] && family == AF_UNIX) {
host = argv[0];
@@ -295,6 +354,26 @@ main(int argc, char *argv[])
errx(1, "cannot use -z and -l");
if (!lflag && kflag)
errx(1, "must use -l with -k");
+ if (uflag && usetls)
+ errx(1, "cannot use -c and -u");
+ if ((family == AF_UNIX) && usetls)
+ errx(1, "cannot use -c and -U");
+ if ((family == AF_UNIX) && Fflag)
+ errx(1, "cannot use -F and -U");
+ if (Fflag && usetls)
+ errx(1, "cannot use -c and -F");
+ if (TLSopt && !usetls)
+ errx(1, "you must specify -c to use TLS options");
+ if (Cflag && !usetls)
+ errx(1, "you must specify -c to use -C");
+ if (Kflag && !usetls)
+ errx(1, "you must specify -c to use -K");
+ if (tls_cachanged && !usetls)
+ errx(1, "you must specify -c to use -R");
+ if (tls_expecthash && !usetls)
+ errx(1, "you must specify -c to use -H");
+ if (tls_expectname && !usetls)
+ errx(1, "you must specify -c to use -e");
/* Get name of temporary socket for unix datagram client */
if ((family == AF_UNIX) && uflag && !lflag) {
@@ -302,7 +381,7 @@ main(int argc, char *argv[])
unix_dg_tmp_socket = sflag;
} else {
strlcpy(unix_dg_tmp_socket_buf, "/tmp/nc.XXXXXXXXXX",
- UNIX_DG_TMP_SOCKET_SIZE);
+ UNIX_DG_TMP_SOCKET_SIZE);
if (mktemp(unix_dg_tmp_socket_buf) == NULL)
err(1, "mktemp");
unix_dg_tmp_socket = unix_dg_tmp_socket_buf;
@@ -347,17 +426,62 @@ main(int argc, char *argv[])
proxyhints.ai_flags |= AI_NUMERICHOST;
}
+ if (usetls) {
+ if (Rflag && (cacert = tls_load_file(Rflag, &cacertlen, NULL)) == NULL)
+ errx(1, "unable to load root CA file %s", Rflag);
+ if (Cflag && (pubcert = tls_load_file(Cflag, &pubcertlen, NULL)) == NULL)
+ errx(1, "unable to load TLS certificate file %s", Cflag);
+ if (Kflag && (privkey = tls_load_file(Kflag, &privkeylen, NULL)) == NULL)
+ errx(1, "unable to load TLS key file %s", Kflag);
+
+ if (pledge("stdio inet dns", NULL) == -1)
+ err(1, "pledge");
+
+ if (tls_init() == -1)
+ errx(1, "unable to initialize TLS");
+ if ((tls_cfg = tls_config_new()) == NULL)
+ errx(1, "unable to allocate TLS config");
+ if (Rflag && tls_config_set_ca_mem(tls_cfg, cacert, cacertlen) == -1)
+ errx(1, "unable to set root CA file %s", Rflag);
+ if (Cflag && tls_config_set_cert_mem(tls_cfg, pubcert, pubcertlen) == -1)
+ errx(1, "unable to set TLS certificate file %s", Cflag);
+ if (Kflag && tls_config_set_key_mem(tls_cfg, privkey, privkeylen) == -1)
+ errx(1, "unable to set TLS key file %s", Kflag);
+ if (TLSopt & TLS_LEGACY) {
+ tls_config_set_protocols(tls_cfg, TLS_PROTOCOLS_ALL);
+ tls_config_set_ciphers(tls_cfg, "legacy");
+ }
+ if (!lflag && (TLSopt & TLS_CCERT))
+ errx(1, "clientcert is only valid with -l");
+ if (TLSopt & TLS_NONAME)
+ tls_config_insecure_noverifyname(tls_cfg);
+ if (TLSopt & TLS_NOVERIFY) {
+ if (tls_expecthash != NULL)
+ errx(1, "-H and -T noverify may not be used"
+ "together");
+ tls_config_insecure_noverifycert(tls_cfg);
+ }
+ }
if (lflag) {
+ struct tls *tls_cctx = NULL;
int connfd;
ret = 0;
if (family == AF_UNIX) {
if (uflag)
- s = unix_bind(host);
+ s = unix_bind(host, 0);
else
s = unix_listen(host);
}
+ if (usetls) {
+ tls_config_verify_client_optional(tls_cfg);
+ if ((tls_ctx = tls_server()) == NULL)
+ errx(1, "tls server creation failed");
+ if (tls_configure(tls_ctx, tls_cfg) == -1)
+ errx(1, "tls configuration failed (%s)",
+ tls_error(tls_ctx));
+ }
/* Allow only one connection at a time, but stay alive. */
for (;;) {
if (family != AF_UNIX)
@@ -369,7 +493,7 @@ main(int argc, char *argv[])
* receive datagrams from multiple socket pairs.
*/
if (uflag && kflag)
- readwrite(s);
+ readwrite(s, NULL);
/*
* For UDP and not -k, we will use recvfrom() initially
* to wait for a caller, then use the regular functions
@@ -394,22 +518,34 @@ main(int argc, char *argv[])
if (vflag)
report_connect((struct sockaddr *)&z, len);
- readwrite(s);
+ readwrite(s, NULL);
} else {
len = sizeof(cliaddr);
- connfd = accept(s, (struct sockaddr *)&cliaddr,
- &len);
+ connfd = accept4(s, (struct sockaddr *)&cliaddr,
+ &len, SOCK_NONBLOCK);
if (connfd == -1) {
/* For now, all errnos are fatal */
err(1, "accept");
}
if (vflag)
report_connect((struct sockaddr *)&cliaddr, len);
-
- readwrite(connfd);
+ if ((usetls) &&
+ (tls_cctx = tls_setup_server(tls_ctx, connfd, host)))
+ readwrite(connfd, tls_cctx);
+ if (!usetls)
+ readwrite(connfd, NULL);
+ if (tls_cctx) {
+ int i;
+
+ do {
+ i = tls_close(tls_cctx);
+ } while (i == TLS_WANT_POLLIN ||
+ i == TLS_WANT_POLLOUT);
+ tls_free(tls_cctx);
+ tls_cctx = NULL;
+ }
close(connfd);
}
-
if (family != AF_UNIX)
close(s);
else if (uflag) {
@@ -424,7 +560,7 @@ main(int argc, char *argv[])
ret = 0;
if ((s = unix_connect(host)) > 0 && !zflag) {
- readwrite(s);
+ readwrite(s, NULL);
close(s);
} else
ret = 1;
@@ -444,6 +580,13 @@ main(int argc, char *argv[])
if (s)
close(s);
+ if (usetls) {
+ if ((tls_ctx = tls_client()) == NULL)
+ errx(1, "tls client creation failed");
+ if (tls_configure(tls_ctx, tls_cfg) == -1)
+ errx(1, "tls configuration failed (%s)",
+ tls_error(tls_ctx));
+ }
if (xflag)
s = socks_connect(host, portlist[i], hints,
proxyhost, proxyport, proxyhints, socksv,
@@ -481,14 +624,30 @@ main(int argc, char *argv[])
}
if (Fflag)
fdpass(s);
- else if (!zflag)
- readwrite(s);
+ else {
+ if (usetls)
+ tls_setup_client(tls_ctx, s, host);
+ if (!zflag)
+ readwrite(s, tls_ctx);
+ if (tls_ctx) {
+ int j;
+
+ do {
+ j = tls_close(tls_ctx);
+ } while (j == TLS_WANT_POLLIN ||
+ j == TLS_WANT_POLLOUT);
+ tls_free(tls_ctx);
+ tls_ctx = NULL;
+ }
+ }
}
}
if (s)
close(s);
+ tls_config_free(tls_cfg);
+
exit(ret);
}
@@ -497,33 +656,95 @@ main(int argc, char *argv[])
* Returns a unix socket bound to the given path
*/
int
-unix_bind(char *path)
+unix_bind(char *path, int flags)
{
- struct sockaddr_un sun;
+ struct sockaddr_un s_un;
int s;
/* Create unix domain socket. */
- if ((s = socket(AF_UNIX, uflag ? SOCK_DGRAM : SOCK_STREAM,
- 0)) < 0)
+ if ((s = socket(AF_UNIX, flags | (uflag ? SOCK_DGRAM : SOCK_STREAM),
+ 0)) < 0)
return (-1);
- memset(&sun, 0, sizeof(struct sockaddr_un));
- sun.sun_family = AF_UNIX;
+ memset(&s_un, 0, sizeof(struct sockaddr_un));
+ s_un.sun_family = AF_UNIX;
- if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
- sizeof(sun.sun_path)) {
+ if (strlcpy(s_un.sun_path, path, sizeof(s_un.sun_path)) >=
+ sizeof(s_un.sun_path)) {
close(s);
errno = ENAMETOOLONG;
return (-1);
}
- if (bind(s, (struct sockaddr *)&sun, SUN_LEN(&sun)) < 0) {
+ if (bind(s, (struct sockaddr *)&s_un, sizeof(s_un)) < 0) {
close(s);
return (-1);
}
return (s);
}
+void
+tls_setup_client(struct tls *tls_ctx, int s, char *host)
+{
+ int i;
+
+ if (tls_connect_socket(tls_ctx, s,
+ tls_expectname ? tls_expectname : host) == -1) {
+ errx(1, "tls connection failed (%s)",
+ tls_error(tls_ctx));
+ }
+ do {
+ if ((i = tls_handshake(tls_ctx)) == -1)
+ errx(1, "tls handshake failed (%s)",
+ tls_error(tls_ctx));
+ } while (i == TLS_WANT_POLLIN || i == TLS_WANT_POLLOUT);
+ if (vflag)
+ report_tls(tls_ctx, host, tls_expectname);
+ if (tls_expecthash && tls_peer_cert_hash(tls_ctx) &&
+ strcmp(tls_expecthash, tls_peer_cert_hash(tls_ctx)) != 0)
+ errx(1, "peer certificate is not %s", tls_expecthash);
+}
+
+struct tls *
+tls_setup_server(struct tls *tls_ctx, int connfd, char *host)
+{
+ struct tls *tls_cctx;
+
+ if (tls_accept_socket(tls_ctx, &tls_cctx,
+ connfd) == -1) {
+ warnx("tls accept failed (%s)",
+ tls_error(tls_ctx));
+ tls_cctx = NULL;
+ } else {
+ int i;
+
+ do {
+ if ((i = tls_handshake(tls_cctx)) == -1)
+ warnx("tls handshake failed (%s)",
+ tls_error(tls_cctx));
+ } while(i == TLS_WANT_POLLIN || i == TLS_WANT_POLLOUT);
+ }
+ if (tls_cctx) {
+ int gotcert = tls_peer_cert_provided(tls_cctx);
+
+ if (vflag && gotcert)
+ report_tls(tls_cctx, host, tls_expectname);
+ if ((TLSopt & TLS_CCERT) && !gotcert)
+ warnx("No client certificate provided");
+ else if (gotcert && tls_peer_cert_hash(tls_ctx) && tls_expecthash &&
+ strcmp(tls_expecthash, tls_peer_cert_hash(tls_ctx)) != 0)
+ warnx("peer certificate is not %s", tls_expecthash);
+ else if (gotcert && tls_expectname &&
+ (!tls_peer_cert_contains_name(tls_cctx, tls_expectname)))
+ warnx("name (%s) not found in client cert",
+ tls_expectname);
+ else {
+ return tls_cctx;
+ }
+ }
+ return NULL;
+}
+
/*
* unix_connect()
* Returns a socket connected to a local unix socket. Returns -1 on failure.
@@ -531,28 +752,27 @@ unix_bind(char *path)
int
unix_connect(char *path)
{
- struct sockaddr_un sun;
+ struct sockaddr_un s_un;
int s;
if (uflag) {
- if ((s = unix_bind(unix_dg_tmp_socket)) < 0)
+ if ((s = unix_bind(unix_dg_tmp_socket, SOCK_CLOEXEC)) < 0)
return (-1);
} else {
- if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ if ((s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) < 0)
return (-1);
}
- (void)fcntl(s, F_SETFD, FD_CLOEXEC);
- memset(&sun, 0, sizeof(struct sockaddr_un));
- sun.sun_family = AF_UNIX;
+ memset(&s_un, 0, sizeof(struct sockaddr_un));
+ s_un.sun_family = AF_UNIX;
- if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
- sizeof(sun.sun_path)) {
+ if (strlcpy(s_un.sun_path, path, sizeof(s_un.sun_path)) >=
+ sizeof(s_un.sun_path)) {
close(s);
errno = ENAMETOOLONG;
return (-1);
}
- if (connect(s, (struct sockaddr *)&sun, SUN_LEN(&sun)) < 0) {
+ if (connect(s, (struct sockaddr *)&s_un, sizeof(s_un)) < 0) {
close(s);
return (-1);
}
@@ -568,7 +788,7 @@ int
unix_listen(char *path)
{
int s;
- if ((s = unix_bind(path)) < 0)
+ if ((s = unix_bind(path, 0)) < 0)
return (-1);
if (listen(s, 5) < 0) {
@@ -594,14 +814,10 @@ remote_connect(const char *host, const char *port, struct addrinfo hints)
res0 = res;
do {
- if ((s = socket(res0->ai_family, res0->ai_socktype,
- res0->ai_protocol)) < 0)
+ if ((s = socket(res0->ai_family, res0->ai_socktype |
+ SOCK_NONBLOCK, res0->ai_protocol)) < 0)
continue;
- if (rtableid >= 0 && (setsockopt(s, SOL_SOCKET, SO_RTABLE,
- &rtableid, sizeof(rtableid)) == -1))
- err(1, "setsockopt SO_RTABLE");
-
/* Bind to a local port or source address if specified. */
if (sflag || pflag) {
struct addrinfo ahints, *ares;
@@ -626,7 +842,7 @@ remote_connect(const char *host, const char *port, struct addrinfo hints)
if (timeout_connect(s, res0->ai_addr, res0->ai_addrlen) == 0)
break;
- else if (vflag)
+ if (vflag)
warn("connect to %s port %s (%s) failed", host, port,
uflag ? "udp" : "tcp");
@@ -644,15 +860,9 @@ timeout_connect(int s, const struct sockaddr *name, socklen_t namelen)
{
struct pollfd pfd;
socklen_t optlen;
- int flags, optval;
+ int optval;
int ret;
- if (timeout != -1) {
- flags = fcntl(s, F_GETFL, 0);
- if (fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1)
- err(1, "set non-blocking mode");
- }
-
if ((ret = connect(s, name, namelen)) != 0 && errno == EINPROGRESS) {
pfd.fd = s;
pfd.events = POLLOUT;
@@ -670,9 +880,6 @@ timeout_connect(int s, const struct sockaddr *name, socklen_t namelen)
err(1, "poll failed");
}
- if (timeout != -1 && fcntl(s, F_SETFL, flags) == -1)
- err(1, "restoring flags");
-
return (ret);
}
@@ -707,10 +914,6 @@ local_listen(char *host, char *port, struct addrinfo hints)
res0->ai_protocol)) < 0)
continue;
- if (rtableid >= 0 && (setsockopt(s, SOL_SOCKET, SO_RTABLE,
- &rtableid, sizeof(rtableid)) == -1))
- err(1, "setsockopt SO_RTABLE");
-
ret = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &x, sizeof(x));
if (ret == -1)
err(1, NULL);
@@ -740,7 +943,7 @@ local_listen(char *host, char *port, struct addrinfo hints)
* Loop that polls on the network file descriptor and stdin.
*/
void
-readwrite(int net_fd)
+readwrite(int net_fd, struct tls *tls_ctx)
{
struct pollfd pfd[4];
int stdin_fd = STDIN_FILENO;
@@ -774,8 +977,8 @@ readwrite(int net_fd)
while (1) {
/* both inputs are gone, buffers are empty, we are done */
- if (pfd[POLL_STDIN].fd == -1 && pfd[POLL_NETIN].fd == -1
- && stdinbufpos == 0 && netinbufpos == 0) {
+ if (pfd[POLL_STDIN].fd == -1 && pfd[POLL_NETIN].fd == -1 &&
+ stdinbufpos == 0 && netinbufpos == 0) {
close(net_fd);
return;
}
@@ -785,8 +988,8 @@ readwrite(int net_fd)
return;
}
/* listen and net in gone, queues empty, done */
- if (lflag && pfd[POLL_NETIN].fd == -1
- && stdinbufpos == 0 && netinbufpos == 0) {
+ if (lflag && pfd[POLL_NETIN].fd == -1 &&
+ stdinbufpos == 0 && netinbufpos == 0) {
close(net_fd);
return;
}
@@ -819,13 +1022,13 @@ readwrite(int net_fd)
/* reading is possible after HUP */
if (pfd[POLL_STDIN].events & POLLIN &&
pfd[POLL_STDIN].revents & POLLHUP &&
- ! (pfd[POLL_STDIN].revents & POLLIN))
- pfd[POLL_STDIN].fd = -1;
+ !(pfd[POLL_STDIN].revents & POLLIN))
+ pfd[POLL_STDIN].fd = -1;
if (pfd[POLL_NETIN].events & POLLIN &&
pfd[POLL_NETIN].revents & POLLHUP &&
- ! (pfd[POLL_NETIN].revents & POLLIN))
- pfd[POLL_NETIN].fd = -1;
+ !(pfd[POLL_NETIN].revents & POLLIN))
+ pfd[POLL_NETIN].fd = -1;
if (pfd[POLL_NETOUT].revents & POLLHUP) {
if (Nflag)
@@ -848,9 +1051,12 @@ readwrite(int net_fd)
/* try to read from stdin */
if (pfd[POLL_STDIN].revents & POLLIN && stdinbufpos < BUFSIZE) {
ret = fillbuf(pfd[POLL_STDIN].fd, stdinbuf,
- &stdinbufpos);
- /* error or eof on stdin - remove from pfd */
- if (ret == 0 || ret == -1)
+ &stdinbufpos, NULL);
+ if (ret == TLS_WANT_POLLIN)
+ pfd[POLL_STDIN].events = POLLIN;
+ else if (ret == TLS_WANT_POLLOUT)
+ pfd[POLL_STDIN].events = POLLOUT;
+ else if (ret == 0 || ret == -1)
pfd[POLL_STDIN].fd = -1;
/* read something - poll net out */
if (stdinbufpos > 0)
@@ -862,8 +1068,12 @@ readwrite(int net_fd)
/* try to write to network */
if (pfd[POLL_NETOUT].revents & POLLOUT && stdinbufpos > 0) {
ret = drainbuf(pfd[POLL_NETOUT].fd, stdinbuf,
- &stdinbufpos);
- if (ret == -1)
+ &stdinbufpos, tls_ctx);
+ if (ret == TLS_WANT_POLLIN)
+ pfd[POLL_NETOUT].events = POLLIN;
+ else if (ret == TLS_WANT_POLLOUT)
+ pfd[POLL_NETOUT].events = POLLOUT;
+ else if (ret == -1)
pfd[POLL_NETOUT].fd = -1;
/* buffer empty - remove self from polling */
if (stdinbufpos == 0)
@@ -875,8 +1085,12 @@ readwrite(int net_fd)
/* try to read from network */
if (pfd[POLL_NETIN].revents & POLLIN && netinbufpos < BUFSIZE) {
ret = fillbuf(pfd[POLL_NETIN].fd, netinbuf,
- &netinbufpos);
- if (ret == -1)
+ &netinbufpos, tls_ctx);
+ if (ret == TLS_WANT_POLLIN)
+ pfd[POLL_NETIN].events = POLLIN;
+ else if (ret == TLS_WANT_POLLOUT)
+ pfd[POLL_NETIN].events = POLLOUT;
+ else if (ret == -1)
pfd[POLL_NETIN].fd = -1;
/* eof on net in - remove from pfd */
if (ret == 0) {
@@ -897,8 +1111,12 @@ readwrite(int net_fd)
/* try to write to stdout */
if (pfd[POLL_STDOUT].revents & POLLOUT && netinbufpos > 0) {
ret = drainbuf(pfd[POLL_STDOUT].fd, netinbuf,
- &netinbufpos);
- if (ret == -1)
+ &netinbufpos, NULL);
+ if (ret == TLS_WANT_POLLIN)
+ pfd[POLL_STDOUT].events = POLLIN;
+ else if (ret == TLS_WANT_POLLOUT)
+ pfd[POLL_STDOUT].events = POLLOUT;
+ else if (ret == -1)
pfd[POLL_STDOUT].fd = -1;
/* buffer empty - remove self from polling */
if (netinbufpos == 0)
@@ -922,15 +1140,19 @@ readwrite(int net_fd)
}
ssize_t
-drainbuf(int fd, unsigned char *buf, size_t *bufpos)
+drainbuf(int fd, unsigned char *buf, size_t *bufpos, struct tls *tls)
{
ssize_t n;
ssize_t adjust;
- n = write(fd, buf, *bufpos);
- /* don't treat EAGAIN, EINTR as error */
- if (n == -1 && (errno == EAGAIN || errno == EINTR))
- n = -2;
+ if (tls)
+ n = tls_write(tls, buf, *bufpos);
+ else {
+ n = write(fd, buf, *bufpos);
+ /* don't treat EAGAIN, EINTR as error */
+ if (n == -1 && (errno == EAGAIN || errno == EINTR))
+ n = TLS_WANT_POLLOUT;
+ }
if (n <= 0)
return n;
/* adjust buffer */
@@ -941,17 +1163,20 @@ drainbuf(int fd, unsigned char *buf, size_t *bufpos)
return n;
}
-
ssize_t
-fillbuf(int fd, unsigned char *buf, size_t *bufpos)
+fillbuf(int fd, unsigned char *buf, size_t *bufpos, struct tls *tls)
{
size_t num = BUFSIZE - *bufpos;
ssize_t n;
- n = read(fd, buf + *bufpos, num);
- /* don't treat EAGAIN, EINTR as error */
- if (n == -1 && (errno == EAGAIN || errno == EINTR))
- n = -2;
+ if (tls)
+ n = tls_read(tls, buf + *bufpos, num);
+ else {
+ n = read(fd, buf + *bufpos, num);
+ /* don't treat EAGAIN, EINTR as error */
+ if (n == -1 && (errno == EAGAIN || errno == EINTR))
+ n = TLS_WANT_POLLIN;
+ }
if (n <= 0)
return n;
*bufpos += n;
@@ -1079,25 +1304,22 @@ build_ports(char *p)
lo = cp;
}
- /* Load ports sequentially. */
- for (cp = lo; cp <= hi; cp++) {
- portlist[x] = calloc(1, PORT_MAX_LEN);
- if (portlist[x] == NULL)
- err(1, NULL);
- snprintf(portlist[x], PORT_MAX_LEN, "%d", cp);
- x++;
- }
-
- /* Randomly swap ports. */
+ /*
+ * Initialize portlist with a random permutation. Based on
+ * Knuth, as in ip_randomid() in sys/netinet/ip_id.c.
+ */
if (rflag) {
- int y;
- char *c;
-
- for (x = 0; x <= (hi - lo); x++) {
- y = (arc4random() & 0xFFFF) % (hi - lo);
- c = portlist[x];
- portlist[x] = portlist[y];
- portlist[y] = c;
+ for (x = 0; x <= hi - lo; x++) {
+ cp = arc4random_uniform(x + 1);
+ portlist[x] = portlist[cp];
+ if (asprintf(&portlist[cp], "%d", x + lo) < 0)
+ err(1, "asprintf");
+ }
+ } else { /* Load ports sequentially. */
+ for (cp = lo; cp <= hi; cp++) {
+ if (asprintf(&portlist[x], "%d", cp) < 0)
+ err(1, "asprintf");
+ x++;
}
}
} else {
@@ -1205,7 +1427,7 @@ map_tos(char *s, int *val)
{ "netcontrol", IPTOS_PREC_NETCONTROL },
{ "reliability", IPTOS_RELIABILITY },
{ "throughput", IPTOS_THROUGHPUT },
- { NULL, -1 },
+ { NULL, -1 },
};
for (t = toskeywords; t->keyword != NULL; t++) {
@@ -1218,6 +1440,52 @@ map_tos(char *s, int *val)
return (0);
}
+int
+map_tls(char *s, int *val)
+{
+ const struct tlskeywords {
+ const char *keyword;
+ int val;
+ } *t, tlskeywords[] = {
+ { "tlslegacy", TLS_LEGACY },
+ { "noverify", TLS_NOVERIFY },
+ { "noname", TLS_NONAME },
+ { "clientcert", TLS_CCERT},
+ { NULL, -1 },
+ };
+
+ for (t = tlskeywords; t->keyword != NULL; t++) {
+ if (strcmp(s, t->keyword) == 0) {
+ *val |= t->val;
+ return (1);
+ }
+ }
+ return (0);
+}
+
+void
+report_tls(struct tls * tls_ctx, char * host, char *tls_expectname)
+{
+ time_t t;
+ fprintf(stderr, "TLS handshake negotiated %s/%s with host %s\n",
+ tls_conn_version(tls_ctx), tls_conn_cipher(tls_ctx), host);
+ fprintf(stderr, "Peer name: %s\n",
+ tls_expectname ? tls_expectname : host);
+ if (tls_peer_cert_subject(tls_ctx))
+ fprintf(stderr, "Subject: %s\n",
+ tls_peer_cert_subject(tls_ctx));
+ if (tls_peer_cert_issuer(tls_ctx))
+ fprintf(stderr, "Issuer: %s\n",
+ tls_peer_cert_issuer(tls_ctx));
+ if ((t = tls_peer_cert_notbefore(tls_ctx)) != -1)
+ fprintf(stderr, "Valid From: %s", ctime(&t));
+ if ((t = tls_peer_cert_notafter(tls_ctx)) != -1)
+ fprintf(stderr, "Valid Until: %s", ctime(&t));
+ if (tls_peer_cert_hash(tls_ctx))
+ fprintf(stderr, "Cert Hash: %s\n",
+ tls_peer_cert_hash(tls_ctx));
+}
+
void
report_connect(const struct sockaddr *sa, socklen_t salen)
{
@@ -1225,10 +1493,10 @@ report_connect(const struct sockaddr *sa, socklen_t salen)
char remote_port[NI_MAXSERV];
int herr;
int flags = NI_NUMERICSERV;
-
+
if (nflag)
flags |= NI_NUMERICHOST;
-
+
if ((herr = getnameinfo(sa, salen,
remote_host, sizeof(remote_host),
remote_port, sizeof(remote_port),
@@ -1238,7 +1506,7 @@ report_connect(const struct sockaddr *sa, socklen_t salen)
else
errx(1, "getnameinfo: %s", gai_strerror(herr));
}
-
+
fprintf(stderr,
"Connection from %s %s "
"received!\n", remote_host, remote_port);
@@ -1251,12 +1519,17 @@ help(void)
fprintf(stderr, "\tCommand Summary:\n\
\t-4 Use IPv4\n\
\t-6 Use IPv6\n\
+ \t-C certfile Public key file\n\
+ \t-c Use TLS\n\
\t-D Enable the debug socket option\n\
\t-d Detach from stdin\n\
+ \t-e name\t Required name in peer certificate\n\
\t-F Pass socket fd\n\
+ \t-H hash\t Hash string of peer certificate\n\
\t-h This help text\n\
\t-I length TCP receive buffer length\n\
- \t-i secs\t Delay interval for lines sent, ports scanned\n\
+ \t-i interval Delay interval for lines sent, ports scanned\n\
+ \t-K keyfile Private key file\n\
\t-k Keep inbound sockets open for multiple connects\n\
\t-l Listen mode, for inbound connects\n\
\t-N Shutdown the network socket after EOF on stdin\n\
@@ -1264,16 +1537,17 @@ help(void)
\t-O length TCP send buffer length\n\
\t-P proxyuser\tUsername for proxy authentication\n\
\t-p port\t Specify local port for remote connects\n\
+ \t-R CAfile CA bundle\n\
\t-r Randomize remote ports\n\
\t-S Enable the TCP MD5 signature option\n\
- \t-s addr\t Local source address\n\
- \t-T toskeyword\tSet IP Type of Service\n\
+ \t-s source Local source address\n\
+ \t-T keyword TOS value or TLS options\n\
\t-t Answer TELNET negotiation\n\
\t-U Use UNIX domain socket\n\
\t-u UDP mode\n\
\t-V rtable Specify alternate routing table\n\
\t-v Verbose\n\
- \t-w secs\t Timeout for connects and final net reads\n\
+ \t-w timeout Timeout for connects and final net reads\n\
\t-X proto Proxy protocol: \"4\", \"5\" (SOCKS) or \"connect\"\n\
\t-x addr[:port]\tSpecify proxy address and port\n\
\t-z Zero-I/O mode [used for scanning]\n\
@@ -1285,10 +1559,13 @@ void
usage(int ret)
{
fprintf(stderr,
- "usage: nc [-46DdFhklNnrStUuvz] [-I length] [-i interval] [-O length]\n"
- "\t [-P proxy_username] [-p source_port] [-s source] [-T ToS]\n"
- "\t [-V rtable] [-w timeout] [-X proxy_protocol]\n"
- "\t [-x proxy_address[:port]] [destination] [port]\n");
+ "usage: nc [-46cDdFhklNnrStUuvz] [-C certfile] [-e name] "
+ "[-H hash] [-I length]\n"
+ "\t [-i interval] [-K keyfile] [-O length] [-P proxy_username]\n"
+ "\t [-p source_port] [-R CAfile] [-s source] "
+ "[-T keyword] [-V rtable]\n"
+ "\t [-w timeout] [-X proxy_protocol] [-x proxy_address[:port]]\n"
+ "\t [destination] [port]\n");
if (ret)
exit(1);
}
diff --git a/socks.c b/socks.c
index 1b06e0e12dd5..c63cf3493954 100644
--- a/socks.c
+++ b/socks.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: socks.c,v 1.21 2015/03/26 21:19:51 tobias Exp $ */
+/* $OpenBSD: socks.c,v 1.23 2015/12/10 18:31:52 mmcc Exp $ */
/*
* Copyright (c) 1999 Niklas Hallqvist. All rights reserved.
@@ -122,6 +122,58 @@ getproxypass(const char *proxyuser, const char *proxyhost)
return (pw);
}
+/*
+ * Error strings adapted from the generally accepted SOCKSv4 spec:
+ *
+ * http://ftp.icm.edu.pl/packages/socks/socks4/SOCKS4.protocol
+ */
+static const char *
+socks4_strerror(int e)
+{
+ switch (e) {
+ case 90:
+ return "Succeeded";
+ case 91:
+ return "Request rejected or failed";
+ case 92:
+ return "SOCKS server cannot connect to identd on the client";
+ case 93:
+ return "Client program and identd report different user-ids";
+ default:
+ return "Unknown error";
+ }
+}
+
+/*
+ * Error strings taken almost directly from RFC 1928.
+ */
+static const char *
+socks5_strerror(int e)
+{
+ switch (e) {
+ case 0:
+ return "Succeeded";
+ case 1:
+ return "General SOCKS server failure";
+ case 2:
+ return "Connection not allowed by ruleset";
+ case 3:
+ return "Network unreachable";
+ case 4:
+ return "Host unreachable";
+ case 5:
+ return "Connection refused";
+ case 6:
+ return "TTL expired";
+ case 7:
+ return "Command not supported";
+ case 8:
+ return "Address type not supported";
+ default:
+ return "Unknown error";
+ }
+}
+
int
socks_connect(const char *host, const char *port,
struct addrinfo hints __attribute__ ((__unused__)),
@@ -225,8 +277,10 @@ socks_connect(const char *host, const char *port,
cnt = atomicio(read, proxyfd, buf, 4);
if (cnt != 4)
err(1, "read failed (%zu/4)", cnt);
- if (buf[1] != 0)
- errx(1, "connection failed, SOCKS error %d", buf[1]);
+ if (buf[1] != 0) {
+ errx(1, "connection failed, SOCKSv5 error: %s",
+ socks5_strerror(buf[1]));
+ }
switch (buf[3]) {
case SOCKS_IPV4:
cnt = atomicio(read, proxyfd, buf + 4, 6);
@@ -261,8 +315,10 @@ socks_connect(const char *host, const char *port,
cnt = atomicio(read, proxyfd, buf, 8);
if (cnt != 8)
err(1, "read failed (%zu/8)", cnt);
- if (buf[1] != 90)
- errx(1, "connection failed, SOCKS error %d", buf[1]);
+ if (buf[1] != 90) {
+ errx(1, "connection failed, SOCKSv4 error: %s",
+ socks4_strerror(buf[1]));
+ }
} else if (socksv == -1) {
/* HTTP proxy CONNECT */