diff options
author | Laurent Bercot <ska-skaware@skarnet.org> | 2023-11-16 05:13:06 +0000 |
---|---|---|
committer | Laurent Bercot <ska@appnovation.com> | 2023-11-16 05:13:06 +0000 |
commit | 26597a785ec2dd4e9ec9fb7d9765d2ee8779ee16 (patch) | |
tree | c2c5602397e9381b1cb04472ed057edd4a00e520 | |
parent | d8ca717da164c3e76ebb56c954d0a08544955601 (diff) | |
download | s6-networking-26597a785ec2dd4e9ec9fb7d9765d2ee8779ee16.tar.xz |
Add -J and -j to the TLS tools to check for peer close_notify.
Also, and more importantly, significantly rewrite stls_run()
for better full-duplex support. This implementation isn't fully
tested yet.
Signed-off-by: Laurent Bercot <ska@appnovation.com>
-rw-r--r-- | doc/s6-tlsc-io.html | 6 | ||||
-rw-r--r-- | doc/s6-tlsc.html | 5 | ||||
-rw-r--r-- | doc/s6-tlsclient.html | 1 | ||||
-rw-r--r-- | doc/s6-tlsd-io.html | 6 | ||||
-rw-r--r-- | doc/s6-tlsd.html | 5 | ||||
-rw-r--r-- | doc/s6-tlsserver.html | 1 | ||||
-rw-r--r-- | doc/s6-ucspitlsc.html | 5 | ||||
-rw-r--r-- | doc/s6-ucspitlsd.html | 5 | ||||
-rw-r--r-- | src/include/s6-networking/stls.h | 5 | ||||
-rw-r--r-- | src/sbearssl/sbearssl_run.c | 13 | ||||
-rw-r--r-- | src/stls/stls_client_init_and_handshake.c | 4 | ||||
-rw-r--r-- | src/stls/stls_run.c | 360 | ||||
-rw-r--r-- | src/stls/stls_server_init_and_handshake.c | 3 | ||||
-rw-r--r-- | src/tls/s6-tlsc-io.c | 8 | ||||
-rw-r--r-- | src/tls/s6-tlsc.c | 12 | ||||
-rw-r--r-- | src/tls/s6-tlsd-io.c | 6 | ||||
-rw-r--r-- | src/tls/s6-tlsd.c | 8 | ||||
-rw-r--r-- | src/tls/s6-ucspitlsc.c | 10 | ||||
-rw-r--r-- | src/tls/s6-ucspitlsd.c | 8 | ||||
-rw-r--r-- | src/tls/s6tls-internal.h | 2 | ||||
-rw-r--r-- | src/tls/s6tls_prep_tlscio.c | 1 | ||||
-rw-r--r-- | src/tls/s6tls_prep_tlsdio.c | 1 |
22 files changed, 261 insertions, 214 deletions
diff --git a/doc/s6-tlsc-io.html b/doc/s6-tlsc-io.html index f4a81a2..17a4e26 100644 --- a/doc/s6-tlsc-io.html +++ b/doc/s6-tlsc-io.html @@ -38,7 +38,7 @@ the options given when configuring s6-networking. <h2> Interface </h2> <pre> - s6-tlsc-io [ -S | -s ] [ -Y | -y ] [ -v <em>verbosity</em> ] [ -K kimeout ] [ -k <em>servername</em> ] [ -d <em>notif</em> ] [ -- ] <em>fdr</em> <em>fdw</em> + s6-tlsc-io [ -S | -s ] [ -J | -j ] [ -Y | -y ] [ -v <em>verbosity</em> ] [ -K kimeout ] [ -k <em>servername</em> ] [ -d <em>notif</em> ] [ -- ] <em>fdr</em> <em>fdw</em> </pre> <ul> @@ -190,6 +190,10 @@ no effect. </li> and break the connection when receiving a local EOF. </li> <li> <tt>-s</tt> : transmit EOF by half-closing the TCP connection without using <tt>close_notify</tt>. This is the default. </li> + <li> <tt>-J</tt> : treat EOF from the peer without a prior close_notify +as an error: print a fatal error message and exit 98. </li> + <li> <tt>-j</tt> : treat EOF from the peer without a prior close_notify +as a normal exit condition. This is the default. </li> <li> <tt>-Y</tt> : Do not send a client certificate. This is the default. </li> <li> <tt>-y</tt> : Send a client certificate. </li> <li> <tt>-k <em>servername</em></tt> : use Server Name diff --git a/doc/s6-tlsc.html b/doc/s6-tlsc.html index 1d11c5b..aff6828 100644 --- a/doc/s6-tlsc.html +++ b/doc/s6-tlsc.html @@ -29,7 +29,7 @@ TLS/SSL. <h2> Interface </h2> <pre> - s6-tlsc [ -S | -s ] [ -Y | -y ] [ -Z | -z ] [ -v <em>verbosity</em> ] [ -K kimeout ] [ -k <em>servername</em> ] [ -6 <em>rfd</em> ] [ -7 <em>wfd</em> ] [ -- ] <em>prog...</em> + s6-tlsc [ -S | -s ] [ -J | -j ] [ -Y | -y ] [ -Z | -z ] [ -v <em>verbosity</em> ] [ -K kimeout ] [ -k <em>servername</em> ] [ -6 <em>rfd</em> ] [ -7 <em>wfd</em> ] [ -- ] <em>prog...</em> </pre> <ul> @@ -126,6 +126,9 @@ before execing <em>prog...</em>. This is the default. </li> and break the connection when <em>prog</em> sends EOF. </li> <li> <tt>-s</tt> : transmit EOF by half-closing the TCP connection without using <tt>close_notify</tt>. This is the default. </li> + <li> <tt>-J</tt> : make <a href="s6-tlsc-io.html">s6-tlsc-io</a> +exit with a nonzero code if the peer sends EOF without a close_notify first </li> + <li> <tt>-j</tt> : treat EOF from the peer as a normal exit condition </li> <li> <tt>-Y</tt> : Do not send a client certificate. This is the default. </li> <li> <tt>-y</tt> : Send a client certificate. </li> <li> <tt>-k <em>servername</em></tt> : use Server Name diff --git a/doc/s6-tlsclient.html b/doc/s6-tlsclient.html index 287c02c..09276d4 100644 --- a/doc/s6-tlsclient.html +++ b/doc/s6-tlsclient.html @@ -144,6 +144,7 @@ generally work: the defaults are sensible. <li> <tt>-Z</tt>, <tt>-z</tt> : keep or remove the <a href="s6-tlsc-io.html">s6-tlsc-io</a>-specific variables from the application's environment </li> <li> <tt>-S</tt>, <tt>-s</tt> : use close_notify or EOF to signal the end of a TLS connection </li> + <li> <tt>-J</tt>, <tt>-j</tt> : exit nonzero with an error message when the peer fails to close_notify, or ignore it </li> <li> <tt>-Y</tt>, <tt>-y</tt> : don't send, or send, a client certificate </li> <li> <tt>-k <em>servername</em></tt> : use SNI and provide a server name </li> <li> <tt>-K <em>kimeout</em></tt> : set a timeout for the TLS handshake </li> diff --git a/doc/s6-tlsd-io.html b/doc/s6-tlsd-io.html index 0f3b922..55e293f 100644 --- a/doc/s6-tlsd-io.html +++ b/doc/s6-tlsd-io.html @@ -38,7 +38,7 @@ the options given when configuring s6-networking. <h2> Interface </h2> <pre> - s6-tlsd-io [ -S | -s ] [ -Y | -y ] [ -v <em>verbosity</em> ] [ -K <em>kimeout</em> ] [ -k <em>snilevel</em> ] [ -d <em>notif</em> ] [ -- ] <em>fdr</em> <em>fdw</em> + s6-tlsd-io [ -S | -s ] [ -J | -j ] [ -Y | -y ] [ -v <em>verbosity</em> ] [ -K <em>kimeout</em> ] [ -k <em>snilevel</em> ] [ -d <em>notif</em> ] [ -- ] <em>fdr</em> <em>fdw</em> </pre> <ul> @@ -210,6 +210,10 @@ no effect. </li> and break the connection when receiving a local EOF. </li> <li> <tt>-s</tt> : transmit EOF by half-closing the TCP connection without using <tt>close_notify</tt>. This is the default. </li> + <li> <tt>-J</tt> : treat EOF from the peer without a prior close_notify +as an error: print a fatal error message and exit 98. </li> + <li> <tt>-j</tt> : treat EOF from the peer without a prior close_notify +as a normal exit condition. This is the default. </li> <li> <tt>-Y</tt> : Request a client certificate. The certificate is optional: if the client gives none, the connection proceeds. </li> diff --git a/doc/s6-tlsd.html b/doc/s6-tlsd.html index 883777b..73a9f9b 100644 --- a/doc/s6-tlsd.html +++ b/doc/s6-tlsd.html @@ -38,7 +38,7 @@ the options given when configuring <tt>s6-networking</tt>. <h2> Interface </h2> <pre> - s6-tlsd [ -S | -s ] [ -Y | -y ] [ -Z | -z ] [ -v <em>verbosity</em> ] [ -K <em>kimeout</em> ] [ -k <em>snilevel</em> ] [ -- ] <em>prog...</em> + s6-tlsd [ -S | -s ] [ -J | -j ] [ -Y | -y ] [ -Z | -z ] [ -v <em>verbosity</em> ] [ -K <em>kimeout</em> ] [ -k <em>snilevel</em> ] [ -- ] <em>prog...</em> </pre> <ul> @@ -139,6 +139,9 @@ before execing <em>prog...</em>. This is the default. </li> and break the connection when <em>prog</em> sends EOF. </li> <li> <tt>-s</tt> : transmit EOF by half-closing the TCP connection without using <tt>close_notify</tt>. This is the default. </li> + <li> <tt>-J</tt> : make <a href="s6-tlsd-io.html">s6-tlsd-io</a> +exit with a nonzero code if the peer sends EOF without a close_notify first </li> + <li> <tt>-j</tt> : treat EOF from the peer as a normal exit condition </li> <li> <tt>-Y</tt> : Request an optional client certificate. </li> <li> <tt>-y</tt> : Request a mandatory client certificate. The default, with neither the <tt>-Y</tt> nor the <tt>-y</tt> option, diff --git a/doc/s6-tlsserver.html b/doc/s6-tlsserver.html index d1ca3e2..8713235 100644 --- a/doc/s6-tlsserver.html +++ b/doc/s6-tlsserver.html @@ -177,6 +177,7 @@ certificates, you probably still want TCP access rules. <li> <tt>-Z</tt>, <tt>-z</tt> : keep or remove the <a href="s6-tlsd-io.html">s6-tlsd-io</a>-specific variables from the application's environment </li> <li> <tt>-S</tt>, <tt>-s</tt> : use close_notify or EOF to signal the end of a TLS connection </li> + <li> <tt>-J</tt>, <tt>-j</tt> : exit nonzero with an error message when the peer fails to close_notify, or ignore it </li> <li> <tt>-Y</tt>, <tt>-y</tt> : request an optional or a mandatory client certificate </li> <li> <tt>-K <em>kimeout</em></tt> : set a timeout for the TLS handshake </li> <li> <tt>-k <em>snilevel</em></tt> : support SNI-based certificate chains </li> diff --git a/doc/s6-ucspitlsc.html b/doc/s6-ucspitlsc.html index e096e24..52af6db 100644 --- a/doc/s6-ucspitlsc.html +++ b/doc/s6-ucspitlsc.html @@ -36,7 +36,7 @@ TLS stack in the client itself. <h2> Interface </h2> <pre> - s6-ucspitlsc [ -S | -s ] [ -Y | -y ] [ -Z | -z ] [ -v <em>verbosity</em> ] [ -K kimeout ] [ -k <em>servername</em> ] [ -6 <em>rfd</em> ] [ -7 <em>wfd</em> ] [ -- ] <em>prog...</em> + s6-ucspitlsc [ -S | -s ] [ -J | -j ] [ -Y | -y ] [ -Z | -z ] [ -v <em>verbosity</em> ] [ -K kimeout ] [ -k <em>servername</em> ] [ -6 <em>rfd</em> ] [ -7 <em>wfd</em> ] [ -- ] <em>prog...</em> </pre> <ul> @@ -142,6 +142,9 @@ before execing <em>prog...</em>. This is the default. </li> and break the connection when <em>prog</em> sends EOF. </li> <li> <tt>-s</tt> : transmit EOF by half-closing the TCP connection without using <tt>close_notify</tt>. This is the default. </li> + <li> <tt>-J</tt> : make <a href="s6-tlsc-io.html">s6-tlsc-io</a> +exit with a nonzero code if the peer sends EOF without a close_notify first </li> + <li> <tt>-j</tt> : treat EOF from the peer as a normal exit condition </li> <li> <tt>-Y</tt> : Do not send a client certificate. This is the default. </li> <li> <tt>-y</tt> : Send a client certificate. </li> <li> <tt>-k <em>servername</em></tt> : use Server Name diff --git a/doc/s6-ucspitlsd.html b/doc/s6-ucspitlsd.html index 8488942..314cc39 100644 --- a/doc/s6-ucspitlsd.html +++ b/doc/s6-ucspitlsd.html @@ -36,7 +36,7 @@ TLS stack in the server itself. <h2> Interface </h2> <pre> - s6-ucspitlsd [ -S | -s ] [ -Y | -y ] [ -Z | -z ] [ -v <em>verbosity</em> ] [ -K kimeout ] [ -k snilevel ] [ -- ] <em>prog...</em> + s6-ucspitlsd [ -S | -s ] [ -J | -j ] [ -Y | -y ] [ -Z | -z ] [ -v <em>verbosity</em> ] [ -K kimeout ] [ -k snilevel ] [ -- ] <em>prog...</em> </pre> <ul> @@ -146,6 +146,9 @@ before execing <em>prog...</em>. This is the default. </li> and break the connection when <em>prog</em> sends EOF. </li> <li> <tt>-s</tt> : transmit EOF by half-closing the TCP connection without using <tt>close_notify</tt>. This is the default. </li> + <li> <tt>-J</tt> : make <a href="s6-tlsd-io.html">s6-tlsd-io</a> +exit with a nonzero code if the peer sends EOF without a close_notify first </li> + <li> <tt>-j</tt> : treat EOF from the peer as a normal exit condition </li> <li> <tt>-Y</tt> : Request an optional client certificate. </li> <li> <tt>-y</tt> : Request a mandatory client certificate. The default, with neither the <tt>-Y</tt> nor the <tt>-y</tt> option, diff --git a/src/include/s6-networking/stls.h b/src/include/s6-networking/stls.h index e9f6136..2eefe36 100644 --- a/src/include/s6-networking/stls.h +++ b/src/include/s6-networking/stls.h @@ -8,15 +8,16 @@ #include <tls.h> #include <skalibs/gccattributes.h> +#include <skalibs/buffer.h> #include <skalibs/tai.h> -#define STLS_BUFSIZE (16384 + 325 + 1) +#define STLS_BUFSIZE 16384 /* Engine */ extern int stls_send_environment (struct tls *, int) ; -extern void stls_run (struct tls *, int *, uint32_t, unsigned int) gccattr_noreturn ; +extern void stls_run (struct tls *, int const *, uint32_t, unsigned int) gccattr_noreturn ; /* s6-tlsc-io and s6-tlsd-io */ diff --git a/src/sbearssl/sbearssl_run.c b/src/sbearssl/sbearssl_run.c index 51263ca..d339b69 100644 --- a/src/sbearssl/sbearssl_run.c +++ b/src/sbearssl/sbearssl_run.c @@ -68,14 +68,9 @@ void sbearssl_run (br_ssl_engine_context *ctx, int const *fds, tain const *tto, } else x[0].events = 0 ; - if (x[1].fd >= 0) - x[1].events = IOPAUSE_EXCEPT | (state & BR_SSL_RECVAPP ? IOPAUSE_WRITE : 0) ; - - if (x[2].fd >= 0 && state & BR_SSL_RECVREC) x[2].events = IOPAUSE_READ ; - else x[2].events = 0 ; - - if (x[3].fd >= 0) - x[3].events = IOPAUSE_EXCEPT | (state & BR_SSL_SENDREC ? IOPAUSE_WRITE : 0) ; + x[1].events = x[1].fd >= 0 ? IOPAUSE_EXCEPT | (state & BR_SSL_RECVAPP ? IOPAUSE_WRITE : 0) : 0 ; + x[2].events = x[2].fd >= 0 && state & BR_SSL_RECVREC) ? IOPAUSE_READ : 0 ; + x[3].events = x[3].fd >= 0 ? IOPAUSE_EXCEPT | (state & BR_SSL_SENDREC ? IOPAUSE_WRITE : 0) : 0 ; /* Wait for events */ @@ -187,6 +182,8 @@ void sbearssl_run (br_ssl_engine_context *ctx, int const *fds, tain const *tto, } else if (!r) { + if (handshake_done && options & 2) + strerr_dief1x(98, "remote closed connection without a close_notify") ; fd_shutdown(x[2].fd, 0) ; fd_close(x[2].fd) ; x[2].fd = -1 ; diff --git a/src/stls/stls_client_init_and_handshake.c b/src/stls/stls_client_init_and_handshake.c index b65c833..6f0e248 100644 --- a/src/stls/stls_client_init_and_handshake.c +++ b/src/stls/stls_client_init_and_handshake.c @@ -50,7 +50,7 @@ struct tls *stls_client_init_and_handshake (int const *fds, tain const *tto, uin { if (tls_config_set_ca_path(cfg, x) < 0) diecfg(cfg, "tls_config_set_ca_path") ; - strerr_warnw1x("some versions of libtls do not work with CADIR, try using CAFILE instead") ; + strerr_warnw1x("some versions of libtls do not work with CADIR, if you experience problems with server certificate verification then try using CAFILE instead") ; } else strerr_diefu1x(100, "get trust anchor list: neither CADIR nor CAFILE is set") ; } @@ -73,7 +73,7 @@ struct tls *stls_client_init_and_handshake (int const *fds, tain const *tto, uin if (!ctx) strerr_diefu1sys(111, "tls_client") ; if (tls_configure(ctx, cfg) < 0) diectx(97, ctx, "tls_configure") ; - if (tls_connect_fds(ctx, fds[0], fds[1], servername) < 0) + if (tls_connect_fds(ctx, fds[0], fds[1], servername) == -1) diectx(97, ctx, "tls_connect_fds") ; tls_config_free(cfg) ; stls_handshake(ctx, tto) ; diff --git a/src/stls/stls_run.c b/src/stls/stls_run.c index 7385c4e..2456e22 100644 --- a/src/stls/stls_run.c +++ b/src/stls/stls_run.c @@ -1,6 +1,7 @@ /* ISC license. */ #include <sys/uio.h> +#include <stdint.h> #include <errno.h> #include <unistd.h> @@ -14,248 +15,259 @@ #include <s6-networking/stls.h> -typedef struct tlsbuf_s tlsbuf_t, *tlsbuf_t_ref ; -struct tlsbuf_s + +typedef struct stls_buffer_s stls_buffer, *stls_buffer_ref ; +struct stls_buffer_s { buffer b ; - unsigned char blockedonother : 1 ; char buf[STLS_BUFSIZE] ; + uint8_t flags ; /* 0x1: flush/fill wants opposite IO; 0x2: close_notify initiated */ } ; -static inline int buffer_tls_flush (struct tls *ctx, tlsbuf_t *b) + + /* + We need access to the state field of struct tls, which is private. + So we fake enough stuff that we get the correct field offset. + */ + +#define TLS_EOF_NO_CLOSE_NOTIFY 1 + +struct fake_tls_error_s { - struct iovec v[2] ; - ssize_t r, w ; - buffer_rpeek(&b[0].b, v) ; - r = tls_write(ctx, v[0].iov_base, v[0].iov_len) ; - switch (r) - { - case -1 : return -1 ; - case TLS_WANT_POLLIN : - if (b[1].blockedonother) strerr_dief1x(101, "TLS deadlock") ; - b[0].blockedonother = 1 ; - case TLS_WANT_POLLOUT : return 0 ; - default : break ; - } - w = r ; - if ((size_t)w == v[0].iov_len && v[1].iov_len) + char *msg ; + int num ; + int tls ; +} ; + +struct fake_tls_s +{ + void *config ; + void *keypair ; + struct fake_tls_error_s error ; + uint32_t flags ; + uint32_t state ; +} ; + + /* All because there's no accessor for this in the official libtls API: */ + +static inline int tls_eof_got_close_notify (struct tls *ctx) +{ + return !(((struct fake_tls_s *)ctx)->state & TLS_EOF_NO_CLOSE_NOTIFY) ; +} + + /* We want tls_read/write to behave l */ + +static int tls_allwrite (struct tls *ctx, char const *s, size_t len, size_t *w) +{ + while (*w < len) { - r = tls_write(ctx, v[1].iov_base, v[1].iov_len) ; + ssize_t r = tls_write(ctx, s + *w, len - *w) ; switch (r) { - case TLS_WANT_POLLIN : - if (b[1].blockedonother) strerr_dief1x(101, "TLS deadlock") ; - b[0].blockedonother = 1 ; - case -1 : - case TLS_WANT_POLLOUT : - buffer_rseek(&b[0].b, w) ; - return 0 ; + case -1 : strerr_diefu2x(98, "tls_write: ", tls_error(ctx)) ; + case TLS_WANT_POLLIN : return 1 ; + case TLS_WANT_POLLOUT : return 0 ; default : break ; } - w += r ; + *w += r ; } - buffer_rseek(&b[0].b, w) ; - return 1 ; + return 0 ; } -static inline int buffer_tls_fill (struct tls *ctx, tlsbuf_t *b) +static void tls_flush (struct tls *ctx, stls_buffer *b) { struct iovec v[2] ; - ssize_t r, w ; - int ok = 1 ; - buffer_wpeek(&b[1].b, v) ; - r = tls_read(ctx, v[0].iov_base, v[0].iov_len) ; - switch (r) - { - case 0 : return -2 ; - case -1 : return -1 ; - case TLS_WANT_POLLOUT : - if (b[0].blockedonother) strerr_dief1x(101, "TLS deadlock") ; - b[1].blockedonother = 1 ; - case TLS_WANT_POLLIN : return 0 ; - default : break ; - } - w = r ; - if ((size_t)w == v[0].iov_len && v[1].iov_len) + size_t w = 0 ; + int r ; + buffer_rpeek(&b[0].b, v) ; + r = tls_allwrite(ctx, v[0].iov_base, v[0].iov_len, &w) ; + buffer_rseek(&b[0].b, w) ; + if (w < v[0].iov_len || !v[1].iov_len) goto out ; + w = 0 ; + r = tls_allwrite(ctx, v[1].iov_base, v[1].iov_len, &w) ; + buffer_rseek(&b[0].b, w) ; + out: + if (r) b[1].flags |= 1 ; else b[1].flags &= ~1 ; +} + +static int tls_allread (struct tls *ctx, char *s, size_t len, size_t *w) +{ + while (*w < len) { - r = tls_read(ctx, v[1].iov_base, v[1].iov_len) ; + ssize_t r = tls_read(ctx, s + *w, len - *w) ; switch (r) { - case TLS_WANT_POLLOUT : - if (b[0].blockedonother) strerr_dief1x(101, "TLS deadlock") ; - b[1].blockedonother = 1 ; - case -1 : - case TLS_WANT_POLLIN : - buffer_wseek(&b[1].b, w) ; - return 0 ; - case 0 : ok = -1 ; errno = EPIPE ; + case -1 : strerr_diefu2x(98, "tls_read: ", tls_error(ctx)) ; + case 0 : return -1 ; + case TLS_WANT_POLLIN : return 0 ; + case TLS_WANT_POLLOUT : return 1 ; default : break ; } - w += r ; + *w += r ; } - buffer_wseek(&b[1].b, w) ; - return ok ; + return 0 ; } -static void send_closenotify (struct tls *ctx, int const *fds) +static int tls_fill (struct tls *ctx, stls_buffer *b) { - iopause_fd x = { .fd = fds[3], .events = IOPAUSE_WRITE } ; - while (tls_close(ctx) == TLS_WANT_POLLOUT) - iopause_g(&x, 1, 0) ; + struct iovec v[2] ; + size_t w = 0 ; + int r ; + buffer_wpeek(&b[1].b, v) ; + r = tls_allread(ctx, v[0].iov_base, v[0].iov_len, &w) ; + buffer_wseek(&b[1].b, w) ; + if (w < v[0].iov_len || !v[1].iov_len) goto out ; + w = 0 ; + r = tls_allread(ctx, v[1].iov_base, v[1].iov_len, &w) ; + buffer_wseek(&b[1].b, w) ; + out: + if (r == -1) return 1 ; + if (r) b[0].flags |= 1 ; else b[0].flags &= ~1 ; + return 0 ; } -static void closeit (struct tls *ctx, int *fds, int closenotify) +static int tls_close_nb (struct tls *ctx, stls_buffer *b) { - if (!closenotify) fd_shutdown(fds[3], 1) ; - else if (fds[2] >= 0) send_closenotify(ctx, fds) ; - fd_close(fds[3]) ; fds[3] = -1 ; + switch (tls_close(ctx)) + { + case 0 : b[0].flags &= ~2 ; b[1].flags &= ~2 ; b[1].flags |= 4 ; return 1 ; + case TLS_WANT_POLLIN : b[0].flags &= ~2 ; b[1].flags |= 2 ; break ; + case TLS_WANT_POLLOUT : b[0].flags |= 2 ; b[1].flags &= ~2 ; break ; + default : strerr_diefu2x(98, "tls_close: ", tls_error(ctx)) ; + } + return 0 ; } -void stls_run (struct tls *ctx, int *fds, uint32_t options, unsigned int verbosity) + /* The engine. */ + +void stls_run (struct tls *ctx, int const *fds, uint32_t options, unsigned int verbosity) { - tlsbuf_t b[2] = { { .blockedonother = 0 }, { .blockedonother = 0 } } ; - iopause_fd x[4] ; - unsigned int xindex[4] ; - - if (ndelay_on(fds[0]) < 0 - || ndelay_on(fds[1]) < 0 - || ndelay_on(fds[2]) < 0 - || ndelay_on(fds[3]) < 0) + stls_buffer b[2] = + { + { .b = BUFFER_INIT(&buffer_read, fds[0], b[0].buf, STLS_BUFSIZE), .flags = 0 }, + { .b = BUFFER_INIT(&buffer_write, fds[1], b[1].buf, STLS_BUFSIZE), .flags = 0 }, + } ; + iopause_fd x[4] = { { .fd = fds[0] }, { .fd = fds[1] }, { .fd = fds[2] }, { .fd = fds[3] } } ; + + if (ndelay_on(x[0].fd) == -1 + || ndelay_on(x[1].fd) == -1 + || ndelay_on(x[2].fd) == -1 + || ndelay_on(x[3].fd) == -1) strerr_diefu1sys(111, "set fds non-blocking") ; - buffer_init(&b[0].b, &buffer_read, fds[0], b[0].buf, STLS_BUFSIZE) ; - buffer_init(&b[1].b, &buffer_write, fds[1], b[1].buf, STLS_BUFSIZE) ; - - for (;;) + while (x[0].fd >= 0 || x[1].fd >= 0 || x[3].fd >= 0) { - unsigned int j = 0 ; - int r ; - - - /* poll() preparation */ - - if (fds[0] >= 0 && buffer_isreadable(&b[0].b)) - { - x[j].fd = fds[0] ; - x[j].events = IOPAUSE_READ ; - xindex[0] = j++ ; - } - else xindex[0] = 4 ; - - if (fds[1] >= 0 && buffer_iswritable(&b[1].b)) - { - x[j].fd = fds[1] ; - x[j].events = IOPAUSE_WRITE ; - xindex[1] = j++ ; - } - else xindex[1] = 4 ; - - if (fds[2] >= 0 && !b[1].blockedonother && buffer_isreadable(&b[1].b)) - { - x[j].fd = fds[2] ; - x[j].events = IOPAUSE_READ ; - xindex[2] = j++ ; - } - else xindex[2] = 4 ; - - if (fds[3] >= 0 && !b[0].blockedonother && buffer_iswritable(&b[0].b)) - { - x[j].fd = fds[3] ; - x[j].events = IOPAUSE_WRITE ; - xindex[3] = j++ ; - } - else xindex[3] = 4 ; - - if (xindex[0] == 4 && xindex[1] == 4 && xindex[3] == 4) break ; + x[0].events = x[0].fd >= 0 && buffer_isreadable(&b[0].b) ? IOPAUSE_READ : 0 ; + x[1].events = x[1].fd >= 0 && buffer_iswritable(&b[1].b) ? IOPAUSE_WRITE : 0 ; + x[2].events = x[2].fd >= 0 && (buffer_isreadable(&b[1].b) || (b[1].flags & 1 && buffer_iswritable(&b[0].b))) ? IOPAUSE_READ : 0 ; + x[3].events = x[3].fd >= 0 && (buffer_iswritable(&b[0].b) || (b[0].flags & 1 && buffer_isreadable(&b[1].b))) ? IOPAUSE_WRITE : 0 ; - /* poll() */ - - r = iopause_g(x, j, 0) ; - if (r < 0) strerr_diefu1sys(111, "iopause") ; - else if (!r) break ; - - while (j--) - if (x[j].revents & IOPAUSE_EXCEPT) - x[j].revents |= IOPAUSE_READ | IOPAUSE_WRITE ; - + if (iopause_g(x, 4, 0) == -1) strerr_diefu1sys(111, "iopause") ; /* Flush to local */ - if (xindex[1] < 4 && x[xindex[1]].revents & IOPAUSE_WRITE) + if (x[1].revents) { - r = buffer_flush(&b[1].b) ; - if (!r && !error_isagain(errno)) + if (!buffer_flush(&b[1].b)) { - strerr_warnwu1sys("write to application") ; - if (fds[2] >= 0) - { - if (options & 1) fd_shutdown(fds[2], 0) ; - fd_close(fds[2]) ; fds[2] = -1 ; - xindex[2] = 4 ; - } - r = 1 ; + if (!error_isagain(errno)) strerr_diefu1sys(111, "write to local") ; } - if (r && fds[2] < 0) + else if (x[2].fd == -1) { - fd_close(fds[1]) ; fds[1] = -1 ; + fd_close(x[1].fd) ; + x[1].fd = -1 ; } } - /* Flush to remote */ + /* Flush to remote: do everything that had TLS_WANT_POLLOUT */ - if (xindex[3] < 4 && x[xindex[3]].revents & IOPAUSE_WRITE) + if (x[3].revents) { - r = buffer_tls_flush(ctx, b) ; - if (r < 0) + if (buffer_len(&b[0].b)) tls_flush(ctx, b) ; /* normal write */ + if ((b[0].flags & 1 && tls_fill(ctx, b)) /* peer sent close_notify and it just completed */ + || (b[0].flags & 2 && tls_close_nb(ctx, b))) /* we send close_notify and it instantly succeeds */ { - strerr_warnwu2x("write to peer: ", tls_error(ctx)) ; - fd_close(fds[0]) ; fds[0] = -1 ; - xindex[0] = 4 ; + if (buffer_isempty(&b[1].b)) break ; + fd_close(x[3].fd) ; x[3].fd = -1 ; + fd_close(x[2].fd) ; x[2].fd = -1 ; + if (x[0].fd >= 0) { fd_close(x[0].fd) ; x[0].fd = -1 ; } + continue ; + } + if (x[0].fd == -1 && buffer_isempty(&b[0].b)) + { + if (!(options & 1) || tls_close_nb(ctx, b)) + { + fd_shutdown(x[3].fd, 1) ; + fd_close(x[3].fd) ; + x[3].fd = -1 ; + } } - if (r && fds[0] < 0) - closeit(ctx, fds, options & 1) ; } /* Fill from local */ - if (xindex[0] < 4 && x[xindex[0]].revents & IOPAUSE_READ) + if (x[0].revents) { - r = sanitize_read(buffer_fill(&b[0].b)) ; - if (r < 0) + ssize_t r = buffer_fill(&b[0].b) ; + if (r == -1 && !error_isagain(errno)) + strerr_diefu1sys(111, "read from local") ; + else if (!r) { - if (errno != EPIPE) strerr_warnwu1sys("read from application") ; - fd_close(fds[0]) ; fds[0] = -1 ; + fd_close(x[0].fd) ; + x[0].fd = -1 ; if (buffer_isempty(&b[0].b)) - closeit(ctx, fds, options & 1) ; + { + if (!(options & 1) || tls_close_nb(ctx, b)) + { + fd_shutdown(x[3].fd, 1) ; + fd_close(x[3].fd) ; + x[3].fd = -1 ; + } + } } } - /* Fill from remote */ + /* Fill from remote: do everything that had TLS_WANT_POLLIN */ - if (xindex[2] < 4 && x[xindex[2]].revents & IOPAUSE_READ) + if (x[2].revents) { - r = buffer_tls_fill(ctx, b) ; - if (r < 0) - { - if (r == -1) strerr_warnwu2x("read from peer: ", tls_error(ctx)) ; - if (!(options & 1)) fd_shutdown(fds[2], 0) ; - /* - XXX: We need a way to detect when we've received a close_notify, - because then we need to trigger a write and then shut the engine - down. This is orthogonal to options&1, it only means that the - peer sent a close_notify. - As for now, libtls doesn't offer an API to detect that, so we - do nothing special - we just wait until our app sends EOF. - */ - fd_close(fds[2]) ; fds[2] = -1 ; + if (buffer_isreadable(&b[1].b) && tls_fill(ctx, b)) + { /* connection closed */ + fd_shutdown(x[2].fd, 0) ; + fd_close(x[2].fd) ; + x[2].fd = -1 ; if (buffer_isempty(&b[1].b)) { - fd_close(fds[1]) ; fds[1] = -1 ; + if (tls_eof_got_close_notify(ctx)) break ; + fd_close(x[1].fd) ; + x[1].fd = -1 ; + } + if (options & 2) + { + if (!tls_eof_got_close_notify(ctx)) + strerr_dief1x(98, "remote closed connection without a close_notify") ; + else if (x[3].fd >= 0) + { + fd_shutdown(x[3].fd, 1) ; + fd_close(x[3].fd) ; + x[3].fd = -1 ; + } + } + } + else + { /* normal case */ + if (b[1].flags & 1) tls_flush(ctx, b) ; + if (b[1].flags & 2 && tls_close_nb(ctx, b)) + { + if (buffer_isempty(&b[1].b)) break ; + if (x[3].fd >= 0) { fd_close(x[3].fd) ; x[3].fd = -1 ; } + if (x[0].fd >= 0) { fd_close(x[0].fd) ; x[0].fd = -1 ; } + fd_close(x[2].fd) ; x[2].fd = -1 ; } } } diff --git a/src/stls/stls_server_init_and_handshake.c b/src/stls/stls_server_init_and_handshake.c index 2304598..2a58885 100644 --- a/src/stls/stls_server_init_and_handshake.c +++ b/src/stls/stls_server_init_and_handshake.c @@ -86,6 +86,7 @@ struct tls *stls_server_init_and_handshake (int const *fds, tain const *tto, uin { if (tls_config_set_ca_path(cfg, x) < 0) diecfg(cfg, "tls_config_set_ca_path") ; + strerr_warnw1x("some versions of libtls do not work with CADIR, if you experience problems with client certificate verification then try using CAFILE instead") ; } else { @@ -109,7 +110,7 @@ struct tls *stls_server_init_and_handshake (int const *fds, tain const *tto, uin if (!sctx) strerr_diefu1sys(111, "tls_server") ; if (tls_configure(sctx, cfg) < 0) diectx(97, sctx, "tls_configure") ; tls_config_free(cfg) ; - if (tls_accept_fds(sctx, &ctx, fds[0], fds[1]) < 0) + if (tls_accept_fds(sctx, &ctx, fds[0], fds[1]) == -1) diectx(97, sctx, "tls_accept_fds") ; /* We can't free sctx, ctx has pointers into it! Stupid API. We let sctx leak. */ /* tls_free(sctx) ; */ diff --git a/src/tls/s6-tlsc-io.c b/src/tls/s6-tlsc-io.c index e64c014..25347b7 100644 --- a/src/tls/s6-tlsc-io.c +++ b/src/tls/s6-tlsc-io.c @@ -13,7 +13,7 @@ #include <s6-networking/config.h> -#define USAGE "s6-tlsc-io [ -v verbosity ] [ -d notif ] [ -S | -s ] [ -Y | -y ] [ -K timeout ] [ -k servername ] fdr fdw" +#define USAGE "s6-tlsc-io [ -v verbosity ] [ -d notif ] [ -S | -s ] [ -J | -j ] [ -Y | -y ] [ -K timeout ] [ -k servername ] fdr fdw" #define dieusage() strerr_dieusage(100, USAGE) static inline void doit (int *, tain const *tto, uint32_t, uint32_t, unsigned int, char const *, unsigned int) gccattr_noreturn ; @@ -81,7 +81,7 @@ int main (int argc, char const *const *argv, char const *const *envp) unsigned int t = 0 ; for (;;) { - int opt = subgetopt_r(argc, argv, "d:SsYyv:K:k:", &l) ; + int opt = subgetopt_r(argc, argv, "d:SsJjYyv:K:k:", &l) ; if (opt == -1) break ; switch (opt) { @@ -89,8 +89,10 @@ int main (int argc, char const *const *argv, char const *const *envp) case 'd' : if (!uint0_scan(l.arg, ¬if)) dieusage() ; break ; case 'S' : options |= 1 ; break ; case 's' : options &= ~1 ; break ; - case 'Y' : preoptions &= ~1 ; break ; + case 'J' : options |= 2 ; break ; + case 'j' : options &= ~2 ; break ; case 'y' : preoptions |= 1 ; break ; + case 'Y' : preoptions &= ~1 ; break ; case 'K' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; case 'k' : servername = l.arg ; break ; default : dieusage() ; diff --git a/src/tls/s6-tlsc.c b/src/tls/s6-tlsc.c index d348599..ebea264 100644 --- a/src/tls/s6-tlsc.c +++ b/src/tls/s6-tlsc.c @@ -11,7 +11,7 @@ #include "s6tls-internal.h" -#define USAGE "s6-tlsc [ -S | -s ] [ -Y | -y ] [ -v verbosity ] [ -K timeout ] [ -k servername ] [ -Z | -z ] [ -6 fdr ] [ -7 fdw ] prog..." +#define USAGE "s6-tlsc [ -S | -s ] [ -J | -j ] [ -Y | -y ] [ -v verbosity ] [ -K timeout ] [ -k servername ] [ -Z | -z ] [ -6 fdr ] [ -7 fdw ] prog..." #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) @@ -30,14 +30,16 @@ int main (int argc, char const *const *argv) subgetopt l = SUBGETOPT_ZERO ; for (;;) { - int opt = subgetopt_r(argc, argv, "SsYyv:K:k:Zz6:7:", &l) ; + int opt = subgetopt_r(argc, argv, "SsJjyYv:K:k:Zz6:7:", &l) ; if (opt == -1) break ; switch (opt) { - case 'S' : coptions &= ~4 ; break ; - case 's' : coptions |= 4 ; break ; - case 'Y' : coptions &= ~1 ; break ; + case 'S' : coptions |= 4 ; break ; + case 's' : coptions &= ~4 ; break ; + case 'J' : coptions |= 2 ; break ; + case 'j' : coptions &= ~2 ; break ; case 'y' : coptions |= 1 ; break ; + case 'Y' : coptions &= ~1 ; break ; case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; case 'K' : if (!uint0_scan(l.arg, &kimeout)) dieusage() ; break ; case 'k' : servername = l.arg ; break ; diff --git a/src/tls/s6-tlsd-io.c b/src/tls/s6-tlsd-io.c index fac2164..08eee77 100644 --- a/src/tls/s6-tlsd-io.c +++ b/src/tls/s6-tlsd-io.c @@ -13,7 +13,7 @@ #include <s6-networking/config.h> -#define USAGE "s6-tlsd-io [ -v verbosity ] [ -d notif ] [ -S | -s ] [ -Y | -y ] [ -K timeout ] [ -k snilevel ] fdr fdw" +#define USAGE "s6-tlsd-io [ -v verbosity ] [ -d notif ] [ -S | -s ] [ -J | -j ] [ -Y | -y ] [ -K timeout ] [ -k snilevel ] fdr fdw" #define dieusage() strerr_dieusage(100, USAGE) static inline void doit (int *, tain const *tto, uint32_t, uint32_t, unsigned int, unsigned int) gccattr_noreturn ; @@ -78,7 +78,7 @@ int main (int argc, char const *const *argv) unsigned int t = 0 ; for (;;) { - int opt = subgetopt_r(argc, argv, "d:SsYyv:K:k:", &l) ; + int opt = subgetopt_r(argc, argv, "d:SsJjYyv:K:k:", &l) ; if (opt == -1) break ; switch (opt) { @@ -86,6 +86,8 @@ int main (int argc, char const *const *argv) case 'd' : if (!uint0_scan(l.arg, ¬if)) dieusage() ; break ; case 'S' : options |= 1 ; break ; case 's' : options &= ~1 ; break ; + case 'J' : options |= 2 ; break ; + case 'j' : options &= ~2 ; break ; case 'Y' : preoptions |= 1 ; preoptions &= ~2 ; break ; case 'y' : preoptions |= 3 ; break ; case 'K' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; diff --git a/src/tls/s6-tlsd.c b/src/tls/s6-tlsd.c index 85c0d9f..acd2945 100644 --- a/src/tls/s6-tlsd.c +++ b/src/tls/s6-tlsd.c @@ -9,7 +9,7 @@ #include "s6tls-internal.h" -#define USAGE "s6-tlsd [ -S | -s ] [ -Y | -y ] [ -k snilevel ] [ -v verbosity ] [ -K timeout ] [ -Z | -z ] prog..." +#define USAGE "s6-tlsd [ -S | -s ] [ -J | -j ] [ -Y | -y ] [ -k snilevel ] [ -v verbosity ] [ -K timeout ] [ -Z | -z ] prog..." #define dieusage() strerr_dieusage(100, USAGE) int main (int argc, char const *const *argv) @@ -28,14 +28,16 @@ int main (int argc, char const *const *argv) subgetopt l = SUBGETOPT_ZERO ; for (;;) { - int opt = subgetopt_r(argc, argv, "SsYyv:K:Zzk:", &l) ; + int opt = subgetopt_r(argc, argv, "SsJjyYv:K:Zzk:", &l) ; if (opt == -1) break ; switch (opt) { case 'S' : coptions |= 4 ; break ; case 's' : coptions &= ~4 ; break ; - case 'Y' : coptions |= 1 ; coptions &= ~2 ; break ; + case 'J' : coptions |= 8 ; break ; + case 'j' : coptions &= ~8 ; break ; case 'y' : coptions |= 3 ; break ; + case 'Y' : coptions |= 1 ; coptions &= ~2 ; break ; case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; case 'K' : if (!uint0_scan(l.arg, &kimeout)) dieusage() ; break ; case 'Z' : poptions &= ~1 ; break ; diff --git a/src/tls/s6-ucspitlsc.c b/src/tls/s6-ucspitlsc.c index bb1a2dc..cd29324 100644 --- a/src/tls/s6-ucspitlsc.c +++ b/src/tls/s6-ucspitlsc.c @@ -15,7 +15,7 @@ #include <s6-networking/config.h> #include "s6tls-internal.h" -#define USAGE "s6-ucspitlsc [ -S | -s ] [ -Y | -y ] [ -v verbosity ] [ -K timeout ] [ -Z | -z ] [ -k servername ] [ -6 fdr ] [ -7 fdw ] prog..." +#define USAGE "s6-ucspitlsc [ -S | -s ] [ -J | -j ] [ -Y | -y ] [ -v verbosity ] [ -K timeout ] [ -Z | -z ] [ -k servername ] [ -6 fdr ] [ -7 fdw ] prog..." #define dieusage() strerr_dieusage(100, USAGE) static inline void child (int *, uint32_t, unsigned int, unsigned int, char const *, pid_t) gccattr_noreturn ; @@ -80,14 +80,16 @@ int main (int argc, char const *const *argv, char const *const *envp) subgetopt l = SUBGETOPT_ZERO ; for (;;) { - int opt = subgetopt_r(argc, argv, "SsYyv:K:Zzk:6:7:", &l) ; + int opt = subgetopt_r(argc, argv, "SsJjyYv:K:Zzk:6:7:", &l) ; if (opt == -1) break ; switch (opt) { case 'S' : coptions |= 4 ; break ; case 's' : coptions &= ~4 ; break ; - case 'Y' : coptions |= 1 ; coptions &= ~2 ; break ; - case 'y' : coptions |= 3 ; break ; + case 'J' : coptions |= 2 ; break ; + case 'j' : coptions &= ~2 ; break ; + case 'y' : coptions |= 1 ; break ; + case 'Y' : coptions &= ~1 ; break ; case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; case 'K' : if (!uint0_scan(l.arg, &kimeout)) dieusage() ; break ; case 'Z' : poptions &= ~1 ; break ; diff --git a/src/tls/s6-ucspitlsd.c b/src/tls/s6-ucspitlsd.c index fc0868a..d2b9580 100644 --- a/src/tls/s6-ucspitlsd.c +++ b/src/tls/s6-ucspitlsd.c @@ -14,7 +14,7 @@ #include <s6-networking/config.h> #include "s6tls-internal.h" -#define USAGE "s6-ucspitlsd [ -S | -s ] [ -Y | -y ] [ -k snilevel ] [ -v verbosity ] [ -K timeout ] [ -Z | -z ] prog..." +#define USAGE "s6-ucspitlsd [ -S | -s ] [ -J | -j ] [ -Y | -y ] [ -k snilevel ] [ -v verbosity ] [ -K timeout ] [ -Z | -z ] prog..." #define dieusage() strerr_dieusage(100, USAGE) static inline void child (int *, uint32_t, unsigned int, unsigned int, unsigned int, pid_t) gccattr_noreturn ; @@ -77,14 +77,16 @@ int main (int argc, char const *const *argv) subgetopt l = SUBGETOPT_ZERO ; for (;;) { - int opt = subgetopt_r(argc, argv, "SsYyv:K:Zzk:", &l) ; + int opt = subgetopt_r(argc, argv, "SsJjyYv:K:Zzk:", &l) ; if (opt == -1) break ; switch (opt) { case 'S' : coptions |= 4 ; break ; case 's' : coptions &= ~4 ; break ; - case 'Y' : coptions |= 1 ; coptions &= ~2 ; break ; + case 'J' : coptions |= 8 ; break ; + case 'j' : coptions &= ~8 ; break ; case 'y' : coptions |= 3 ; break ; + case 'Y' : coptions |= 1 ; coptions &= ~2 ; break ; case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; case 'K' : if (!uint0_scan(l.arg, &kimeout)) dieusage() ; break ; case 'Z' : poptions &= ~1 ; break ; diff --git a/src/tls/s6tls-internal.h b/src/tls/s6tls-internal.h index fdd247f..1ab77f6 100644 --- a/src/tls/s6tls-internal.h +++ b/src/tls/s6tls-internal.h @@ -9,7 +9,7 @@ #include <skalibs/gccattributes.h> #include <skalibs/types.h> -#define S6TLS_PREP_IO_ARGC 15 +#define S6TLS_PREP_IO_ARGC 16 #define S6TLS_PREP_IO_BUFLEN (5 * UINT_FMT) extern pid_t s6tls_io_spawn (char const *const *argv, int const *, int) ; diff --git a/src/tls/s6tls_prep_tlscio.c b/src/tls/s6tls_prep_tlscio.c index a3f33b0..0b7ff1f 100644 --- a/src/tls/s6tls_prep_tlscio.c +++ b/src/tls/s6tls_prep_tlscio.c @@ -26,6 +26,7 @@ void s6tls_prep_tlscio (char const **argv, char *buf, int const *p, uint32_t opt buf[n++] = 0 ; } argv[m++] = options & 4 ? "-S" : "-s" ; + argv[m++] = options & 2 ? "-J" : "-j" ; argv[m++] = options & 1 ? "-y" : "-Y" ; if (kimeout) { diff --git a/src/tls/s6tls_prep_tlsdio.c b/src/tls/s6tls_prep_tlsdio.c index 73099bf..59cc536 100644 --- a/src/tls/s6tls_prep_tlsdio.c +++ b/src/tls/s6tls_prep_tlsdio.c @@ -26,6 +26,7 @@ void s6tls_prep_tlsdio (char const **argv, char *buf, int const *p, uint32_t opt buf[n++] = 0 ; } argv[m++] = options & 4 ? "-S" : "-s" ; + argv[m++] = options & 8 ? "-J" : "-j" ; if (options & 1) argv[m++] = options & 2 ? "-y" : "-Y" ; if (kimeout) |