diff options
| author | beck <> | 2015-09-11 21:07:01 +0000 |
|---|---|---|
| committer | beck <> | 2015-09-11 21:07:01 +0000 |
| commit | 978fe6ccf47347b1b6888d1ca844a45f27f02afd (patch) | |
| tree | 388c48fe01cb6fc8e34d78f32f9557bce52e0311 | |
| parent | 1cba32b3a378b3e7e9771e07233c1515474ce4ab (diff) | |
| download | openbsd-978fe6ccf47347b1b6888d1ca844a45f27f02afd.tar.gz openbsd-978fe6ccf47347b1b6888d1ca844a45f27f02afd.tar.bz2 openbsd-978fe6ccf47347b1b6888d1ca844a45f27f02afd.zip | |
Add TLS suppport to nc. Provides a useful little test and script tool.
ok jsing@ bluhm@
Diffstat (limited to '')
| -rw-r--r-- | src/usr.bin/nc/Makefile | 4 | ||||
| -rw-r--r-- | src/usr.bin/nc/nc.1 | 51 | ||||
| -rw-r--r-- | src/usr.bin/nc/netcat.c | 336 |
3 files changed, 346 insertions, 45 deletions
diff --git a/src/usr.bin/nc/Makefile b/src/usr.bin/nc/Makefile index 150f8295bd..5f20c40cf7 100644 --- a/src/usr.bin/nc/Makefile +++ b/src/usr.bin/nc/Makefile | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | # $OpenBSD: Makefile,v 1.6 2001/09/02 18:45:41 jakob Exp $ | 1 | # $OpenBSD: Makefile,v 1.7 2015/09/11 21:07:01 beck Exp $ |
| 2 | 2 | ||
| 3 | PROG= nc | 3 | PROG= nc |
| 4 | SRCS= netcat.c atomicio.c socks.c | 4 | SRCS= netcat.c atomicio.c socks.c |
| 5 | LDADD+= -ltls -lssl -lcrypto | ||
| 6 | DPADD+= ${LIBTLS} ${LIBSSL} ${LIBCRYPTO} | ||
| 5 | 7 | ||
| 6 | .include <bsd.prog.mk> | 8 | .include <bsd.prog.mk> |
diff --git a/src/usr.bin/nc/nc.1 b/src/usr.bin/nc/nc.1 index d83cb5ca8d..8cb96e8734 100644 --- a/src/usr.bin/nc/nc.1 +++ b/src/usr.bin/nc/nc.1 | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | .\" $OpenBSD: nc.1,v 1.68 2015/03/26 10:35:04 tobias Exp $ | 1 | .\" $OpenBSD: nc.1,v 1.69 2015/09/11 21:07:01 beck Exp $ |
| 2 | .\" | 2 | .\" |
| 3 | .\" Copyright (c) 1996 David Sacerdote | 3 | .\" Copyright (c) 1996 David Sacerdote |
| 4 | .\" All rights reserved. | 4 | .\" All rights reserved. |
| @@ -25,7 +25,7 @@ | |||
| 25 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 25 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 26 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | .\" | 27 | .\" |
| 28 | .Dd $Mdocdate: March 26 2015 $ | 28 | .Dd $Mdocdate: September 11 2015 $ |
| 29 | .Dt NC 1 | 29 | .Dt NC 1 |
| 30 | .Os | 30 | .Os |
| 31 | .Sh NAME | 31 | .Sh NAME |
| @@ -41,7 +41,7 @@ | |||
| 41 | .Op Fl P Ar proxy_username | 41 | .Op Fl P Ar proxy_username |
| 42 | .Op Fl p Ar source_port | 42 | .Op Fl p Ar source_port |
| 43 | .Op Fl s Ar source | 43 | .Op Fl s Ar source |
| 44 | .Op Fl T Ar toskeyword | 44 | .Op Fl T Ar keyword |
| 45 | .Op Fl V Ar rtable | 45 | .Op Fl V Ar rtable |
| 46 | .Op Fl w Ar timeout | 46 | .Op Fl w Ar timeout |
| 47 | .Op Fl X Ar proxy_protocol | 47 | .Op Fl X Ar proxy_protocol |
| @@ -98,10 +98,18 @@ to use IPv4 addresses only. | |||
| 98 | Forces | 98 | Forces |
| 99 | .Nm | 99 | .Nm |
| 100 | to use IPv6 addresses only. | 100 | to use IPv6 addresses only. |
| 101 | .It Fl C Ar certificate_filename | ||
| 102 | Specifies the filename from which the public key part of the TLS | ||
| 103 | certificate is loaded, in PEM format. May only be used with TLS. | ||
| 104 | .It Fl c | ||
| 105 | If using a TCP socket to connect or listen, use TLS. Illegal if not using TCP sockets. | ||
| 101 | .It Fl D | 106 | .It Fl D |
| 102 | Enable debugging on the socket. | 107 | Enable debugging on the socket. |
| 103 | .It Fl d | 108 | .It Fl d |
| 104 | Do not attempt to read from stdin. | 109 | Do not attempt to read from stdin. |
| 110 | .It Fl e Ar name | ||
| 111 | specify the name that must be present in the peer certificate when using TLS. | ||
| 112 | Illegal if not using TLS. | ||
| 105 | .It Fl F | 113 | .It Fl F |
| 106 | Pass the first connected socket using | 114 | Pass the first connected socket using |
| 107 | .Xr sendmsg 2 | 115 | .Xr sendmsg 2 |
| @@ -117,6 +125,11 @@ using the | |||
| 117 | .Xr ssh_config 5 | 125 | .Xr ssh_config 5 |
| 118 | .Cm ProxyUseFdpass | 126 | .Cm ProxyUseFdpass |
| 119 | option). | 127 | option). |
| 128 | .It Fl H Ar hash_string | ||
| 129 | Specifies the required hash string of the peer certificate when using TLS. | ||
| 130 | The string format required is that used by | ||
| 131 | .Xr tls_peer_cert_hash 3 . | ||
| 132 | Illegal if not using TLS, and may not be used with -T noverify. | ||
| 120 | .It Fl h | 133 | .It Fl h |
| 121 | Prints out | 134 | Prints out |
| 122 | .Nm | 135 | .Nm |
| @@ -126,6 +139,9 @@ Specifies the size of the TCP receive buffer. | |||
| 126 | .It Fl i Ar interval | 139 | .It Fl i Ar interval |
| 127 | Specifies a delay time interval between lines of text sent and received. | 140 | Specifies a delay time interval between lines of text sent and received. |
| 128 | Also causes a delay time between connections to multiple ports. | 141 | Also causes a delay time between connections to multiple ports. |
| 142 | .It Fl K Ar key_filename | ||
| 143 | Specifies the filename from which the private key | ||
| 144 | is loaded in PEM format. May only be used with TLS. | ||
| 129 | .It Fl k | 145 | .It Fl k |
| 130 | Forces | 146 | Forces |
| 131 | .Nm | 147 | .Nm |
| @@ -172,6 +188,11 @@ should use, subject to privilege restrictions and availability. | |||
| 172 | It is an error to use this option in conjunction with the | 188 | It is an error to use this option in conjunction with the |
| 173 | .Fl l | 189 | .Fl l |
| 174 | option. | 190 | option. |
| 191 | .It Fl R Ar CA_filename | ||
| 192 | Specifies the filename from which the root CA bundle for Certificate | ||
| 193 | verification is loaded in pem format. Illegal if not using TLS. | ||
| 194 | Default value is | ||
| 195 | .Pa /etc/ssl/cert.pem . | ||
| 175 | .It Fl r | 196 | .It Fl r |
| 176 | Specifies that source and/or destination ports should be chosen randomly | 197 | Specifies that source and/or destination ports should be chosen randomly |
| 177 | instead of sequentially within a range or in the order that the system | 198 | instead of sequentially within a range or in the order that the system |
| @@ -187,9 +208,22 @@ to create and use so that datagrams can be received. | |||
| 187 | It is an error to use this option in conjunction with the | 208 | It is an error to use this option in conjunction with the |
| 188 | .Fl l | 209 | .Fl l |
| 189 | option. | 210 | option. |
| 190 | .It Fl T Ar toskeyword | 211 | .It Fl T Ar keyword |
| 191 | Change IPv4 TOS value. | 212 | Change IPv4 TOS value or TLS options. |
| 192 | .Ar toskeyword | 213 | For TLS options |
| 214 | .Ar keyword | ||
| 215 | may be one of | ||
| 216 | .Ar tlslegacy , | ||
| 217 | which allows legacy TLS protocols, | ||
| 218 | .Ar noverify , | ||
| 219 | which disables certificate verification | ||
| 220 | .Ar noname , | ||
| 221 | which disables certificate name checking, or | ||
| 222 | .Ar clientcert, | ||
| 223 | which requires a client certificate on incoming connections . | ||
| 224 | It is illegal to specify TLS options if not using TLS. | ||
| 225 | For IPv4 TOS value | ||
| 226 | .Ar keyword | ||
| 193 | may be one of | 227 | may be one of |
| 194 | .Ar critical , | 228 | .Ar critical , |
| 195 | .Ar inetcontrol , | 229 | .Ar inetcontrol , |
| @@ -429,6 +463,11 @@ the source port, with a timeout of 5 seconds: | |||
| 429 | .Pp | 463 | .Pp |
| 430 | .Dl $ nc -p 31337 -w 5 host.example.com 42 | 464 | .Dl $ nc -p 31337 -w 5 host.example.com 42 |
| 431 | .Pp | 465 | .Pp |
| 466 | Open a TCP connection to port 443 of www.google.ca, and negotiate | ||
| 467 | TLS. Check for a different name in the certificate for validation. | ||
| 468 | .Pp | ||
| 469 | .Dl $ nc -v -c -e adsf.au.doubleclick.net www.google.ca 443 | ||
| 470 | .Pp | ||
| 432 | Open a UDP connection to port 53 of host.example.com: | 471 | Open a UDP connection to port 53 of host.example.com: |
| 433 | .Pp | 472 | .Pp |
| 434 | .Dl $ nc -u host.example.com 53 | 473 | .Dl $ nc -u host.example.com 53 |
diff --git a/src/usr.bin/nc/netcat.c b/src/usr.bin/nc/netcat.c index ce613cd75d..3909f70817 100644 --- a/src/usr.bin/nc/netcat.c +++ b/src/usr.bin/nc/netcat.c | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | /* $OpenBSD: netcat.c,v 1.132 2015/09/08 17:28:47 bluhm Exp $ */ | 1 | /* $OpenBSD: netcat.c,v 1.133 2015/09/11 21:07:01 beck Exp $ */ |
| 2 | /* | 2 | /* |
| 3 | * Copyright (c) 2001 Eric Jackson <ericj@monkey.org> | 3 | * Copyright (c) 2001 Eric Jackson <ericj@monkey.org> |
| 4 | * Copyright (c) 2015 Bob Beck. All rights reserved. | ||
| 4 | * | 5 | * |
| 5 | * Redistribution and use in source and binary forms, with or without | 6 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions | 7 | * modification, are permitted provided that the following conditions |
| @@ -54,6 +55,7 @@ | |||
| 54 | #include <stdlib.h> | 55 | #include <stdlib.h> |
| 55 | #include <string.h> | 56 | #include <string.h> |
| 56 | #include <unistd.h> | 57 | #include <unistd.h> |
| 58 | #include <tls.h> | ||
| 57 | #include "atomicio.h" | 59 | #include "atomicio.h" |
| 58 | 60 | ||
| 59 | #ifndef SUN_LEN | 61 | #ifndef SUN_LEN |
| @@ -70,6 +72,12 @@ | |||
| 70 | #define POLL_NETIN 2 | 72 | #define POLL_NETIN 2 |
| 71 | #define POLL_STDOUT 3 | 73 | #define POLL_STDOUT 3 |
| 72 | #define BUFSIZE 16384 | 74 | #define BUFSIZE 16384 |
| 75 | #define DEFAULT_CA_FILE "/etc/ssl/cert.pem" | ||
| 76 | |||
| 77 | #define TLS_LEGACY (1 << 1) | ||
| 78 | #define TLS_NOVERIFY (1 << 2) | ||
| 79 | #define TLS_NONAME (1 << 3) | ||
| 80 | #define TLS_CCERT (1 << 4) | ||
| 73 | 81 | ||
| 74 | /* Command Line Options */ | 82 | /* Command Line Options */ |
| 75 | int dflag; /* detached, no stdin */ | 83 | int dflag; /* detached, no stdin */ |
| @@ -95,6 +103,16 @@ int Sflag; /* TCP MD5 signature option */ | |||
| 95 | int Tflag = -1; /* IP Type of Service */ | 103 | int Tflag = -1; /* IP Type of Service */ |
| 96 | int rtableid = -1; | 104 | int rtableid = -1; |
| 97 | 105 | ||
| 106 | int usetls; /* use TLS */ | ||
| 107 | char *Cflag; /* Public cert file */ | ||
| 108 | char *Kflag; /* Private key file */ | ||
| 109 | char *Rflag = DEFAULT_CA_FILE; /* Root CA file */ | ||
| 110 | int tls_cachanged; /* Using non-default CA file */ | ||
| 111 | int TLSopt; /* TLS options */ | ||
| 112 | char *tls_expectname; /* required name in peer cert */ | ||
| 113 | char *tls_peerhash; /* hash of peer cert */ | ||
| 114 | char *tls_expecthash; /* required hash of peer cert */ | ||
| 115 | |||
| 98 | int timeout = -1; | 116 | int timeout = -1; |
| 99 | int family = AF_UNSPEC; | 117 | int family = AF_UNSPEC; |
| 100 | char *portlist[PORT_MAX+1]; | 118 | char *portlist[PORT_MAX+1]; |
| @@ -104,7 +122,7 @@ void atelnet(int, unsigned char *, unsigned int); | |||
| 104 | void build_ports(char *); | 122 | void build_ports(char *); |
| 105 | void help(void); | 123 | void help(void); |
| 106 | int local_listen(char *, char *, struct addrinfo); | 124 | int local_listen(char *, char *, struct addrinfo); |
| 107 | void readwrite(int); | 125 | void readwrite(int, struct tls *); |
| 108 | void fdpass(int nfd) __attribute__((noreturn)); | 126 | void fdpass(int nfd) __attribute__((noreturn)); |
| 109 | int remote_connect(const char *, const char *, struct addrinfo); | 127 | int remote_connect(const char *, const char *, struct addrinfo); |
| 110 | int timeout_connect(int, const struct sockaddr *, socklen_t); | 128 | int timeout_connect(int, const struct sockaddr *, socklen_t); |
| @@ -116,10 +134,14 @@ int unix_connect(char *); | |||
| 116 | int unix_listen(char *); | 134 | int unix_listen(char *); |
| 117 | void set_common_sockopts(int, int); | 135 | void set_common_sockopts(int, int); |
| 118 | int map_tos(char *, int *); | 136 | int map_tos(char *, int *); |
| 137 | int map_tls(char *, int *); | ||
| 119 | void report_connect(const struct sockaddr *, socklen_t); | 138 | void report_connect(const struct sockaddr *, socklen_t); |
| 139 | void report_tls(struct tls * tls_ctx, char * host, char *tls_expectname); | ||
| 120 | void usage(int); | 140 | void usage(int); |
| 121 | ssize_t drainbuf(int, unsigned char *, size_t *); | 141 | ssize_t drainbuf(int, unsigned char *, size_t *, struct tls *); |
| 122 | ssize_t fillbuf(int, unsigned char *, size_t *); | 142 | ssize_t fillbuf(int, unsigned char *, size_t *, struct tls *); |
| 143 | void tls_setup_client(struct tls *, int, char *); | ||
| 144 | struct tls * tls_setup_server(struct tls *, int, char *); | ||
| 123 | 145 | ||
| 124 | int | 146 | int |
| 125 | main(int argc, char *argv[]) | 147 | main(int argc, char *argv[]) |
| @@ -134,6 +156,8 @@ main(int argc, char *argv[]) | |||
| 134 | const char *errstr, *proxyhost = "", *proxyport = NULL; | 156 | const char *errstr, *proxyhost = "", *proxyport = NULL; |
| 135 | struct addrinfo proxyhints; | 157 | struct addrinfo proxyhints; |
| 136 | char unix_dg_tmp_socket_buf[UNIX_DG_TMP_SOCKET_SIZE]; | 158 | char unix_dg_tmp_socket_buf[UNIX_DG_TMP_SOCKET_SIZE]; |
| 159 | struct tls_config *tls_cfg = NULL; | ||
| 160 | struct tls *tls_ctx = NULL; | ||
| 137 | 161 | ||
| 138 | ret = 1; | 162 | ret = 1; |
| 139 | s = 0; | 163 | s = 0; |
| @@ -145,7 +169,7 @@ main(int argc, char *argv[]) | |||
| 145 | signal(SIGPIPE, SIG_IGN); | 169 | signal(SIGPIPE, SIG_IGN); |
| 146 | 170 | ||
| 147 | while ((ch = getopt(argc, argv, | 171 | while ((ch = getopt(argc, argv, |
| 148 | "46DdFhI:i:klNnO:P:p:rSs:tT:UuV:vw:X:x:z")) != -1) { | 172 | "46C:cDde:FH:hI:i:K:klNnO:P:p:R:rSs:T:tUuV:vw:X:x:z")) != -1) { |
| 149 | switch (ch) { | 173 | switch (ch) { |
| 150 | case '4': | 174 | case '4': |
| 151 | family = AF_INET; | 175 | family = AF_INET; |
| @@ -166,12 +190,24 @@ main(int argc, char *argv[]) | |||
| 166 | else | 190 | else |
| 167 | errx(1, "unsupported proxy protocol"); | 191 | errx(1, "unsupported proxy protocol"); |
| 168 | break; | 192 | break; |
| 193 | case 'C': | ||
| 194 | Cflag = optarg; | ||
| 195 | break; | ||
| 196 | case 'c': | ||
| 197 | usetls = 1; | ||
| 198 | break; | ||
| 169 | case 'd': | 199 | case 'd': |
| 170 | dflag = 1; | 200 | dflag = 1; |
| 171 | break; | 201 | break; |
| 202 | case 'e': | ||
| 203 | tls_expectname = optarg; | ||
| 204 | break; | ||
| 172 | case 'F': | 205 | case 'F': |
| 173 | Fflag = 1; | 206 | Fflag = 1; |
| 174 | break; | 207 | break; |
| 208 | case 'H': | ||
| 209 | tls_expecthash = optarg; | ||
| 210 | break; | ||
| 175 | case 'h': | 211 | case 'h': |
| 176 | help(); | 212 | help(); |
| 177 | break; | 213 | break; |
| @@ -180,6 +216,9 @@ main(int argc, char *argv[]) | |||
| 180 | if (errstr) | 216 | if (errstr) |
| 181 | errx(1, "interval %s: %s", errstr, optarg); | 217 | errx(1, "interval %s: %s", errstr, optarg); |
| 182 | break; | 218 | break; |
| 219 | case 'K': | ||
| 220 | Kflag = optarg; | ||
| 221 | break; | ||
| 183 | case 'k': | 222 | case 'k': |
| 184 | kflag = 1; | 223 | kflag = 1; |
| 185 | break; | 224 | break; |
| @@ -198,6 +237,10 @@ main(int argc, char *argv[]) | |||
| 198 | case 'p': | 237 | case 'p': |
| 199 | pflag = optarg; | 238 | pflag = optarg; |
| 200 | break; | 239 | break; |
| 240 | case 'R': | ||
| 241 | tls_cachanged = 1; | ||
| 242 | Rflag = optarg; | ||
| 243 | break; | ||
| 201 | case 'r': | 244 | case 'r': |
| 202 | rflag = 1; | 245 | rflag = 1; |
| 203 | break; | 246 | break; |
| @@ -256,6 +299,8 @@ main(int argc, char *argv[]) | |||
| 256 | errno = 0; | 299 | errno = 0; |
| 257 | if (map_tos(optarg, &Tflag)) | 300 | if (map_tos(optarg, &Tflag)) |
| 258 | break; | 301 | break; |
| 302 | if (map_tls(optarg, &TLSopt)) | ||
| 303 | break; | ||
| 259 | if (strlen(optarg) > 1 && optarg[0] == '0' && | 304 | if (strlen(optarg) > 1 && optarg[0] == '0' && |
| 260 | optarg[1] == 'x') | 305 | optarg[1] == 'x') |
| 261 | Tflag = (int)strtol(optarg, NULL, 16); | 306 | Tflag = (int)strtol(optarg, NULL, 16); |
| @@ -263,7 +308,7 @@ main(int argc, char *argv[]) | |||
| 263 | Tflag = (int)strtonum(optarg, 0, 255, | 308 | Tflag = (int)strtonum(optarg, 0, 255, |
| 264 | &errstr); | 309 | &errstr); |
| 265 | if (Tflag < 0 || Tflag > 255 || errstr || errno) | 310 | if (Tflag < 0 || Tflag > 255 || errstr || errno) |
| 266 | errx(1, "illegal tos value %s", optarg); | 311 | errx(1, "illegal tos/tls value %s", optarg); |
| 267 | break; | 312 | break; |
| 268 | default: | 313 | default: |
| 269 | usage(1); | 314 | usage(1); |
| @@ -295,6 +340,22 @@ main(int argc, char *argv[]) | |||
| 295 | errx(1, "cannot use -z and -l"); | 340 | errx(1, "cannot use -z and -l"); |
| 296 | if (!lflag && kflag) | 341 | if (!lflag && kflag) |
| 297 | errx(1, "must use -l with -k"); | 342 | errx(1, "must use -l with -k"); |
| 343 | if (uflag && usetls) | ||
| 344 | errx(1, "cannot use -c and -u"); | ||
| 345 | if ((family == AF_UNIX) && usetls) | ||
| 346 | errx(1, "cannot use -c and -U"); | ||
| 347 | if (TLSopt && !usetls) | ||
| 348 | errx(1, "you must specify -c to use TLS options"); | ||
| 349 | if (Cflag && !usetls) | ||
| 350 | errx(1, "you must specify -c to use -C"); | ||
| 351 | if (Kflag && !usetls) | ||
| 352 | errx(1, "you must specify -c to use -K"); | ||
| 353 | if (tls_cachanged && !usetls) | ||
| 354 | errx(1, "you must specify -c to use -R"); | ||
| 355 | if (tls_expecthash && !usetls) | ||
| 356 | errx(1, "you must specify -c to use -H"); | ||
| 357 | if (tls_expectname && !usetls) | ||
| 358 | errx(1, "you must specify -c to use -e"); | ||
| 298 | 359 | ||
| 299 | /* Get name of temporary socket for unix datagram client */ | 360 | /* Get name of temporary socket for unix datagram client */ |
| 300 | if ((family == AF_UNIX) && uflag && !lflag) { | 361 | if ((family == AF_UNIX) && uflag && !lflag) { |
| @@ -347,7 +408,34 @@ main(int argc, char *argv[]) | |||
| 347 | proxyhints.ai_flags |= AI_NUMERICHOST; | 408 | proxyhints.ai_flags |= AI_NUMERICHOST; |
| 348 | } | 409 | } |
| 349 | 410 | ||
| 411 | if (usetls) { | ||
| 412 | if (tls_init() == -1) | ||
| 413 | errx(1, "unable to initialize TLS"); | ||
| 414 | if ((tls_cfg = tls_config_new()) == NULL) | ||
| 415 | errx(1, "unable to allocate TLS config"); | ||
| 416 | if (Cflag && (tls_config_set_cert_file(tls_cfg, Cflag) == -1)) | ||
| 417 | errx(1, "unable to set TLS certificate file %s", Cflag); | ||
| 418 | if (Kflag && (tls_config_set_key_file(tls_cfg, Kflag) == -1)) | ||
| 419 | errx(1, "unable to set TLS key file %s", Kflag); | ||
| 420 | if (Rflag && (tls_config_set_ca_file(tls_cfg, Rflag) == -1)) | ||
| 421 | errx(1, "unable to set root CA file %s", Rflag); | ||
| 422 | if (TLSopt & TLS_LEGACY) { | ||
| 423 | tls_config_set_protocols(tls_cfg, TLS_PROTOCOLS_ALL); | ||
| 424 | tls_config_set_ciphers(tls_cfg, "legacy"); | ||
| 425 | } | ||
| 426 | if (!lflag && (TLSopt & TLS_CCERT)) | ||
| 427 | errx(1, "clientcert is only valid with -l"); | ||
| 428 | if (TLSopt & TLS_NONAME) | ||
| 429 | tls_config_insecure_noverifyname(tls_cfg); | ||
| 430 | if (TLSopt & TLS_NOVERIFY) { | ||
| 431 | if (tls_expecthash != NULL) | ||
| 432 | errx(1, "-H and -T noverify may not be used" | ||
| 433 | "together"); | ||
| 434 | tls_config_insecure_noverifycert(tls_cfg); | ||
| 435 | } | ||
| 436 | } | ||
| 350 | if (lflag) { | 437 | if (lflag) { |
| 438 | struct tls *tls_cctx = NULL; | ||
| 351 | int connfd; | 439 | int connfd; |
| 352 | ret = 0; | 440 | ret = 0; |
| 353 | 441 | ||
| @@ -358,6 +446,14 @@ main(int argc, char *argv[]) | |||
| 358 | s = unix_listen(host); | 446 | s = unix_listen(host); |
| 359 | } | 447 | } |
| 360 | 448 | ||
| 449 | if (usetls) { | ||
| 450 | tls_config_verify_client_optional(tls_cfg); | ||
| 451 | if ((tls_ctx = tls_server()) == NULL) | ||
| 452 | errx(1, "tls server creation failed"); | ||
| 453 | if (tls_configure(tls_ctx, tls_cfg) == -1) | ||
| 454 | errx(1, "tls configuration failed (%s)", | ||
| 455 | tls_error(tls_ctx)); | ||
| 456 | } | ||
| 361 | /* Allow only one connection at a time, but stay alive. */ | 457 | /* Allow only one connection at a time, but stay alive. */ |
| 362 | for (;;) { | 458 | for (;;) { |
| 363 | if (family != AF_UNIX) | 459 | if (family != AF_UNIX) |
| @@ -369,7 +465,7 @@ main(int argc, char *argv[]) | |||
| 369 | * receive datagrams from multiple socket pairs. | 465 | * receive datagrams from multiple socket pairs. |
| 370 | */ | 466 | */ |
| 371 | if (uflag && kflag) | 467 | if (uflag && kflag) |
| 372 | readwrite(s); | 468 | readwrite(s, NULL); |
| 373 | /* | 469 | /* |
| 374 | * For UDP and not -k, we will use recvfrom() initially | 470 | * For UDP and not -k, we will use recvfrom() initially |
| 375 | * to wait for a caller, then use the regular functions | 471 | * to wait for a caller, then use the regular functions |
| @@ -394,7 +490,7 @@ main(int argc, char *argv[]) | |||
| 394 | if (vflag) | 490 | if (vflag) |
| 395 | report_connect((struct sockaddr *)&z, len); | 491 | report_connect((struct sockaddr *)&z, len); |
| 396 | 492 | ||
| 397 | readwrite(s); | 493 | readwrite(s, NULL); |
| 398 | } else { | 494 | } else { |
| 399 | len = sizeof(cliaddr); | 495 | len = sizeof(cliaddr); |
| 400 | connfd = accept4(s, (struct sockaddr *)&cliaddr, | 496 | connfd = accept4(s, (struct sockaddr *)&cliaddr, |
| @@ -405,11 +501,23 @@ main(int argc, char *argv[]) | |||
| 405 | } | 501 | } |
| 406 | if (vflag) | 502 | if (vflag) |
| 407 | report_connect((struct sockaddr *)&cliaddr, len); | 503 | report_connect((struct sockaddr *)&cliaddr, len); |
| 408 | 504 | if ((usetls) && | |
| 409 | readwrite(connfd); | 505 | (tls_cctx = tls_setup_server(tls_ctx, connfd, |
| 506 | host))) | ||
| 507 | readwrite(connfd, tls_cctx); | ||
| 508 | if (!usetls) | ||
| 509 | readwrite(connfd, NULL); | ||
| 510 | if (tls_cctx) { | ||
| 511 | int i; | ||
| 512 | do { | ||
| 513 | i = tls_close(tls_cctx); | ||
| 514 | } while (i == TLS_WANT_POLLIN || | ||
| 515 | i == TLS_WANT_POLLOUT); | ||
| 516 | tls_free(tls_cctx); | ||
| 517 | tls_cctx = NULL; | ||
| 518 | } | ||
| 410 | close(connfd); | 519 | close(connfd); |
| 411 | } | 520 | } |
| 412 | |||
| 413 | if (family != AF_UNIX) | 521 | if (family != AF_UNIX) |
| 414 | close(s); | 522 | close(s); |
| 415 | else if (uflag) { | 523 | else if (uflag) { |
| @@ -424,7 +532,7 @@ main(int argc, char *argv[]) | |||
| 424 | ret = 0; | 532 | ret = 0; |
| 425 | 533 | ||
| 426 | if ((s = unix_connect(host)) > 0 && !zflag) { | 534 | if ((s = unix_connect(host)) > 0 && !zflag) { |
| 427 | readwrite(s); | 535 | readwrite(s, NULL); |
| 428 | close(s); | 536 | close(s); |
| 429 | } else | 537 | } else |
| 430 | ret = 1; | 538 | ret = 1; |
| @@ -444,6 +552,13 @@ main(int argc, char *argv[]) | |||
| 444 | if (s) | 552 | if (s) |
| 445 | close(s); | 553 | close(s); |
| 446 | 554 | ||
| 555 | if (usetls) { | ||
| 556 | if ((tls_ctx = tls_client()) == NULL) | ||
| 557 | errx(1, "tls client creation failed"); | ||
| 558 | if (tls_configure(tls_ctx, tls_cfg) == -1) | ||
| 559 | errx(1, "tls configuration failed (%s)", | ||
| 560 | tls_error(tls_ctx)); | ||
| 561 | } | ||
| 447 | if (xflag) | 562 | if (xflag) |
| 448 | s = socks_connect(host, portlist[i], hints, | 563 | s = socks_connect(host, portlist[i], hints, |
| 449 | proxyhost, proxyport, proxyhints, socksv, | 564 | proxyhost, proxyport, proxyhints, socksv, |
| @@ -481,14 +596,30 @@ main(int argc, char *argv[]) | |||
| 481 | } | 596 | } |
| 482 | if (Fflag) | 597 | if (Fflag) |
| 483 | fdpass(s); | 598 | fdpass(s); |
| 484 | else if (!zflag) | 599 | else { |
| 485 | readwrite(s); | 600 | if (usetls) |
| 601 | tls_setup_client(tls_ctx, s, host); | ||
| 602 | if (!zflag) | ||
| 603 | readwrite(s, tls_ctx); | ||
| 604 | if (tls_ctx) { | ||
| 605 | int j; | ||
| 606 | do { | ||
| 607 | j = tls_close(tls_ctx); | ||
| 608 | } while (j == TLS_WANT_POLLIN || | ||
| 609 | j == TLS_WANT_POLLOUT); | ||
| 610 | tls_free(tls_ctx); | ||
| 611 | tls_ctx = NULL; | ||
| 612 | } | ||
| 613 | } | ||
| 486 | } | 614 | } |
| 487 | } | 615 | } |
| 488 | 616 | ||
| 489 | if (s) | 617 | if (s) |
| 490 | close(s); | 618 | close(s); |
| 491 | 619 | ||
| 620 | free(tls_peerhash); | ||
| 621 | tls_config_free(tls_cfg); | ||
| 622 | |||
| 492 | exit(ret); | 623 | exit(ret); |
| 493 | } | 624 | } |
| 494 | 625 | ||
| @@ -524,6 +655,67 @@ unix_bind(char *path) | |||
| 524 | return (s); | 655 | return (s); |
| 525 | } | 656 | } |
| 526 | 657 | ||
| 658 | void | ||
| 659 | tls_setup_client(struct tls *tls_ctx, int s, char *host) | ||
| 660 | |||
| 661 | { | ||
| 662 | int i; | ||
| 663 | if (tls_connect_socket(tls_ctx, s, | ||
| 664 | tls_expectname ? tls_expectname : host) == -1) { | ||
| 665 | errx(1, "tls connection failed (%s)", | ||
| 666 | tls_error(tls_ctx)); | ||
| 667 | } | ||
| 668 | do { | ||
| 669 | if ((i = tls_handshake(tls_ctx)) == -1) | ||
| 670 | errx(1, "tls handshake failed (%s)", | ||
| 671 | tls_error(tls_ctx)); | ||
| 672 | } while (i == TLS_WANT_POLLIN || i == TLS_WANT_POLLOUT); | ||
| 673 | if (tls_peer_cert_hash(tls_ctx, &tls_peerhash) == -1) | ||
| 674 | errx(1, "hash of peer certificate failed"); | ||
| 675 | if (vflag) | ||
| 676 | report_tls(tls_ctx, host, tls_expectname); | ||
| 677 | if (tls_expecthash && tls_peerhash && | ||
| 678 | strcmp(tls_expecthash, tls_peerhash) != 0) | ||
| 679 | errx(1, "peer certificate is not %s", tls_expecthash); | ||
| 680 | } | ||
| 681 | struct tls * | ||
| 682 | tls_setup_server(struct tls *tls_ctx, int connfd, char *host) | ||
| 683 | { | ||
| 684 | struct tls *tls_cctx; | ||
| 685 | if (tls_accept_socket(tls_ctx, &tls_cctx, | ||
| 686 | connfd) == -1) { | ||
| 687 | warnx("tls accept failed (%s)", | ||
| 688 | tls_error(tls_ctx)); | ||
| 689 | tls_cctx = NULL; | ||
| 690 | } else { | ||
| 691 | int i; | ||
| 692 | do { | ||
| 693 | if ((i = tls_handshake(tls_cctx)) == -1) | ||
| 694 | warnx("tls handshake failed (%s)", | ||
| 695 | tls_error(tls_cctx)); | ||
| 696 | } while(i == TLS_WANT_POLLIN || i == TLS_WANT_POLLOUT); | ||
| 697 | } | ||
| 698 | if (tls_cctx) { | ||
| 699 | int gotcert = tls_peer_cert_provided(tls_cctx); | ||
| 700 | if (gotcert && tls_peer_cert_hash(tls_cctx, &tls_peerhash) == -1) | ||
| 701 | warn("hash of peer certificate failed"); | ||
| 702 | if (vflag && gotcert) | ||
| 703 | report_tls(tls_cctx, host, tls_expectname); | ||
| 704 | if ((TLSopt & TLS_CCERT) && !gotcert) | ||
| 705 | warnx("No client certificate provided"); | ||
| 706 | else if (gotcert && tls_peerhash && tls_expecthash && | ||
| 707 | strcmp(tls_expecthash, tls_peerhash) != 0) | ||
| 708 | warnx("peer certificate is not %s", tls_expecthash); | ||
| 709 | else if (gotcert && tls_expectname && | ||
| 710 | (! tls_peer_cert_contains_name(tls_cctx, tls_expectname))) | ||
| 711 | warnx("name (%s) not found in client cert", | ||
| 712 | tls_expectname); | ||
| 713 | else { | ||
| 714 | return tls_cctx; | ||
| 715 | } | ||
| 716 | } | ||
| 717 | return NULL; | ||
| 718 | } | ||
| 527 | /* | 719 | /* |
| 528 | * unix_connect() | 720 | * unix_connect() |
| 529 | * Returns a socket connected to a local unix socket. Returns -1 on failure. | 721 | * Returns a socket connected to a local unix socket. Returns -1 on failure. |
| @@ -731,7 +923,7 @@ local_listen(char *host, char *port, struct addrinfo hints) | |||
| 731 | * Loop that polls on the network file descriptor and stdin. | 923 | * Loop that polls on the network file descriptor and stdin. |
| 732 | */ | 924 | */ |
| 733 | void | 925 | void |
| 734 | readwrite(int net_fd) | 926 | readwrite(int net_fd, struct tls *tls_ctx) |
| 735 | { | 927 | { |
| 736 | struct pollfd pfd[4]; | 928 | struct pollfd pfd[4]; |
| 737 | int stdin_fd = STDIN_FILENO; | 929 | int stdin_fd = STDIN_FILENO; |
| @@ -839,9 +1031,12 @@ readwrite(int net_fd) | |||
| 839 | /* try to read from stdin */ | 1031 | /* try to read from stdin */ |
| 840 | if (pfd[POLL_STDIN].revents & POLLIN && stdinbufpos < BUFSIZE) { | 1032 | if (pfd[POLL_STDIN].revents & POLLIN && stdinbufpos < BUFSIZE) { |
| 841 | ret = fillbuf(pfd[POLL_STDIN].fd, stdinbuf, | 1033 | ret = fillbuf(pfd[POLL_STDIN].fd, stdinbuf, |
| 842 | &stdinbufpos); | 1034 | &stdinbufpos, NULL); |
| 843 | /* error or eof on stdin - remove from pfd */ | 1035 | if (ret == TLS_WANT_POLLIN) |
| 844 | if (ret == 0 || ret == -1) | 1036 | pfd[POLL_STDIN].events = POLLIN; |
| 1037 | else if (ret == TLS_WANT_POLLOUT) | ||
| 1038 | pfd[POLL_STDIN].events = POLLOUT; | ||
| 1039 | else if (ret == 0 || ret == -1) | ||
| 845 | pfd[POLL_STDIN].fd = -1; | 1040 | pfd[POLL_STDIN].fd = -1; |
| 846 | /* read something - poll net out */ | 1041 | /* read something - poll net out */ |
| 847 | if (stdinbufpos > 0) | 1042 | if (stdinbufpos > 0) |
| @@ -853,8 +1048,12 @@ readwrite(int net_fd) | |||
| 853 | /* try to write to network */ | 1048 | /* try to write to network */ |
| 854 | if (pfd[POLL_NETOUT].revents & POLLOUT && stdinbufpos > 0) { | 1049 | if (pfd[POLL_NETOUT].revents & POLLOUT && stdinbufpos > 0) { |
| 855 | ret = drainbuf(pfd[POLL_NETOUT].fd, stdinbuf, | 1050 | ret = drainbuf(pfd[POLL_NETOUT].fd, stdinbuf, |
| 856 | &stdinbufpos); | 1051 | &stdinbufpos, tls_ctx); |
| 857 | if (ret == -1) | 1052 | if (ret == TLS_WANT_POLLIN) |
| 1053 | pfd[POLL_NETOUT].events = POLLIN; | ||
| 1054 | else if (ret == TLS_WANT_POLLOUT) | ||
| 1055 | pfd[POLL_NETOUT].events = POLLOUT; | ||
| 1056 | else if (ret == -1) | ||
| 858 | pfd[POLL_NETOUT].fd = -1; | 1057 | pfd[POLL_NETOUT].fd = -1; |
| 859 | /* buffer empty - remove self from polling */ | 1058 | /* buffer empty - remove self from polling */ |
| 860 | if (stdinbufpos == 0) | 1059 | if (stdinbufpos == 0) |
| @@ -866,8 +1065,12 @@ readwrite(int net_fd) | |||
| 866 | /* try to read from network */ | 1065 | /* try to read from network */ |
| 867 | if (pfd[POLL_NETIN].revents & POLLIN && netinbufpos < BUFSIZE) { | 1066 | if (pfd[POLL_NETIN].revents & POLLIN && netinbufpos < BUFSIZE) { |
| 868 | ret = fillbuf(pfd[POLL_NETIN].fd, netinbuf, | 1067 | ret = fillbuf(pfd[POLL_NETIN].fd, netinbuf, |
| 869 | &netinbufpos); | 1068 | &netinbufpos, tls_ctx); |
| 870 | if (ret == -1) | 1069 | if (ret == TLS_WANT_POLLIN) |
| 1070 | pfd[POLL_NETIN].events = POLLIN; | ||
| 1071 | else if (ret == TLS_WANT_POLLOUT) | ||
| 1072 | pfd[POLL_NETIN].events = POLLOUT; | ||
| 1073 | else if (ret == -1) | ||
| 871 | pfd[POLL_NETIN].fd = -1; | 1074 | pfd[POLL_NETIN].fd = -1; |
| 872 | /* eof on net in - remove from pfd */ | 1075 | /* eof on net in - remove from pfd */ |
| 873 | if (ret == 0) { | 1076 | if (ret == 0) { |
| @@ -888,8 +1091,12 @@ readwrite(int net_fd) | |||
| 888 | /* try to write to stdout */ | 1091 | /* try to write to stdout */ |
| 889 | if (pfd[POLL_STDOUT].revents & POLLOUT && netinbufpos > 0) { | 1092 | if (pfd[POLL_STDOUT].revents & POLLOUT && netinbufpos > 0) { |
| 890 | ret = drainbuf(pfd[POLL_STDOUT].fd, netinbuf, | 1093 | ret = drainbuf(pfd[POLL_STDOUT].fd, netinbuf, |
| 891 | &netinbufpos); | 1094 | &netinbufpos, NULL); |
| 892 | if (ret == -1) | 1095 | if (ret == TLS_WANT_POLLIN) |
| 1096 | pfd[POLL_STDOUT].events = POLLIN; | ||
| 1097 | else if (ret == TLS_WANT_POLLOUT) | ||
| 1098 | pfd[POLL_STDOUT].events = POLLOUT; | ||
| 1099 | else if (ret == -1) | ||
| 893 | pfd[POLL_STDOUT].fd = -1; | 1100 | pfd[POLL_STDOUT].fd = -1; |
| 894 | /* buffer empty - remove self from polling */ | 1101 | /* buffer empty - remove self from polling */ |
| 895 | if (netinbufpos == 0) | 1102 | if (netinbufpos == 0) |
| @@ -913,15 +1120,19 @@ readwrite(int net_fd) | |||
| 913 | } | 1120 | } |
| 914 | 1121 | ||
| 915 | ssize_t | 1122 | ssize_t |
| 916 | drainbuf(int fd, unsigned char *buf, size_t *bufpos) | 1123 | drainbuf(int fd, unsigned char *buf, size_t *bufpos, struct tls *tls) |
| 917 | { | 1124 | { |
| 918 | ssize_t n; | 1125 | ssize_t n; |
| 919 | ssize_t adjust; | 1126 | ssize_t adjust; |
| 920 | 1127 | ||
| 921 | n = write(fd, buf, *bufpos); | 1128 | if (tls) |
| 922 | /* don't treat EAGAIN, EINTR as error */ | 1129 | n = tls_write(tls, buf, *bufpos); |
| 923 | if (n == -1 && (errno == EAGAIN || errno == EINTR)) | 1130 | else { |
| 924 | n = -2; | 1131 | n = write(fd, buf, *bufpos); |
| 1132 | /* don't treat EAGAIN, EINTR as error */ | ||
| 1133 | if (n == -1 && (errno == EAGAIN || errno == EINTR)) | ||
| 1134 | n = TLS_WANT_POLLOUT; | ||
| 1135 | } | ||
| 925 | if (n <= 0) | 1136 | if (n <= 0) |
| 926 | return n; | 1137 | return n; |
| 927 | /* adjust buffer */ | 1138 | /* adjust buffer */ |
| @@ -934,15 +1145,19 @@ drainbuf(int fd, unsigned char *buf, size_t *bufpos) | |||
| 934 | 1145 | ||
| 935 | 1146 | ||
| 936 | ssize_t | 1147 | ssize_t |
| 937 | fillbuf(int fd, unsigned char *buf, size_t *bufpos) | 1148 | fillbuf(int fd, unsigned char *buf, size_t *bufpos, struct tls *tls) |
| 938 | { | 1149 | { |
| 939 | size_t num = BUFSIZE - *bufpos; | 1150 | size_t num = BUFSIZE - *bufpos; |
| 940 | ssize_t n; | 1151 | ssize_t n; |
| 941 | 1152 | ||
| 942 | n = read(fd, buf + *bufpos, num); | 1153 | if (tls) |
| 943 | /* don't treat EAGAIN, EINTR as error */ | 1154 | n = tls_read(tls, buf + *bufpos, num); |
| 944 | if (n == -1 && (errno == EAGAIN || errno == EINTR)) | 1155 | else { |
| 945 | n = -2; | 1156 | n = read(fd, buf + *bufpos, num); |
| 1157 | /* don't treat EAGAIN, EINTR as error */ | ||
| 1158 | if (n == -1 && (errno == EAGAIN || errno == EINTR)) | ||
| 1159 | n = TLS_WANT_POLLIN; | ||
| 1160 | } | ||
| 946 | if (n <= 0) | 1161 | if (n <= 0) |
| 947 | return n; | 1162 | return n; |
| 948 | *bufpos += n; | 1163 | *bufpos += n; |
| @@ -1209,6 +1424,49 @@ map_tos(char *s, int *val) | |||
| 1209 | return (0); | 1424 | return (0); |
| 1210 | } | 1425 | } |
| 1211 | 1426 | ||
| 1427 | int | ||
| 1428 | map_tls(char *s, int *val) | ||
| 1429 | { | ||
| 1430 | const struct tlskeywords { | ||
| 1431 | const char *keyword; | ||
| 1432 | int val; | ||
| 1433 | } *t, tlskeywords[] = { | ||
| 1434 | { "tlslegacy", TLS_LEGACY }, | ||
| 1435 | { "noverify", TLS_NOVERIFY }, | ||
| 1436 | { "noname", TLS_NONAME }, | ||
| 1437 | { "clientcert", TLS_CCERT}, | ||
| 1438 | { NULL, -1 }, | ||
| 1439 | }; | ||
| 1440 | |||
| 1441 | for (t = tlskeywords; t->keyword != NULL; t++) { | ||
| 1442 | if (strcmp(s, t->keyword) == 0) { | ||
| 1443 | *val |= t->val; | ||
| 1444 | return (1); | ||
| 1445 | } | ||
| 1446 | } | ||
| 1447 | return (0); | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | void | ||
| 1451 | report_tls(struct tls * tls_ctx, char * host, char *tls_expectname) | ||
| 1452 | { | ||
| 1453 | char *subject = NULL, *issuer = NULL; | ||
| 1454 | if (tls_peer_cert_subject(tls_ctx, &subject) == -1) | ||
| 1455 | errx(1, "unable to get certificate subject"); | ||
| 1456 | if (tls_peer_cert_issuer(tls_ctx, &issuer) == -1) | ||
| 1457 | errx(1, "unable to get certificate issuer"); | ||
| 1458 | fprintf(stderr, "TLS handshake completed with %s\n", host); | ||
| 1459 | fprintf(stderr, "Peer name %s\n", | ||
| 1460 | tls_expectname ? tls_expectname : host); | ||
| 1461 | if (subject) | ||
| 1462 | fprintf(stderr, "Subject: %s\n", subject); | ||
| 1463 | if (issuer) | ||
| 1464 | fprintf(stderr, "Issuer: %s\n", issuer); | ||
| 1465 | if (tls_peerhash) | ||
| 1466 | fprintf(stderr, "Cert Hash: %s\n", tls_peerhash); | ||
| 1467 | free(subject); | ||
| 1468 | free(issuer); | ||
| 1469 | } | ||
| 1212 | void | 1470 | void |
| 1213 | report_connect(const struct sockaddr *sa, socklen_t salen) | 1471 | report_connect(const struct sockaddr *sa, socklen_t salen) |
| 1214 | { | 1472 | { |
| @@ -1276,10 +1534,12 @@ void | |||
| 1276 | usage(int ret) | 1534 | usage(int ret) |
| 1277 | { | 1535 | { |
| 1278 | fprintf(stderr, | 1536 | fprintf(stderr, |
| 1279 | "usage: nc [-46DdFhklNnrStUuvz] [-I length] [-i interval] [-O length]\n" | 1537 | "usage: nc [-46cDdFhklNnrStUuvz] [-C certfile] [-e name] \n" |
| 1280 | "\t [-P proxy_username] [-p source_port] [-s source] [-T toskeyword]\n" | 1538 | "\t [-I length] [-i interval] [-H hash] [-K keyfile] [-O length]\n" |
| 1281 | "\t [-V rtable] [-w timeout] [-X proxy_protocol]\n" | 1539 | "\t [-P proxy_username] [-p source_port] [-R cafile] [-s source]\n" |
| 1282 | "\t [-x proxy_address[:port]] [destination] [port]\n"); | 1540 | "\t [-T tls|toskeyword] [-V rtable] [-w timeout]\n" |
| 1541 | "\t [-X proxy_protocol] [-x proxy_address[:port]]\n" | ||
| 1542 | "\t [destination] [port]\n"); | ||
| 1283 | if (ret) | 1543 | if (ret) |
| 1284 | exit(1); | 1544 | exit(1); |
| 1285 | } | 1545 | } |
