SSL_read() seems to close my connection

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

SSL_read() seems to close my connection

Mario
Hi,

I have an issue with a try of my implementation of a secure
communication software.

I´m compiling everything on Debian 6.0.7 (openssl 0.9.8o-4squeeze14 and
its dev package).

For the application I´m dealing with non-blocking IO for the socket. I
can communicate a few bytes between server and client. However when my
server answers too much, the conneciton will be closed for both sides
when I try to read (SSL_read() returns 0 and the SSL-Error is 5 but the
systems errno is 0 all the time). The last SSL_write will always return
the amount of bytes I have to send, but ins this setup only 4 Bytes are
transmitted to the client. I case I send more than those 4 Bytes the
connection will be closed as described.

Here is my SSL relevant code of my server part:
--- SNIP
/* setup our crypto environment */
SSL_load_error_strings();
SSL_library_init();
OpenSSL_add_all_ciphers();
if((ctx->ssl_ctx = SSL_CTX_new(TLSv1_server_method())) == NULL) {
   tproxy_error_die("unable to initialize SSLv3 methods", 1);
}
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_ALL);
SSL_CTX_set_timeout(ctx->ssl_ctx, 300);
if(!SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, ctx->cert_bundle))
{
   tproxy_error_die("unable to read certificate bundle file", 1);
}
if(strlen(ctx->key_file_pw)) {
   SSL_CTX_set_default_passwd_cb(ctx->ssl_ctx,
private_key_pass_callback);
   SSL_CTX_set_default_passwd_cb_userdata(ctx->ssl_ctx, (void *)ctx);
}
if(!SSL_CTX_use_RSAPrivateKey_file(ctx->ssl_ctx, ctx->key_file,
SSL_FILETYPE_PEM)) {
   tproxy_error_die("unable to read key file", 1);
}
SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_NONE, NULL);
SSL_CTX_set_info_callback(ctx->ssl_ctx, apps_ssl_info_callback);
[...]
con->sock = accept(ctx->socket[i], (struct sockaddr *)&in_socket,
&s_size);
/* make the accepted socket non-blocking */
if(ioctl(con->sock, FIONBIO, (char *)&enable) < 0) {
   tproxy_error_die("cannot set socket flags", 1);
}
/* create a new SSL context to handle the connection with the peer */
con->ssl = SSL_new(ctx->ssl_ctx);
SSL_set_mode(con->ssl, SSL_MODE_AUTO_RETRY |
SSL_MODE_ENABLE_PARTIAL_WRITE);

/* complete ssl connection */
con->sbio = BIO_new_socket(con->sock, BIO_NOCLOSE);
SSL_set_bio(con->ssl, con->sbio, con->sbio);
/* set chipher */
if (SSL_set_cipher_list(con->ssl,"AES128-SHA") <= 0) {
   tproxy_error_die("unable to set chipher", 1);
}
/* do not require a valid client certificate */
SSL_set_verify(con->ssl, SSL_VERIFY_NONE, NULL);
add_connection(ctx, con);
[...]

    /* later in the connection worker thread */

