"Andy Schneider" <[EMAIL PROTECTED]> writes:
> [1  <text/plain; Windows-1252 (quoted-printable)>]
> I failed to get SSL BIO working. However I can get renogitiation working
> if I use the SSL_* functions and (when I need to) do the renegotiation
> calls _before_ an SSL_write from the server to the client. If I do the
> calls before an SSL_read I get problems. Can anyone confirm that
> renegotiation should only take place before an SSL_write? If so, maybe
> this is why the SSL BIO doesn't appear to work (since it can do
> renegotiation in the read as well as write code I believe). To perform
> renegotiation I am doing this:
> 
> // Read from client
> SSL_read (...)
> 
> if (connectionOpenForManyHours) {
>   SSL_renegotiate (s);
>   SSL_do_handshake (s);
>   s->state = SSL_ST_ACCEPT;
>   SSL_do_handshake (s);
> }
This is extremely tricky. Lutz Jaenicke, Bodo Moeller and I have
just spent the last week or two figuring it out.

In order to understand what's happening here it's important to
understand that there's no requirement for the client to initiate
a rehandshake once it receives a HelloRequest. It could continue to
send data and NEVER rehandshake. The only way for a rehandshake
to actually get started is for the client to send a ClientHello
and the server to read it. 

SSL_renegotiate() simply puts the SSL object into a state where
it will send an SSL HelloRequest when you next call
SSL_do_handshake(). After the HelloRequest is sent the server goes
back to it's normal operations. 

Recall that I said that a rehandshake requires the server to
read the client's ClientHello. This requires that the server
actually read from the network. Thus, the following code won't
work:

  SSL_renegotiate (s);
  SSL_do_handshake (s);

  SSL_write(...)
  SSL_write(...)

Even if the client happens to send a ClientHello (and recall that
it may not) you'll never see it. 

The appropriate thing to do here depends on what you expect the
client to do. In the simplest case (the one you usually have with
HTTP) the client has transmitted all the data that it's going to
transmit and it's waiting for the server. The server needs the
client to do cert-based client-auth to proceed. In this case, 
the next data from the client must be a ClientHello or it's an
error. There are two ways to go about this:

  s->state=SSL_ST_ACCEPT;
  SSL_do_handshake(s);

The explicit state setting forces the server into expecting a
ClientHello. If it doesn't get one it will transmit an
unexpected_message alert and SSL_do_handshake() will return
an error. You've probably noticed that if you don't set the
state variable, SSL_do_handshake() returns immediately. That's
because it doesn't think there's a handshake to do.

Now, setting state variables inside the struct pretty horribly
violates the API. An alternative is to use the fact that SSL_read()
automatically rehandshakes when it gets a ClientHello (if you're
a server) or a HelloRequest (if you're a client). Thus, if you're
using blocking sockets, you can do something like this: 

  SSL_read(ssl, NULL, 0);

The SSL_read() will automatically do the handshake (if the client
complies). For reasons I won't go into now, when SSL_read()
performs a handshake it always returns SSL_ERROR_WANT_READ
afterwards. Thus, if you get SSL_ERROR_WANT_READ you know that
a rehandshake took place. On the other hand, if SSL_read()
succeeds then you know that the client sent data and you can
throw an error at this point. Note that this approach only works
if you're using blocking sockets. If you're using non-blocking
you'll get SSL_ERROR_WANT_READ all the time. Also, it's worth
noting that whatever you do once you detect that a rehandshake
hasn't occurred, the other side may not understand it because
it likely won't generate the unexpected_message alert (or any
other error alert).

Now, both of these approaches absolutely depend on your wanting
to force an immediate rehandshake and fail otherwise. This
makes sense with some protocols but if you're simply trying
to refresh the keying material it can be quite dangerous.
If you try either of the above approaches on an active connection
then there are all sorts of race conditions that are likely
to cause problems. For instance, on a bidirectional stream with
modest latency, consider the following sequence of events:

Client                          Server
SSL_write(DATA)
                                SSL_renegotiate()
                                SSL_do_handshake()

                                DATA Arrives            

                                s->state=SSL_ST_ACCEPT
                                SSL_do_handshake() returns error
HelloRequest arrives
send ClientHello

Now, the client is correctly responding to your HelloRequest
but unfortunately there's already data in flight. 

If this is your case then you'll have to do something more subtle. I
recommend just proceeding with your SSL_reads() and SSL_writes() and
periodically checking to see if a rehandshake has taken place. If
you're using blocking sockets you can simply check for
SSL_ERROR_WANT_READ. If you're using non-blocking you'll need to
interrogate the SSL object (try SSL_ctrl_num_reneogotiations()). If
the renegotiation doesn't occur within some timeout window you can
throw an error then if you like.

All that said, it appears to me that OpenSSL needs an API call
that allows you to tell it that a ClientHello is expected
and that it should generate an error if one is not received
(i.e. a clean way to do s->state=SSL_ST_ACCEPT). However, it's
important to realize that such a call is only useful in
certain circumstances.

-Ekr

[Eric Rescorla                                   [EMAIL PROTECTED]]
                http://www.rtfm.com/


______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
User Support Mailing List                    [EMAIL PROTECTED]
Automated List Manager                           [EMAIL PROTECTED]

Reply via email to