diff options
author | beck <> | 2015-09-11 21:07:01 +0000 |
---|---|---|
committer | beck <> | 2015-09-11 21:07:01 +0000 |
commit | 37a2330d0f355087b739ffe7d0a69f1f0d87055d (patch) | |
tree | 388c48fe01cb6fc8e34d78f32f9557bce52e0311 | |
parent | 150d9a597eaa22312d0f25f7714b9d3cbfb1ce2d (diff) | |
download | openbsd-37a2330d0f355087b739ffe7d0a69f1f0d87055d.tar.gz openbsd-37a2330d0f355087b739ffe7d0a69f1f0d87055d.tar.bz2 openbsd-37a2330d0f355087b739ffe7d0a69f1f0d87055d.zip |
Add TLS suppport to nc. Provides a useful little test and script tool.
ok jsing@ bluhm@
-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 | } |