[...]
while(cur) {
switch(cur->state) {
case CSTATE_SSL_HANDSHAKE:
   /* wait for the client to accept */
   if(1 != SSL_accept(cur->ssl)) {
     if(SSL_get_error(cur->ssl, -1) != SSL_ERROR_WANT_READ &&
SSL_get_error(cur->ssl, -1) != SSL_ERROR_WANT_WRITE) {
       /* an SSL error occured */
       cur->state = CSTATE_TERMINATING;
     }
   } else {
     cur->state = CSTATE_T_SETUP;
   }
   break;
case CSTATE_T_SETUP:
   len = SSL_read(cur->ssl, cur->data + cur->data_offset,
TPROXY_BUFFER_SIZE - cur->data_offset);
   if(len > 0) {
     cur->data_offset += len;
   } else if(len == 0){
     cur->state = CSTATE_TERMINATING;
   } else {
     if(SSL_get_error(cur->ssl, -1) != SSL_ERROR_WANT_READ &&
SSL_get_error(cur->ssl, -1) != SSL_ERROR_WANT_WRITE) {
       cur->state = CSTATE_TERMINATING;
     } else {
       if(cur->data_offset) {
         [... do something with the data for application handshake...]
     }
   }
   break;
case CSTATE_CONNECTED:
   if(SSL_want(cur->ssl) == SSL_NOTHING) {
     [...] check if something is in the pipe to send [...]
     if(proto_data->len != SSL_write(cur->ssl, proto_data,
proto_data->len)) {
       [...handle the partial send (never notices)...]
     }
   }
   len = SSL_read(cur->ssl, cur->data + cur->data_offset,
TPROXY_BUFFER_SIZE - cur->data_offset);
   if(len > 0) {
     cur->data_offset += len;
   } else if(len == 0){
     [...here is the actual error I´m talking about...]
   } else {
     if(SSL_get_error(cur->ssl, -1) != SSL_ERROR_WANT_READ &&
SSL_get_error(cur->ssl, -1) != SSL_ERROR_WANT_WRITE) {
       cur->state = CSTATE_TERMINATING;
     } else {
       if(cur->data_offset) {
         [... do something with the data for application ...]
     }
   }
   break;
[...]
----SNIP

I guess there was nothing special about it. The client is almost equal
- except that it do not handle multiple connections in a structure (cur)
as it only has one ssl connection a time to the server:

----SNIP
   /* setup ssl stuff */
   SSL_load_error_strings();
   SSL_library_init();
   OpenSSL_add_all_ciphers();
   if(NULL == (ctx->ssl_ctx = SSL_CTX_new(TLSv1_client_method()))) {
     tproxy_error_die("unable to initialize ssl environment", 1);
   }
   SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_NONE, NULL);
   SSL_CTX_set_info_callback(ctx->ssl_ctx, apps_ssl_info_callback);
[...]
   if(connect(ctx->proxy_socket, (struct sockaddr *)&addr, sizeof(struct
sockaddr_in)) < 0) {
     close(ctx->proxy_socket);
     tproxy_error_die("unable to connect", 1);
   }
   if(ioctl(ctx->proxy_socket, FIONBIO, (char *)&enable) < 0) {
     tproxy_error_die("cannot modify socket", 1);
   }
   SSL_CTX_set_timeout(ctx->ssl_ctx, 300);
   ctx->ssl = SSL_new(ctx->ssl_ctx);
   if (SSL_set_cipher_list(ctx->ssl, "AES128-SHA") <= 0) {
     tproxy_error_die("unable to set chipher", 1);
   }
   ctx->bio = BIO_new_socket(ctx->proxy_socket, BIO_NOCLOSE);
   SSL_set_bio(ctx->ssl, ctx->bio, ctx->bio);
   SSL_set_mode(ctx->ssl, SSL_MODE_AUTO_RETRY);
   while(1 != (len = SSL_connect(ctx->ssl))) {
     switch(SSL_get_error(ctx->ssl, len)) {
       case SSL_ERROR_WANT_WRITE:
       case SSL_ERROR_WANT_READ:
         usleep(10000);
         /* everything went fine (->non-blocking connect) */
         break;
       default:
         tproxy_error_die("unable to connect", 1);
     }
   }

   peer = SSL_get_peer_certificate(ctx->ssl);
   X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
NID_commonName, common_name, sizeof(common_name));
   printf("cn of peer: %s\n", common_name);
[... prepare application handshake ...]
   if(len != SSL_write(ctx->ssl, init_data_out, len)) {
     [... handle it (program was never here)...]
   }
   /* getting the configuration of the server */
   while(buf_offset < sizeof(struct proxy_proto_init_resp)) {
     len = SSL_read(ctx->ssl, buffer + buf_offset, BUFFER_SIZE -
buf_offset);
     if(len <= 0) {
       switch(SSL_get_error(ctx->ssl, len)) {
         case SSL_ERROR_WANT_WRITE:
         case SSL_ERROR_WANT_READ:
           usleep(10000);
           /* everything went fine (->non-blocking connect) */
           break;
         default:
           tproxy_error_die("did not receive configuration", 1);
       }
     } else {
       buf_offset += len;
     }
   }
[...setup application for user...]
while(1) {
     /***** recv from ssl link *****/
     ERR_print_errors(ctx->bio);
     len = SSL_read(ctx->ssl, buffer + buf_offset, sizeof(BUFFER_SIZE) -
buf_offset);
     t_err = errno;
     if(len > 0) {
       printf("%d bytes recvd %.*s\n", len, len, buffer + buf_offset);
// debugging
       buf_offset += len;
     } else if(len == 0) {
       ERR_print_errors(ctx->bio);
       if(SSL_ERROR_SYSCALL == SSL_get_error(ctx->ssl, len)) {
         printf("fatal:%s\n", strerror(t_err));
       }
       ERR_print_errors(ctx->bio);
       tproxy_error_die("connection broken 1", SSL_get_error(ctx->ssl,
len));
     } else {
       if(SSL_get_error(ctx->ssl, -1) != SSL_ERROR_WANT_READ &&
SSL_get_error(ctx->ssl, -1) != SSL_ERROR_WANT_WRITE) {
         tproxy_error_die("connection broken 2", 1);
       } else {
         [... do something with the date in the application ...]
       }
     }
}
[...]
----SNIP

Sending application data to the server is currently not implementet as
the server is not able to send the first application related message (>4
bytes) to the client. However the application handshake proceeds without
any problems.

The msg-callback and my debugging writes the following information into
the stdout:
CLIENT:
SSL_connect:before/connect initialization
SSL_connect:SSLv3 write client hello A
SSL_connect:error in SSLv3 read server hello A
SSL_connect:SSLv3 read server hello A
SSL_connect:SSLv3 read server certificate A
SSL_connect:SSLv3 read server done A
SSL_connect:SSLv3 write client key exchange A
SSL_connect:SSLv3 write change cipher spec A
SSL_connect:SSLv3 write finished A
SSL_connect:SSLv3 flush data
SSL_connect:error in SSLv3 read server session ticket A
SSL_connect:SSLv3 read server session ticket A
SSL_connect:SSLv3 read finished A
fatal:Success
fatal:connection broken 1
Error: error:00000000:lib(0):func(0):reason(0)

SERVER:
SSL_accept:before/accept initialization
SSL_accept:error in SSLv3 read client hello B
SSL_accept:SSLv3 read client hello B
SSL_accept:SSLv3 write server hello A
SSL_accept:SSLv3 write certificate A
SSL_accept:SSLv3 write server done A
SSL_accept:SSLv3 flush data
SSL_accept:error in SSLv3 read client certificate A
SSL_accept:error in SSLv3 read client certificate A
SSL_accept:error in SSLv3 read client certificate A
SSL_accept:error in SSLv3 read client certificate A
SSL_accept:error in SSLv3 read client certificate A
SSL_accept:error in SSLv3 read client certificate A
SSL_accept:error in SSLv3 read client certificate A
SSL_accept:error in SSLv3 read client certificate A
SSL_accept:error in SSLv3 read client certificate A
SSL_accept:error in SSLv3 read client certificate A
SSL_accept:SSLv3 read client key exchange A
SSL_accept:SSLv3 read finished A
SSL_accept:SSLv3 write session ticket A
SSL_accept:SSLv3 write change cipher spec A
SSL_accept:SSLv3 write finished A
SSL_accept:SSLv3 flush data


I´m happy about any hint and any idea! Is there anything I can do for
deeper debugging openssl?

Thank you.

Mario
______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
User Support Mailing List                    [hidden email]
Automated List Manager                           [hidden email]
Reply | Threaded
Open this post in threaded view
|

RE: SSL_read() seems to close my connection

J. J. Farrell-2
I've not been through your code properly, but this line grabbed my eye as I skimmed over it:

>      len = SSL_read(ctx->ssl, buffer + buf_offset, sizeof(BUFFER_SIZE) -
 buf_offset);

You don't show the definition of BUFFER_SIZE anywhere, but sizeof(BUFFER_SIZE) is likely to be 4 or 8 or similar; are you sure that's what you mean?

Regards,
                     jjf

> From: Mario [mailto:[hidden email]]
>
> I have an issue with a try of my implementation of a secure
> communication software.
>
> I´m compiling everything on Debian 6.0.7 (openssl 0.9.8o-4squeeze14 and
> its dev package).
>
> For the application I´m dealing with non-blocking IO for the socket. I
> can communicate a few bytes between server and client. However when my
> server answers too much, the conneciton will be closed for both sides
> when I try to read (SSL_read() returns 0 and the SSL-Error is 5 but the
> systems errno is 0 all the time). The last SSL_write will always return
> the amount of bytes I have to send, but ins this setup only 4 Bytes are
> transmitted to the client. I case I send more than those 4 Bytes the
> connection will be closed as described.
>
> Here is my SSL relevant code of my server part:
> --- SNIP
> /* setup our crypto environment */
> SSL_load_error_strings();
> SSL_library_init();
> OpenSSL_add_all_ciphers();
> if((ctx->ssl_ctx = SSL_CTX_new(TLSv1_server_method())) == NULL) {
>    tproxy_error_die("unable to initialize SSLv3 methods", 1);
> }
> SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_ALL);
> SSL_CTX_set_timeout(ctx->ssl_ctx, 300);
> if(!SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, ctx->cert_bundle))
> {
>    tproxy_error_die("unable to read certificate bundle file", 1);
> }
> if(strlen(ctx->key_file_pw)) {
>    SSL_CTX_set_default_passwd_cb(ctx->ssl_ctx,
> private_key_pass_callback);
>    SSL_CTX_set_default_passwd_cb_userdata(ctx->ssl_ctx, (void *)ctx);
> }
> if(!SSL_CTX_use_RSAPrivateKey_file(ctx->ssl_ctx, ctx->key_file,
> SSL_FILETYPE_PEM)) {
>    tproxy_error_die("unable to read key file", 1);
> }
> SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_NONE, NULL);
> SSL_CTX_set_info_callback(ctx->ssl_ctx, apps_ssl_info_callback);
> [...]
> con->sock = accept(ctx->socket[i], (struct sockaddr *)&in_socket,
> &s_size);
> /* make the accepted socket non-blocking */
> if(ioctl(con->sock, FIONBIO, (char *)&enable) < 0) {
>    tproxy_error_die("cannot set socket flags", 1);
> }
> /* create a new SSL context to handle the connection with the peer */
> con->ssl = SSL_new(ctx->ssl_ctx);
> SSL_set_mode(con->ssl, SSL_MODE_AUTO_RETRY |
> SSL_MODE_ENABLE_PARTIAL_WRITE);
>
> /* complete ssl connection */
> con->sbio = BIO_new_socket(con->sock, BIO_NOCLOSE);
> SSL_set_bio(con->ssl, con->sbio, con->sbio);
> /* set chipher */
> if (SSL_set_cipher_list(con->ssl,"AES128-SHA") <= 0) {
>    tproxy_error_die("unable to set chipher", 1);
> }
> /* do not require a valid client certificate */
> SSL_set_verify(con->ssl, SSL_VERIFY_NONE, NULL);
> add_connection(ctx, con);
> [...]
>
>     /* later in the connection worker thread */
>
> [...]
> while(cur) {
> switch(cur->state) {
> case CSTATE_SSL_HANDSHAKE:
>    /* wait for the client to accept */
>    if(1 != SSL_accept(cur->ssl)) {
>      if(SSL_get_error(cur->ssl, -1) != SSL_ERROR_WANT_READ &&
> SSL_get_error(cur->ssl, -1) != SSL_ERROR_WANT_WRITE) {
>        /* an SSL error occured */
>        cur->state = CSTATE_TERMINATING;
>      }
>    } else {
>      cur->state = CSTATE_T_SETUP;
>    }
>    break;
> case CSTATE_T_SETUP:
>    len = SSL_read(cur->ssl, cur->data + cur->data_offset,
> TPROXY_BUFFER_SIZE - cur->data_offset);
>    if(len > 0) {
>      cur->data_offset += len;
>    } else if(len == 0){
>      cur->state = CSTATE_TERMINATING;
>    } else {
>      if(SSL_get_error(cur->ssl, -1) != SSL_ERROR_WANT_READ &&
> SSL_get_error(cur->ssl, -1) != SSL_ERROR_WANT_WRITE) {
>        cur->state = CSTATE_TERMINATING;
>      } else {
>        if(cur->data_offset) {
>          [... do something with the data for application handshake...]
>      }
>    }
>    break;
> case CSTATE_CONNECTED:
>    if(SSL_want(cur->ssl) == SSL_NOTHING) {
>      [...] check if something is in the pipe to send [...]
>      if(proto_data->len != SSL_write(cur->ssl, proto_data,
> proto_data->len)) {
>        [...handle the partial send (never notices)...]
>      }
>    }
>    len = SSL_read(cur->ssl, cur->data + cur->data_offset,
> TPROXY_BUFFER_SIZE - cur->data_offset);
>    if(len > 0) {
>      cur->data_offset += len;
>    } else if(len == 0){
>      [...here is the actual error I´m talking about...]
>    } else {
>      if(SSL_get_error(cur->ssl, -1) != SSL_ERROR_WANT_READ &&
> SSL_get_error(cur->ssl, -1) != SSL_ERROR_WANT_WRITE) {
>        cur->state = CSTATE_TERMINATING;
>      } else {
>        if(cur->data_offset) {
>          [... do something with the data for application ...]
>      }
>    }
>    break;
> [...]
> ----SNIP
>
> I guess there was nothing special about it. The client is almost equal
> - except that it do not handle multiple connections in a structure
> (cur)
> as it only has one ssl connection a time to the server:
>
> ----SNIP
>    /* setup ssl stuff */
>    SSL_load_error_strings();
>    SSL_library_init();
>    OpenSSL_add_all_ciphers();
>    if(NULL == (ctx->ssl_ctx = SSL_CTX_new(TLSv1_client_method()))) {
>      tproxy_error_die("unable to initialize ssl environment", 1);
>    }
>    SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_NONE, NULL);
>    SSL_CTX_set_info_callback(ctx->ssl_ctx, apps_ssl_info_callback);
> [...]
>    if(connect(ctx->proxy_socket, (struct sockaddr *)&addr,
> sizeof(struct
> sockaddr_in)) < 0) {
>      close(ctx->proxy_socket);
>      tproxy_error_die("unable to connect", 1);
>    }
>    if(ioctl(ctx->proxy_socket, FIONBIO, (char *)&enable) < 0) {
>      tproxy_error_die("cannot modify socket", 1);
>    }
>    SSL_CTX_set_timeout(ctx->ssl_ctx, 300);
>    ctx->ssl = SSL_new(ctx->ssl_ctx);
>    if (SSL_set_cipher_list(ctx->ssl, "AES128-SHA") <= 0) {
>      tproxy_error_die("unable to set chipher", 1);
>    }
>    ctx->bio = BIO_new_socket(ctx->proxy_socket, BIO_NOCLOSE);
>    SSL_set_bio(ctx->ssl, ctx->bio, ctx->bio);
>    SSL_set_mode(ctx->ssl, SSL_MODE_AUTO_RETRY);
>    while(1 != (len = SSL_connect(ctx->ssl))) {
>      switch(SSL_get_error(ctx->ssl, len)) {
>        case SSL_ERROR_WANT_WRITE:
>        case SSL_ERROR_WANT_READ:
>          usleep(10000);
>          /* everything went fine (->non-blocking connect) */
>          break;
>        default:
>          tproxy_error_die("unable to connect", 1);
>      }
>    }
>
>    peer = SSL_get_peer_certificate(ctx->ssl);
>    X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
> NID_commonName, common_name, sizeof(common_name));
>    printf("cn of peer: %s\n", common_name);
> [... prepare application handshake ...]
>    if(len != SSL_write(ctx->ssl, init_data_out, len)) {
>      [... handle it (program was never here)...]
>    }
>    /* getting the configuration of the server */
>    while(buf_offset < sizeof(struct proxy_proto_init_resp)) {
>      len = SSL_read(ctx->ssl, buffer + buf_offset, BUFFER_SIZE -
> buf_offset);
>      if(len <= 0) {
>        switch(SSL_get_error(ctx->ssl, len)) {
>          case SSL_ERROR_WANT_WRITE:
>          case SSL_ERROR_WANT_READ:
>            usleep(10000);
>            /* everything went fine (->non-blocking connect) */
>            break;
>          default:
>            tproxy_error_die("did not receive configuration", 1);
>        }
>      } else {
>        buf_offset += len;
>      }
>    }
> [...setup application for user...]
> while(1) {
>      /***** recv from ssl link *****/
>      ERR_print_errors(ctx->bio);
>      len = SSL_read(ctx->ssl, buffer + buf_offset, sizeof(BUFFER_SIZE)
> -
> buf_offset);
>      t_err = errno;
>      if(len > 0) {
>        printf("%d bytes recvd %.*s\n", len, len, buffer + buf_offset);
> // debugging
>        buf_offset += len;
>      } else if(len == 0) {
>        ERR_print_errors(ctx->bio);
>        if(SSL_ERROR_SYSCALL == SSL_get_error(ctx->ssl, len)) {
>          printf("fatal:%s\n", strerror(t_err));
>        }
>        ERR_print_errors(ctx->bio);
>        tproxy_error_die("connection broken 1", SSL_get_error(ctx->ssl,
> len));
>      } else {
>        if(SSL_get_error(ctx->ssl, -1) != SSL_ERROR_WANT_READ &&
> SSL_get_error(ctx->ssl, -1) != SSL_ERROR_WANT_WRITE) {
>          tproxy_error_die("connection broken 2", 1);
>        } else {
>          [... do something with the date in the application ...]
>        }
>      }
> }
> [...]
> ----SNIP
>
> Sending application data to the server is currently not implementet as
> the server is not able to send the first application related message
> (>4
> bytes) to the client. However the application handshake proceeds
> without
> any problems.
>
> The msg-callback and my debugging writes the following information into
> the stdout:
> CLIENT:
> SSL_connect:before/connect initialization
> SSL_connect:SSLv3 write client hello A
> SSL_connect:error in SSLv3 read server hello A
> SSL_connect:SSLv3 read server hello A
> SSL_connect:SSLv3 read server certificate A
> SSL_connect:SSLv3 read server done A
> SSL_connect:SSLv3 write client key exchange A
> SSL_connect:SSLv3 write change cipher spec A
> SSL_connect:SSLv3 write finished A
> SSL_connect:SSLv3 flush data
> SSL_connect:error in SSLv3 read server session ticket A
> SSL_connect:SSLv3 read server session ticket A
> SSL_connect:SSLv3 read finished A
> fatal:Success
> fatal:connection broken 1
> Error: error:00000000:lib(0):func(0):reason(0)
>
> SERVER:
> SSL_accept:before/accept initialization
> SSL_accept:error in SSLv3 read client hello B
> SSL_accept:SSLv3 read client hello B
> SSL_accept:SSLv3 write server hello A
> SSL_accept:SSLv3 write certificate A
> SSL_accept:SSLv3 write server done A
> SSL_accept:SSLv3 flush data
> SSL_accept:error in SSLv3 read client certificate A
> SSL_accept:error in SSLv3 read client certificate A
> SSL_accept:error in SSLv3 read client certificate A
> SSL_accept:error in SSLv3 read client certificate A
> SSL_accept:error in SSLv3 read client certificate A
> SSL_accept:error in SSLv3 read client certificate A
> SSL_accept:error in SSLv3 read client certificate A
> SSL_accept:error in SSLv3 read client certificate A
> SSL_accept:error in SSLv3 read client certificate A
> SSL_accept:error in SSLv3 read client certificate A
> SSL_accept:SSLv3 read client key exchange A
> SSL_accept:SSLv3 read finished A
> SSL_accept:SSLv3 write session ticket A
> SSL_accept:SSLv3 write change cipher spec A
> SSL_accept:SSLv3 write finished A
> SSL_accept:SSLv3 flush data
>
>
> I´m happy about any hint and any idea! Is there anything I can do for
> deeper debugging openssl?
>
> Thank you.
______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
User Support Mailing List                    [hidden email]
Automated List Manager                           [hidden email]