Bug#914034: Bug#911938: libhttp-daemon-ssl-perl FTBFS: tests fail: Connection refused
Guilhem Moulin
guilhem at debian.org
Mon May 13 14:18:14 BST 2019
On Mon, 13 May 2019 at 06:31:26 +0200, Steffen Ullrich wrote:
> Applications which relied on blocking I/O in connection with select could
> also hang before,
Uh, what? “Before” meaning with ≤TLSv1.2, or with OpenSSL <1.1.1a's
default flags? libssl mentions no such thing beside the new default
mode. And in fact the s_client() program, *from OpenSSL upstream
itself*, does precisely that: a select loop in blocking I/O mode (unless
the ‘-nbio’ flag is set).
https://github.com/openssl/openssl/blob/OpenSSL_1_1_1/apps/s_client.c#L2817
s_client() is roughly speaking a C version the ‘netcat.pl’ prototype I
attached earlier. Unsurprisingly, since 1.1.1a the code clears
SSL_MODE_AUTO_RETRY from the bitmask mode of the newly created SSL_CTX
object. That change was even done in the very same commit that enabled
SSL_MODE_AUTO_RETRY by default :-)
https://github.com/openssl/openssl/commit/693cf80c6ff54ae276a44d305d4ad07168ec6895#diff-7f3b79983f6d53c047c90a62813cc11f
IMHO using polling sockets in blocking I/O is fairly common. Granted,
that itself itself doesn't say much about code quality, but the fact that
the OpenSSL project *does* use it (and explicitly mentions it in the
docs) gives me confidence that it's a fine use which should still be
supported.
> only this problem is worse now. Before TLS 1.3 these applications
> could hang if the peer initiated a renegotiation since this were TCP
> level data without any SSL application payload, i.e. select was
> triggered but sysread would not return with data.
I feel that we're talking past each other :-/ select() being triggered
means indeed that a subsequent read() system call *won't block*;
however read() is not called directly, but through SSL_read(). Whether
SSL_read() will block until application data is received, or not, is
controlled with the SSL_MODE_AUTO_RETRY flag. If that flag is unset,
then SSL_read() returns immediately with failure and sets SSL_ERROR.
For an IO::Socket::SSL object, $sock->sysread() is AFAICT a mere wrapper
for SSL_read():
https://github.com/noxxi/p5-io-socket-ssl/blob/2.066/lib/IO/Socket/SSL.pm#L1187
So if select() is triggered because the TLS session is renegotiated, but
no application data was received, sysread will block iff. SSL_MODE_AUTO_RETRY
is set. As I showed in the traces enclosed in my previous message.
> Additionally switching off SSL_MODE_AUTO_RETRY would actually just add a
> different unexpected behavior: that sysread might return with EAGAIN on a
> blocking socket. This is not the behavior one expects from a blocking
> socket, i.e. it should block until it returns data, should return no data
> only on connection shutdown or should fail permanently.
The documentation (libssl's SSL_read()'s [0], Net::SSLeay::read() [1],
as well as IO::Socket::SSL::sysread() [2]) all warn that reading from an
SSL socket has a different behavior than usual read(2) system calls, in
that read failures should be treated with care, as there are both
retryable (eg. when no application data was received) and non-retryable
errors.
So from my perspective, the expectation that IO::Socket::SSL::sysread()
behaves like a “normal” sysread doesn't hold, and never did. (There is
even an entry for “Expecting exactly the same behavior as plain
sockets” in the “Common errors” section of IO::Socket::SSL's manpage!)
For what it's worth I think it's a shame that SSL_MODE_AUTO_RETRY was
not the default earlier, as it's convenient to be able to write
$sock->sysread($buf, $len) // die;
and not bother about inspecting $SSL_ERROR. But programs have been
written with the old default in mind, and the fact that SSL_read()
doesn't behave like a normal read() system call. For these programs to
keep working, there needs to be a way to switch SSL_MODE_AUTO_RETRY off.
libssl provides SSL_CTX_clear_mode(), Net::SSLeay has
CTX_ctrl(,78,SSL_CTRL_CLEAR_MODE,0), and I'd like to have something
similar in IO::Socket::SSL, too.
> It was just a coincidence that LWP::protocol::http could deal with
> this situation. And this coincidence came from the fact, that this
> code was actually designed for non-blocking sockets and only the
> Debian patch caused it to use a blocking socket instead.
Yup, I now agree with you as far as LWP is concerned.
> I've added more information regarding this to the IO::Socket::SSL
> documentation:
> https://github.com/noxxi/p5-io-socket-ssl/commit/ee176e489f02bfaaa479fc8d9312c8458bf55565
| A sysread on the IO::Socket::SSL socket will not return any data
| though since it is an abstraction which only returns application data.
| This causes the sysread to hang in case the socket was blocking
I believe that statement is incorrect for OpenSSL <1.1.1a's (or if
SSL_MODE_AUTO_RETRY was toggled via some other means). If you try
netcat.pl with an older OpenSSL release, you won't be able to reproduce
the TLSv1.2 trace I pasted yesterday; the client doesn't end up stuck in
a blocking read :-) If no application data is available, then
IO::Socket::SSL::sysread() hangs in blocking I/O iff. SSL_MODE_AUTO_RETRY
is set.
Cheers,
--
Guilhem.
[0] “If the underlying BIO is blocking, a read function will only return
once the read operation has been finished or an error occurred,
except when a non-application data record has been processed and
SSL_MODE_AUTO_RETRY is not set. Note that if SSL_MODE_AUTO_RETRY is
set and only non-application data is available the call will hang.”
— https://www.openssl.org/docs/man1.1.1/man3/SSL_read.html
[1] “If you need to select(2) on the socket, go right ahead, but be
warned that OpenSSL does some internal buffering so SSL_read does
not always return data even if the socket selected for reading (just
keep on selecting and trying to read). "Net::SSLeay" is no different
from the C language OpenSSL in this respect.”
— https://metacpan.org/pod/Net::SSLeay#Sockets
[2] “Thus, in order to decide if you can read more data (e.g. if sysread
will block) you must check if there are still data in the current
SSL frame by calling pending […]
[…]
Also, calls to sysread might fail, because it must first finish an
SSL handshake.”
— https://metacpan.org/pod/IO::Socket::SSL#sysread
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://alioth-lists.debian.net/pipermail/pkg-perl-maintainers/attachments/20190513/06f9133a/attachment.sig>
More information about the pkg-perl-maintainers
mailing list