Kyle Hamilton wrote:
On 6/26/06, Darryl Miles <[EMAIL PROTECTED]> wrote:
I still have not gotten to the bottom of the entire scope of
situation(s) can cause an SSL_write() to return -1 WANT_READ.  If its
only renegotiation that can; then this is always instigated by a
SSL_renegotiate() (from my side) or and SSL_read() that causes a
re-negotiate request (from the remote side) to be processed.

For maximum security, you need to read any pending data before you
write.  This is because there's another type of data that the protocol
uses: alerts.  A good number of which are fatal and require the entire
connection to be destroyed and recreated from scratch.

I am happy when doing bulk writes to poll OpenSSL to give OpenSSL temporary program control to read and process any *fatal* alert conditions only and allow my application to act on them immediately.

But I *don't* want to process any non-fatal alerts at that time or any application data. A non-fatal alert would be a renegotiate instigated from the remote end, I want to defer that until I can more usefully deal with it a little later.

I think SSL_peek() will allow non-fatal alerts to be processed, so that API call isn't any use to me, when I am bulk writing.

I'm thinking like: if(SSL_check_fatal_alert(ssl) < 0) { handle_this(); }


Do all alerts that require a round trip automatically suspend the instigating ends outbound application data. SSL_write() -1 WANT_READ ? I.e. the protocol doesn't allow application data between the send alert packet and the receiving of the alert reply from its peer. But it does allow inbound application data to be receive around that alert reply (depending on the nature of the alert and if the other end if now waiting for out reply).

If this is so there isn't any big problem of application data filling up the buffering (TCP/kernel/openssl) and stopping the alert response from being seen when an outbound alert request is in progress.


The need for this dumbfounds me.  If SSL_write() is returning (<= 0)
then it should not have taken any data from my buffer, nor be retaining
my buffer address (or accessing data outside the scope of the function
call).

I understand that you have prioritized traffic, but you've already
stated that /you have committed that data to the connection/.  OpenSSL
has every right to take some of the data (for example, if you pass a
buffer with a length of 4096 and the underlying interface handles only
1440-byte writes at a time) and run its internal operations -- such as
updating its packet count, calculating the HMAC, and running the part
of the buffer it can process next through the block or stream cipher,
before it determines if it needs to read.  (This is because
cryptographic operations can be time-consuming, and more data can come
in while all those operations are being done.)

Basically, it's akin to a database that has autocommit set to 1.  It's
already lost its previous state before the data was pulled out of the
buffer in the first place.

With a kernel call a return of -1 does not commit _ANY_ data to the kernel buffer from the application. This is the behavior most application programmer would expect of OpenSSL (unless otherwise documented clearly - and I'd say its not at this time).

Once I commit a partial write of a packet (my appl data is packetized) then that whole packet is considered inprogress and that situation can not be revoked. This works 100% without OpenSSL.

Bodo's fine explaination has simply altered the point when I consider the next packet of my packetized data committed. After first presenting it with SSL_write() regardless of its return value, not after the first time SSL_write() returns (> 0) as is the case with raw kernel sockets.

Once my application data packet is in progress it will always be the next thing to be driven into the layer below, before we look for the next packet by assessing priority queues. Pretty standard stuff for a priority queue implementation.


On your comment of "and more data can come in while ...". More data can't come in, unless I call SSL_read(), providing there are no pending alert request instigated from my side. Bodo clarified that point too.


It is also valid for me to "change my mind" about exactly what
application data I want to write at the next SSL_write() call.  This
maybe a change of application data contents or a change of amount of
data to write (length).

It's valid for you to change your mind at write().  SSL_write() does
not have precisely the same semantics... because SSL_write has already
changed the state of the SSL object.  (This is a case where multiple
return values would be useful, but we don't have them, so SSL_write
returns -1 to indicate that none of the application data has yet gone
out on the interface.)  It can't "roll back" to the state it was in
before it returned WANT_READ.

I think this is actually unnecessary design. I'm thinking that there should be:

int SSL_write(SSL *s, const void *, int len); with the same type of semantics as kernel write() calls. The don't bite you in the ass approach.

int SSL_write_ex(SSL *s, const void *data, int len, int *committedlenptr); This allows an error and commitedlen to be returned at the same time. This is the problem with the existing implementation the first return by SSL_write() is trying to do two things at once and the return value mechanism doesn't allow it, so application space has to deal with it.

int SSL_write_compat(SSL *s, const void *data, int len); The current behavior to applications that rely on its exact nature.

I think is the perfectly possible to achieve the above with an SSL API.


Infact I have an application that does exactly this, it implements a
priority queue of packetized data and the decision about what to send
next is made right at the moment it knows it can call write().

So it depends on a write() semantic that isn't matched by SSL_write().
The best thing to do in that case is to loop it through the reading
process until it says WANT_WRITE.

Now Bodo has explained the situation fixing my application to work within the rules is an easy task for me (using the method outlined in a previous comment). But that still doesn't make the OpenSSL SSL API nice to use.


When you say "change the buffer location" do you mean the exact offset
given to SSL_write() in 2nd argument ?  Or do you mean for repeated
calls to SSL_write() the last byte (4096th byte from example) address
remains constant until OpenSSL gives indication that the last byte has
been committed ?

The address of the buffer that you send.  The contents and length of
the buffer need to stay the same (at least until the TLS 1.2 maximum
segment length extension is put in, at which point you can know what
amount of data to send to SSL_write and know what it hasn't sent yet).

No.  Bodo is saying the "address of the buffer" doesn't matter.

Yes.  The contents and length need to stay the same.

So I ask. Am I allowed to increase the data length on my next call to SSL_write(), I'm thinking I can. So this is another clarification on what is allowed and what is not allowed.

Your approach to using SSL_write() and TLS extension sounds like even more of a kludge inside the application. The application doesn't want to care too much about SSL it just wants a good interface to use, so its not going to go looking up if the extension is in play write length tricks. IMHO the app really shouldn't need to know about this. The SSL library high level interface could be better designed.


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

Reply via email to