On Sat, Sep 05, 2015 at 10:13:14AM -0600, Bob Beck wrote:
> I doodled this into netcat last night, because I got tired of typing openssl
> s_client
> and s_server when testing things.. Still scripts nicely.
>
>
>
> -Bob
>
Here's a nicer version that gets rid of the magic number I hated..
Theo gave me a good idea to deal with it, which was sneaking some new
keywords onto the -T option. It also will now errx if you try to do
anything related to TLS without specifying -c, and using a tcp socket,
so it won't ever fail to unencrypted.
Note, the intention is to have what most people need most of the time
for basic connectivity tests and scripts with a minimum of nobs, If
you want crazy options you can always go back to "openssl s_client
--this_stinky_flag --no_this_stinky_flag --stinky_mayby_baby"
Now lets you do
nc -c www.google.com 443
or things like
nc -c -C /home/beck/server.crt -K /home/beck/server.key -k -l localhost 443
and
nc -c -T noverify -T nonanme -T tlslegacy localhost 443
Yes, the -T option parsing in nc is fugly. I don't want to fix that with
this diff.
OK to put it in and have others turdshine along?
Index: Makefile
===================================================================
RCS file: /cvs/src/usr.bin/nc/Makefile,v
retrieving revision 1.6
diff -u -p -u -p -r1.6 Makefile
--- Makefile 2 Sep 2001 18:45:41 -0000 1.6
+++ Makefile 5 Sep 2015 18:45:18 -0000
@@ -2,5 +2,7 @@
PROG= nc
SRCS= netcat.c atomicio.c socks.c
+LDADD+= -ltls -lssl -lcrypto
+DPADD+= ${LIBTLS} ${LIBSSL} ${LIBCRYPTO}
.include <bsd.prog.mk>
Index: nc.1
===================================================================
RCS file: /cvs/src/usr.bin/nc/nc.1,v
retrieving revision 1.68
diff -u -p -u -p -r1.68 nc.1
--- nc.1 26 Mar 2015 10:35:04 -0000 1.68
+++ nc.1 5 Sep 2015 18:45:18 -0000
@@ -41,7 +41,7 @@
.Op Fl P Ar proxy_username
.Op Fl p Ar source_port
.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
@@ -98,6 +98,11 @@ to use IPv4 addresses only.
Forces
.Nm
to use IPv6 addresses only.
+.It Fl c
+If using a tcp socket to connect or listen, use TLS. Illegal if not using TCP
sockets.
+.It Fl C Ar certificate_filename
+Specifies the filename from which the public part of the TLS
+certificate is loaded, in pem format. Illegal if not using TLS.
.It Fl D
Enable debugging on the socket.
.It Fl d
@@ -132,6 +137,9 @@ Forces
to stay listening for another connection after its current connection
is completed.
It is an error to use this option without the
+.It Fl K Ar key_filename
+Specifies the filename from which the private key for the TLS certificate
+is loaded in pem format. Illegal if not using TLS.
.Fl l
option.
When used together with the
@@ -176,6 +184,11 @@ option.
Specifies that source and/or destination ports should be chosen randomly
instead of sequentially within a range or in the order that the system
assigns them.
+.It Fl R Ar CA_filename
+Specifies the filename from which the root CA bundle for Certificate
+verification is loaded in pem format. Illegal if not using TLS.
+Default value is
+.Pa /etc/ssl/cert.pem .
.It Fl S
Enables the RFC 2385 TCP MD5 signature option.
.It Fl s Ar source
@@ -187,9 +200,20 @@ to create and use so that datagrams can
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, or
+.Ar noname ,
+which disables certificate name checking.
+It is illegal to specify TLS options if not using TLS.
+For IPv4 TOS value
+.Ar keyword
may be one of
.Ar critical ,
.Ar inetcontrol ,
Index: netcat.c
===================================================================
RCS file: /cvs/src/usr.bin/nc/netcat.c,v
retrieving revision 1.129
diff -u -p -u -p -r1.129 netcat.c
--- netcat.c 26 Mar 2015 21:22:50 -0000 1.129
+++ netcat.c 5 Sep 2015 18:45:18 -0000
@@ -1,6 +1,7 @@
/* $OpenBSD: netcat.c,v 1.129 2015/03/26 21:22:50 tobias Exp $ */
/*
* Copyright (c) 2001 Eric Jackson <[email protected]>
+ * 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
@@ -54,6 +55,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <tls.h>
#include "atomicio.h"
#ifndef SUN_LEN
@@ -70,6 +72,11 @@
#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)
/* Command Line Options */
int dflag; /* detached, no stdin */
@@ -95,6 +102,13 @@ int Sflag; /* TCP MD5
signature opti
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 cachanged; /* Using non-default CA file */
+int TLSopt; /* TLS options */
+
int timeout = -1;
int family = AF_UNSPEC;
char *portlist[PORT_MAX+1];
@@ -104,7 +118,7 @@ void atelnet(int, unsigned char *, unsig
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);
@@ -116,10 +130,11 @@ 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 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 *);
int
main(int argc, char *argv[])
@@ -134,6 +149,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 *tlsc = NULL;
+ struct tls *ctx = NULL;
ret = 1;
s = 0;
@@ -145,7 +162,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) {
+ "46DcC:dFhI:i:kK:lNnO:P:p:rSs:tT:UuV:vw:X:x:z")) != -1) {
switch (ch) {
case '4':
family = AF_INET;
@@ -166,6 +183,9 @@ main(int argc, char *argv[])
else
errx(1, "unsupported proxy protocol");
break;
+ case 'c':
+ usetls = 1;
+ break;
case 'd':
dflag = 1;
break;
@@ -183,6 +203,9 @@ main(int argc, char *argv[])
case 'k':
kflag = 1;
break;
+ case 'K':
+ Kflag = optarg;
+ break;
case 'l':
lflag = 1;
break;
@@ -195,12 +218,19 @@ main(int argc, char *argv[])
case 'P':
Pflag = optarg;
break;
+ case 'C':
+ Cflag = optarg;
+ break;
case 'p':
pflag = optarg;
break;
case 'r':
rflag = 1;
break;
+ case 'R':
+ cachanged = 1;
+ Rflag = optarg;
+ break;
case 's':
sflag = optarg;
break;
@@ -256,6 +286,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 +295,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);
@@ -295,6 +327,18 @@ 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 (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 (cachanged && !usetls)
+ errx(1, "you must specify -c to use -R");
/* Get name of temporary socket for unix datagram client */
if ((family == AF_UNIX) && uflag && !lflag) {
@@ -347,6 +391,25 @@ main(int argc, char *argv[])
proxyhints.ai_flags |= AI_NUMERICHOST;
}
+ if (usetls) {
+ if (tls_init() == -1)
+ errx(1, "unable to initialize tls");
+ if ((tlsc = tls_config_new()) == NULL)
+ errx(1, "unable allocate tls config");
+ if (Cflag && (tls_config_set_cert_file(tlsc, Cflag) == -1))
+ errx(1, "unable to set TLS certificate file %s", Cflag);
+ if (Kflag && (tls_config_set_key_file(tlsc, Kflag) == -1))
+ errx(1, "unable to set TLS key file %s", Kflag);
+ if (Rflag && (tls_config_set_ca_file(tlsc, Rflag) == -1))
+ errx(1, "unable to set Root CA file %s", Rflag);
+ if (TLSopt & TLS_LEGACY)
+ tls_config_set_protocols(tlsc, TLS_PROTOCOLS_ALL);
+ if (TLSopt & TLS_NONAME)
+ tls_config_insecure_noverifyname(tlsc);
+ if (TLSopt & TLS_NOVERIFY)
+ tls_config_insecure_noverifycert(tlsc);
+ }
+
if (lflag) {
int connfd;
ret = 0;
@@ -369,7 +432,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,8 +457,9 @@ main(int argc, char *argv[])
if (vflag)
report_connect((struct sockaddr *)&z,
len);
- readwrite(s);
+ readwrite(s, NULL);
} else {
+ struct tls *cctx = NULL;
len = sizeof(cliaddr);
connfd = accept(s, (struct sockaddr *)&cliaddr,
&len);
@@ -405,11 +469,44 @@ main(int argc, char *argv[])
}
if (vflag)
report_connect((struct sockaddr
*)&cliaddr, len);
-
- readwrite(connfd);
+ if (usetls) {
+ int i;
+ if ((ctx = tls_server()) == NULL)
+ errx(1, "tls client creation
failed");
+ if (tls_configure(ctx, tlsc) == -1)
+ errx(1, "tls configuration
failed (%s)",
+ tls_error(ctx));
+ do {
+ i = tls_accept_socket(ctx,
&cctx, connfd);
+ if (i == -1) {
+ warn ("tls connection
failed (%s)",
+ tls_error(ctx));
+ cctx = NULL;
+ }
+ } while (i == TLS_READ_AGAIN || i ==
TLS_WRITE_AGAIN);
+ }
+ if (usetls && cctx)
+ readwrite(connfd, cctx);
+ if (!usetls)
+ readwrite(connfd, NULL);
+ if (ctx) {
+ int i;
+ do {
+ i = tls_close(ctx);
+ } while (i == TLS_READ_AGAIN || i ==
TLS_WRITE_AGAIN);
+ tls_free(ctx);
+ ctx = NULL;
+ }
+ if (cctx) {
+ int i;
+ do {
+ i = tls_close(cctx);
+ } while (i == TLS_READ_AGAIN || i ==
TLS_WRITE_AGAIN);
+ tls_free(cctx);
+ cctx = NULL;
+ }
close(connfd);
}
-
if (family != AF_UNIX)
close(s);
else if (uflag) {
@@ -424,7 +521,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;
@@ -481,14 +578,34 @@ main(int argc, char *argv[])
}
if (Fflag)
fdpass(s);
- else if (!zflag)
- readwrite(s);
+ else {
+ if (usetls) {
+ if ((ctx = tls_client()) == NULL)
+ errx(1, "tls client creation
failed");
+ if (tls_configure(ctx, tlsc) == -1)
+ errx(1, "tls configuration
failed");
+ if (tls_connect_socket(ctx, s, host) ==
-1)
+ errx(1, "tls connection failed
(%s)", tls_error(ctx));
+ }
+ if (!zflag)
+ readwrite(s, ctx);
+ if (ctx) {
+ int i;
+ do {
+ i = tls_close(ctx);
+ } while (i == TLS_READ_AGAIN || i ==
TLS_WRITE_AGAIN);
+ tls_free(ctx);
+ ctx = NULL;
+ }
+ }
}
}
if (s)
close(s);
+ tls_config_free(tlsc);
+
exit(ret);
}
@@ -740,7 +857,7 @@ local_listen(char *host, char *port, str
* Loop that polls on the network file descriptor and stdin.
*/
void
-readwrite(int net_fd)
+readwrite(int net_fd, struct tls *ctx)
{
struct pollfd pfd[4];
int stdin_fd = STDIN_FILENO;
@@ -772,6 +889,7 @@ readwrite(int net_fd)
pfd[POLL_STDOUT].fd = stdout_fd;
pfd[POLL_STDOUT].events = 0;
+
while (1) {
/* both inputs are gone, buffers are empty, we are done */
if (pfd[POLL_STDIN].fd == -1 && pfd[POLL_NETIN].fd == -1
@@ -848,7 +966,7 @@ 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);
+ &stdinbufpos, NULL);
/* error or eof on stdin - remove from pfd */
if (ret == 0 || ret == -1)
pfd[POLL_STDIN].fd = -1;
@@ -862,7 +980,7 @@ 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);
+ &stdinbufpos, ctx);
if (ret == -1)
pfd[POLL_NETOUT].fd = -1;
/* buffer empty - remove self from polling */
@@ -875,7 +993,7 @@ 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);
+ &netinbufpos, ctx);
if (ret == -1)
pfd[POLL_NETIN].fd = -1;
/* eof on net in - remove from pfd */
@@ -897,7 +1015,7 @@ 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);
+ &netinbufpos, NULL);
if (ret == -1)
pfd[POLL_STDOUT].fd = -1;
/* buffer empty - remove self from polling */
@@ -922,17 +1040,27 @@ 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;
+ size_t t;
- n = write(fd, buf, *bufpos);
- /* don't treat EAGAIN, EINTR as error */
- if (n == -1 && (errno == EAGAIN || errno == EINTR))
- n = -2;
- if (n <= 0)
- return n;
+ if (tls) {
+ n = tls_write(tls, buf, *bufpos, &t);
+ if (n == TLS_READ_AGAIN || n == TLS_WRITE_AGAIN)
+ n = -2;
+ if (n < 0)
+ return n;
+ n = t;
+ } else {
+ n = write(fd, buf, *bufpos);
+ /* don't treat EAGAIN, EINTR as error */
+ if (n == -1 && (errno == EAGAIN || errno == EINTR))
+ n = -2;
+ if (n <= 0)
+ return n;
+ }
/* adjust buffer */
adjust = *bufpos - n;
if (adjust > 0)
@@ -943,17 +1071,27 @@ drainbuf(int fd, unsigned char *buf, siz
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;
+ size_t t;
- n = read(fd, buf + *bufpos, num);
- /* don't treat EAGAIN, EINTR as error */
- if (n == -1 && (errno == EAGAIN || errno == EINTR))
- n = -2;
- if (n <= 0)
- return n;
+ if (tls) {
+ n = tls_read(tls, buf + *bufpos, num, &t);
+ if (n == TLS_READ_AGAIN || n == TLS_WRITE_AGAIN)
+ n = -2;
+ if (n < 0)
+ return n;
+ n = t;
+ } else {
+ n = read(fd, buf + *bufpos, num);
+ /* don't treat EAGAIN, EINTR as error */
+ if (n == -1 && (errno == EAGAIN || errno == EINTR))
+ n = -2;
+ if (n <= 0)
+ return n;
+ }
*bufpos += n;
return n;
}
@@ -1215,6 +1353,28 @@ 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 },
+ { NULL, -1 },
+ };
+
+ for (t = tlskeywords; t->keyword != NULL; t++) {
+ if (strcmp(s, t->keyword) == 0) {
+ *val |= t->val;
+ return (1);
+ }
+ }
return (0);
}