Peter Djalaliev wrote:

> I have a question about what happens when the first SSL handshake on a
> SSL connection tries to gather data from a socket that would block
> (e.g. there is no data tobe read yet).

Peter, Rather than answer your questions about the functions in the code,
one by one, I'm going to try to give you the "big picture".  I'll also
explain about that historic artifact known as "SECWouldBlock".  Maybe the
behavior of the code will make more sense in light of this information.

NSS's libSSL is a "pushable" layer on an NSPR socket.  It modifies the
behavior of an NSPR socket, by adding SSL protocol to it.  With or without
the presence of libSSL, the semantics of an NSPR socket with respect to
blocking/non-blocking behavior are the same.

NSPR sockets are defined to operate as blocking or non-blocking sockets.
The behaviors of these are very similar, but not quite identical, to
unix BSD sockets.

Under the hood, inside of NSPR, NSPR always uses non-blocking sockets from
the underlying OS implementation.  NSPR then implements NSPR socket semantics
on top of the underlying non-blocking socket.  That is, NSPR implements
blocking or non-blocking semantics, as the application has requested, on top
of the underlying non-blocking OS socket.  For the purposes of the
application, and the purposes of NSS, it doesn't matter that NSPR uses
non-blocking sockets underneath.  The layers above NSPR concern themselves
only with the semantic behavior provided by NSPR to its users.  When NSS
refers to a socket as blocking or non-blocking, it is referring to the
NSPR behavior of the socket, not the underlying OS socket behavior.
So, it's best to just forget that you know that NSPR sockets are always
non-blocking under the hood, and just think of them as being blocking or
non-blocking as you would for the OS sockets if you weren't use NSPR.

NSPR blocking sockets have the property that they NEVER return a short
write, with or without SSL.  They also never return PR_FAILURE with
error code PR_WOULD_BLOCK_ERROR.

As seen by the application, the behavior of NSPR sockets, with respect to
blocking or non-blocking, is the same with or without SSL.

With or without libSSL, a read on a non-blocking NSPR socket that cannot
proceed because no data is available to be read will return PR_FAILURE
with error code PR_WOULD_BLOCK_ERROR.  The presence of libSSL does not
change the behavior of the socket from non-blocking to blocking.  Even
in the middle of a handshake on a non-blocking socket, if the handshake
protocol requires the receipt of a record from the peer, and no data is
available to be read from the underlying socket, the SSL socket will
return PR_FAILURE with error code PR_WOULD_BLOCK_ERROR.

If the SSL code sees that it has received PR_WOULD_BLOCK_ERROR from the
lower layer, it may safely assume that it is operating on a non-blocking
NSPR socket, and therefore it must also provide non-blocking NSPR socket
semantics, and must return the same result to its caller.

If an application wants a PR_Read or PR_Write to not return until the
handshake is over and the first application data has been sent or
received, the NSPR socket must be configured as a blocking socket.

That is the default condition of a new NSPR socket, by the way.
An application will not see a non-blocking socket unless it sets the
NSPR socket option to be non-blocking.  An NSPR socket that is
returned from PR_Accept will inherit its blocking/non-blocking status
from the listen FD passed to PR_Accept.

Finally, I must speak to SECWouldBlock.  SECWouldBlock is not part of
the definition of an NSPR socket.  An NSPR socket (with or without SSL)
does not return SECWouldBlock in response to any NSPR function (e.g.
PR_Read never returns SECWouldBlock).

SECWouldBlock is a value used inside libSSL for various purposes.
Some of the callback interfaces, where libSSL calls out to application-
supplied functions, are defined to allow the called function to return
SECWouldBlock to libSSL.  More on that below.

SECWouldBlock is largely an artifact of the days before NSS was converted
to use NSPR (which was in NSS 1.5).  Before NSS 1.5, libSSL would return
SECSuccess, SECFailure, or SECWouldBlock to any caller of the SSL socket
IO functions (e.g. SSL_Read).

SECWouldBlock does NOT mean the same thing as PR_WOULD_BLOCK_ERROR,
and never did.  It does NOT mean that the underlying socket is awaiting
I/O completion.

The meaning of SECWouldBlock is context dependent, and is used throughout
NSS in various places with various meanings, usually to distinguish the
cases of "function failed", and "function succeeded but results were
negative".  For example, when various PK11_* password functions return
SECWouldBlock, it means the function succeeded but the entered password
was bad.  I won't discuss all the places it is used in NSS, but I will
mention how it is used in the context of SSL.

When ssl2_GatherData returns SECWouldBlock, it means that ssl2_GatherData
has gathered (and processed) an SSL3/TLS record, and the caller should
switch from the SSL2 record gathering engine to the SSL3/TLS record
gathering engine.

In other contexts within libSSL, SECWouldBlock means that the progress on
the SSL socket is stopped because of something that is NOT related to the
underlying socket.  It indicates that the caller should NOT use a poll/select
type function to determine when further progress can be made on the socket.

libSSL defines interfaces for calling application-supplied callback
functions for several purposes, some of which are:
a) validating a received cert chain,
b) the "bad cert" callback, which allows the application to choose to
override an error in the received cert chain, and
c) the client authentication certificate selection callback, which asks
the client application to choose a client auth certificate to send to
the server.

The callbacks listed above may initiate other activity that must finish
before further progress can be made in the SSL handshake.  For example,
an OCSP request may be sent, or the callback may choose to ask the user
(through a dialog)how to proceed (e.g. to choose a cert from among several).

While the application is waiting for response from the user (or OCSP
responder), the application may not wish to stop the entire thread waiting
for that result.  So, the callback may return SECWouldBlock to libSSL,
telling it that the answer for which the callback was called is not yet
known.  LibSSL responds to that value by taking a path of execution that
suspends the progress of the SSL state machine at that point, and returns
to the caller (e.g. to the caller of PR_Read).  When that happens, libSSL
returns PR_FAILURE with PR_WOULD_BLOCK_ERROR.  That is, libSSL effectively
turns SECWouldBlock into PR_WOULD_BLOCK_ERROR.

This means that upon return of PR_Read, the calling application cannot
distinguish between the cases where the SSL socket is blocked waiting
for more data to arrive on the SSL socket itself, and the case where the
socket if effectively blocked waiting on local user UI action or OCSP
response, based solely on the error code.  But since the callback function
that returns SECWouldBlock to libSSL comes from the same application that
called PR_Read, presumably the application can arrange to communicate that
knowledge to itself.

See also http://lxr.mozilla.org/mozilla/source/security/nss/lib/ssl/notes.txt

-- 
Nelson B
_______________________________________________
dev-tech-crypto mailing list
dev-tech-crypto@lists.mozilla.org
https://lists.mozilla.org/listinfo/dev-tech-crypto

Reply via email to