Victor Stinner wrote:
I'm trying to fix a bug in Python which is specific to OpenSSL 0.9.8m. The
problem is in a FTP test using a blocking socket (client) and a non blocking
socket (server). There are different tests, some tests use a timeout of 2
seconds on the client socket.
Pseudo-code of Python shutdown low-level function:
err = SSL_shutdown(self->ssl);
if (err == 0)
err = SSL_shutdown(self->ssl);
if (err < 0)
<raise an exception>
else
<ok>
Using OpenSSL 0.9.8m, SSL_shutdown() returns sometimes -1 and SSL_get_error()
gives SSL_ERROR_WANT_READ. If I understood correctly, I have to read some
bytes from the sockets using SSL_read() to make OpenSSL happy. But how many
bytes? And can I read directly bytes or should I ensure that bytes are
available using select() (or anything else)?
The change in behavior was introduced by a patch I submitted to fix a
long standing bug with SSL_shutdown() and the handling of non-blocking
sockets. This did change the behavior since it never used to return
-1/WANT_WRITE or -1/WANT_READ at all but internally mitigate them back
to a zero value.
Please take a look at the following threads for background info on the bug:
http://marc.info/?t=119109061500001&r=1&w=2
http://marc.info/?t=119246586800001&r=1&w=2
Short answer
============
For all intents an purposes to convert older code not expecting to see
these two error returns you simply check for them explicitly and follow
the same execution path in your code as you do for a 0 return value from
SSL_shutdown().
For any other kind of -1 error return you follow the execution path as
you did before, for example your <raise an exception>.
However by not understanding what is going on fully you could get a hung
connection in situations where you shouldn't (unless you implement an
external timeout) if you don't SSL_read() to sink the application data
that may exist in the stream ahead of the inbound end-of-stream notify.
To port older code try the following C code snippet (this code is also
compatible with older versions of the OpenSSL library, so it doesn't
matter if you add this snippet to your application but use an older
version of OpenSSL at runtime) :
int rc = SSL_shutdown(ssl);
/***** BEGIN - INSERT THIS CODE AFTER EVERY SSL_shutdown() INVOCATION IN
YOUR CODE *****/
if(rc == -1) {
int ssl_errno;
SSL_get_error(ssl, ssl_errno);
if(ssl_errno == SSL_ERROR_WANT_READ || ssl_errno ==
SSL_ERROR_WANT_WRITE)
rc = 0;
}
/***** END - INSERT THIS CODE AFTER EVERY SSL_shutdown() INVOCATION IN
YOUR CODE *****/
With this the observable behavior that you got before should be consistent.
This doesn't necessarily mean your code is correctly going a graceful
SSL stream shutdown. For that you need to understand your application
the context you use SSL etc... hence the long answer.
Long answer
===========
* SSL_read() is responsible for reading application data (i.e. the data
that is encrypted for transport and then decrypted)
* Application payload data can only be received while the receiving half
of the SSL stream is still open.
* The other end voluntarily controls weather the receiving half of the
SSL stream is still open. Or to put another way the sending side
controls when the end-of-stream notify is sent to securely close that
half of the stream.
As opposed to dropping the TCP network connection and not being
cryptographically secure stream shutdown; how do you know there wasn't
some other piece of data the sending side sent but some attacker doesn't
want you to receive it ? You securely need to know when the
end-of-stream has been reached in order to be sure the stream has not
been tampered with in anyway.
So the first time you call SSL_shutdown() what you are in effect saying
is, "I have no more data to send to the other side, so I'm going to
write the end-of-stream notify packet into the SSL stream so the other
end knows this."
The next action is then for your side to finish processing all inbound
application data. Now just because you decided you were not going to
send any more data to the other end, this doesn't mean the far end has
finished sending data to you.
So between zero and an infinite amount of application data may still
need to be received and removed from the stream via SSL_read(). It is
possible for whatever reason the far end isn't sending any data now and
is keeping the stream open but has nothing to send right at this moment.
These situations are valid SSL protocol scenarios.
Eventually the far end should finish sending and will then send its own
end-of-stream notify packet for you to receive.
Only once your end receives this notify packet does the OpenSSL API
function SSL_shutdown() return a value of 1.
You must also consider that due to buffering an inbound end-of-stream
packet may not be processed while there is application data still in
transit and not yet removed from buffers with SSL_read().
Only once this zero to infinite amount of data has been removed will the
inbound end-of-stream marker be visible to the inbound SSL protocol
stack to process. Until that has happens and as far as the local end is
concerned the receiving half of the stream is still open for business.
So you ask how much data the local side needs to read. Well this data
might still be valid and relevant to the application so if the library
supports half-open streams it should continue to pass it up the line to
the application.
If however you are implementing a "close" kind of function to attempt to
close the stream completely in a neighborly fashion then you should sink
this data (i.e. use SSL_read to read whatever you see but immediately
discard it, so lets say use a stack based buffer to read it into and
read in somewhere between 512 bytes and 16 Kb at a time).
Everytime you see -1/WANT_READ you put your application to sleep and
wakeup when there is something to read (this is normal non-blocking
sleep/wakeup mechanics for OpensSL). At each wakeup you can loop
SSL_read() multiple times if you wish (probably sane to apply a fixed
upper limit on the number of iterations per wakeup, lets say in the
order of 1 to 100) until no more data is found then you must call
SSL_shutdown() again to check for 1.
If you see the return value 1 then thats it, you can free the OpenSSL
objects and close the underlying BSD socket.
I wrote a patch using a loop:
while 1:
try:
self._sslobj.shutdown()
break
except SSLError as err:
if err.args[0] == SSL_ERROR_WANT_READ:
try:
self.read()
except SSLError as read_err:
if read_err.args[0] == SSL_ERROR_ZERO_RETURN:
# connection closed: done
break
else:
# non blocking socket
raise err
else:
continue
else:
raise
except socket_error as err:
if err.errno == EPIPE:
# connection closed: done
break
else:
raise
The code is written in Python, don't hesitate to ask me if you don't
understand something.
I don't understand why I'm getting SSL_ERROR_ZERO_RETURN or EPIPE errors.
ZERO_RETURN is the end-of-stream return. It is not really an error it
is more an expected natural condition when there is never going to be
any more data to receive. It means you have received the end-of-stream
notify packet from the far end. It is possible for BOTH ends of an SSL
stream to decide to send their end-of-stream notify packets at the same
time. There is no master/slave, as I put before the sending side is in
control of its half of the connection.
EPIPE is because the other end of the SSL connection hung up on you
abruptly (at a BSD socket level). For example some application received
a "QUIT" command from you and the other end just closed the BSD socket.
(without doing any of the graceful SSL stream shutdown). This is not a
"secure stream shutdown" but never the less achieves the same thing.
This is not a neighbourly thing to do but never the less some server
implementations do it. So EPIPE is because OpenSSL called write()
system call to send more data to the BSD socket but the the other end
hung up at a TCP level. The data that it maybe trying to send is your
end-of-stream notify packet.
I tried to call SSL_shutdown() in a loop, but if the first or the second call
returns the SSL_ERROR_WANT_READ error: the next call will always return the
same error (I tried to wait some seconds, but it doesn't change). Does it mean
that SSL_Shutdown() is not compatible between 0.9.8l and 0.9.8m for non
blocking sockets?
I think my C code snippet should address this.
Regards,
Darryl
______________________________________________________________________
OpenSSL Project http://www.openssl.org
User Support Mailing List openssl-users@openssl.org
Automated List Manager majord...@openssl.org