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);
 }
 

Reply via email to