We encountered a situation within NT 4.0 (using Visual C++ 6.0) where data
was
sent to the socket layer of openssl correctly, but openssl never receives
it.
Unix appears to be unaffected. What makes this especially peculiar, is that
we
configured the socket to be blocking, but somehow, it becomes non-blocking.
Nowhere in our code did we use WSAEventSelect or WSAAsyncSelect or
temporarily
use a non-blocking socket.

The client sends the client hello message and the server responds with the
server hello message. The format and certificates of these messages are
correct. For some reason, after a call to SSL_write recv never gets the data
from the server. All the client receives is a -1 and is forced to give up
without the option of trying to receive data again.

We traced the problem to the sock_read function within
openssl-0.9.4\crypto\bio\bss_sock.c.

179   #ifndef BIO_FD
180   static int sock_read(BIO *b, char *out, int outl)
181   #else
182   static int fd_read(BIO *b, char *out,int outl)
183   #endif
184       {
185       int ret=0;
186
187       if (out != NULL)
188           {
189   #ifndef BIO_FD
190           clear_socket_error();
191           ret=readsocket(b->num,out,outl);
192   #else
193           clear_sys_error();
194           ret=read(b->num,out,outl);
195   #endif
196           BIO_clear_retry_flags(b);
197           if (ret <= 0)
198               {
199   #ifndef BIO_FD
200               if (BIO_sock_should_retry(ret))
201   #else
202               if (BIO_fd_should_retry(ret))
203   #endif
204               BIO_set_retry_read(b);
205               }
206           }
207           return(ret);
208       }

The function readsocket is actually recv if BIO_FD is not defined.

    From openssl-0.9.4\inc32\openssl\e_os.h(119):
    #define readsocket(s,b,n)    recv((s),(b),(n),0)

Under closer examination, when openssl executes the ssl3_server_hello
portion
of the protocol, readsocket (line 191) returns with -1 and outl > 0 with no
bytes read within variable out. While executing line 200, openssl discovers
that the error is WSAEWOULDBLOCK (10035), and sets the appropriate flags for
retrying the socket. Instead of immediately retrying the socket, each
function
called in the stack within openssl, passes -1 to the function calling it all
the way up to SSL_write. If recv waited a few milliseconds more, the data
would
be available.

After receiving a -1 from SSL_write, if someone tries calling it again, the
same error occurs. By calling it again, the whole process is started rather
than retrying to receive the last message. Because of these facts, the
following temporary solution was developed:

 66A* #if defined(MSDOS) || defined(WINDOWS)
 66B* #include <time.h>
 66C* #endif


179   #ifndef BIO_FD
180   static int sock_read(BIO *b, char *out, int outl)
181   #else
182   static int fd_read(BIO *b, char *out,int outl)
183   #endif
184       {
185       int ret=0;
185A* #if defined(MSDOS) || defined(WINDOWS)
185B*     int err = 0;
185C*     clock_t referencetime = clock();
185D*     clock_t currenttime   = clock();
185E*     double difftime = 0;
185F*     double timeout = 100; /* in ms */
185G* #endif
186
187       if (out != NULL)
188           {
189   #ifndef BIO_FD
189A* #if defined(MSDOS) || defined(WINDOWS)
189B*         do
189C*

189D* #endif
190               clear_socket_error();
191               ret=readsocket(b->num,out,outl);
191A* #if defined(MSDOS) || defined(WINDOWS)
191B*             err = get_last_socket_error();
191C*             currenttime = clock();
191D*             difftime = (double)(((currenttime - referencetime) * 100)
/

CLOCKS_PER_SEC);
191E*             }
191F*         while((err != 0)&&(timeout > difftime));
191G* #endif
192   #else
193           clear_sys_error();
194           ret=read(b->num,out,outl);
195   #endif
196           BIO_clear_retry_flags(b);
197           if (ret <= 0)
198               {
199   #ifndef BIO_FD
200               if (BIO_sock_should_retry(ret))
201   #else
202               if (BIO_fd_should_retry(ret))
203   #endif
204               BIO_set_retry_read(b);
205               }
206           }
207           return(ret);
208       }

We verified that the client sends the client hello message and the server
responds with the server hello message. The format and certificates of these
messages are correct. For some reason, recv never gets the data from the
server. All the client gets is a -1 and is forced to give up without the
option
of trying to receive data again.

In other words: with the socket in a non-blocking mode, if this section of
code
generates a EWOULDBLOCK error, it does not retry reading. All it does is set
a
flag and report an error up the call stack.

There may be a better way to handle this; however, this is what we had to do
to make our application work.


Paul Kudlawiec
Software Engineer
[EMAIL PROTECTED]


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

Reply via email to