diff options
author | Xin LI <delphij@FreeBSD.org> | 2016-04-24 22:12:47 +0000 |
---|---|---|
committer | Xin LI <delphij@FreeBSD.org> | 2016-04-24 22:12:47 +0000 |
commit | 09ac48e1c32eeedc204f8543f59cf29a7459298b (patch) | |
tree | ece78a3a8bdb725e9c7e47f96ddd59e9e66a230f | |
parent | 70061f65353c351c2a0712f76d3e29a0164ce1dc (diff) |
Notes
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | nc.1 | 75 | ||||
-rw-r--r-- | netcat.c | 529 | ||||
-rw-r--r-- | socks.c | 66 |
4 files changed, 525 insertions, 149 deletions
@@ -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> @@ -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 @@ -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); } @@ -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 */ |