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
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 16:03:45 -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 16:03:45 -0000
@@ -102,6 +102,17 @@ to use IPv6 addresses only.
Enable debugging on the socket.
.It Fl d
Do not attempt to read from stdin.
+.It Fl c
+If using a tcp socket to connect or listen, use TLS. This option
+takes optional numeric argument that can be used to make the TLS
+connection less secure. specifying 1 disables certificate validity
+verification, specifying 2 disables the verification of the
+certificate name against the hostname, and 3 disables all certificate
+verification.
+.It Fl C Ar certificate_filename
+Specifies the filename from which the public part of the TLS
+certificate is loaded, in pem format. Has no effect when TLS is not
+used.
.It Fl F
Pass the first connected socket using
.Xr sendmsg 2
@@ -132,6 +143,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. Has no effect when TLS is not used.
.Fl l
option.
When used together with the
@@ -176,6 +190,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. Has no effect when TLS is not used.
+Default value is
+.Pa /etc/ssl/cert.pem .
.It Fl S
Enables the RFC 2385 TCP MD5 signature option.
.It Fl s Ar source
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 16:03:45 -0000
@@ -1,6 +1,7 @@
/* $OpenBSD: netcat.c,v 1.129 2015/03/26 21:22:50 tobias Exp $ */
/*
* Copyright (c) 2001 Eric Jackson <er...@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
@@ -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,7 @@
#define POLL_NETIN 2
#define POLL_STDOUT 3
#define BUFSIZE 16384
+#define DEFAULT_CA_FILE "/etc/ssl/cert.pem"
/* Command Line Options */
int dflag; /* detached, no stdin */
@@ -95,6 +98,12 @@ 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 insecure; /* Insecure Mode */
+
int timeout = -1;
int family = AF_UNSPEC;
char *portlist[PORT_MAX+1];
@@ -104,7 +113,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);
@@ -118,8 +127,8 @@ void set_common_sockopts(int, int);
int map_tos(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 +143,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 +156,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) {
+ "46Dc::C:dFhI:i:kK:lNnO:P:p:rSs:tT:UuV:vw:X:x:z")) != -1) {
switch (ch) {
case '4':
family = AF_INET;
@@ -166,6 +177,14 @@ main(int argc, char *argv[])
else
errx(1, "unsupported proxy protocol");
break;
+ case 'c':
+ usetls = 1;
+ if (optarg != NULL) {
+ insecure = strtonum(optarg, 0, 3, &errstr);
+ if (errstr)
+ errx(1, "insecure %s: %s", errstr,
optarg);
+ }
+ break;
case 'd':
dflag = 1;
break;
@@ -183,6 +202,9 @@ main(int argc, char *argv[])
case 'k':
kflag = 1;
break;
+ case 'K':
+ Kflag = optarg;
+ break;
case 'l':
lflag = 1;
break;
@@ -195,12 +217,18 @@ 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':
+ Rflag = optarg;
+ break;
case 's':
sflag = optarg;
break;
@@ -347,6 +375,33 @@ 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);
+ switch (insecure) {
+ case 1:
+ tls_config_insecure_noverifyname(tlsc);
+ break;
+ case 2:
+ tls_config_insecure_noverifycert(tlsc);
+ break;
+ case 3:
+ tls_config_insecure_noverifyname(tlsc);
+ tls_config_insecure_noverifycert(tlsc);
+ break;
+ default:
+ break;
+ }
+ }
+
if (lflag) {
int connfd;
ret = 0;
@@ -369,7 +424,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 +449,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 +461,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 +513,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 +570,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);
}
@@ -608,7 +717,7 @@ remote_connect(const char *host, const c
/* try SO_BINDANY, but don't insist */
setsockopt(s, SOL_SOCKET, SO_BINDANY, &on, sizeof(on));
- memset(&ahints, 0, sizeof(struct addrinfo));
+ memset(&ahints, 0, sizeof(struct addrinfo));
ahints.ai_family = res0->ai_family;
ahints.ai_socktype = uflag ? SOCK_DGRAM : SOCK_STREAM;
ahints.ai_protocol = uflag ? IPPROTO_UDP : IPPROTO_TCP;
@@ -740,7 +849,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 +881,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 +958,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 +972,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 +985,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 +1007,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 +1032,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 +1063,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;
}