0

I am trying to connect to a tor server through https. I use a non-blocking socket, with the SSL object not linked with the socket.

The reading works likes this: all the encrypted data is read from the socket, written to the rbio(BIO_write) and finally read from the rbio(SSL_read), such that it is decrypted. The writing works likes this: to encrypt the unencrypted data(destined for the socket) it is first written to wbio(SSL_write) and then read from wbio(BIO_read) to write it to the socket.

The handshake succeeds and the client can write to the server normally, but when something is read from socket and written to the rbio it fails at SSL_read. SSL_read gives an error and the weird thing is if BIO_read is called instead of SSL_read, it reads all. But I want the decrypted data instead of the encrypted data given by BIO_read.

SSL_read gives the SSL_ERROR_WANT_READ error, and I know it gives this error when a non-blocking socket is used and there is nothing to read. But there is something to read and when I do the same thing with a blocking socket, it just blocks the whole time.

I tried a lot of things to figure out what's wrong. I also used Wireshark to see if there is something wrong with the handshake, but there isn't.

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <malloc/malloc.h>
#include <unistd.h>
#include <poll.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <openssl/ssl.h>
#include <openssl/err.h>




struct ssl_information {
    int connected;
    int sock;

    BIO *bio_read;
    BIO *bio_write;

    SSL *ssl;
    SSL_CTX *ctx;
};

void ssl_connection_init()
{
    SSL_load_error_strings();
    SSL_library_init();
    OpenSSL_add_all_algorithms();
}

void ssl_disconnect(struct ssl_information *ssl)
{
    if(!ssl->connected) return;

    if(ssl->ctx) SSL_CTX_free(ssl->ctx);

    if(ssl->ssl) {
        SSL_shutdown(ssl->ssl);
        SSL_free(ssl->ssl);
    }

    close(ssl->sock);
    ssl->connected = 0;
}

int waiting_socket_reading(int sock, unsigned long milli_seconds)
{
    struct pollfd file_descriptor;

    file_descriptor.fd = sock;
    file_descriptor.events = POLLIN;

    return poll(&file_descriptor, 1, milli_seconds);
}

int tcp_connection(char *ip, int port)
{
    int flags, rc, sock;
    struct sockaddr_in address;

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock == -1) return -1;

    flags = fcntl(sock, F_GETFL);
    rc = fcntl(sock, F_SETFL, flags | O_NONBLOCK);

    memset(&address, 0, sizeof(address));
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = inet_addr(ip);
    address.sin_port = htons(port);

    connect(sock, (struct sockaddr *)&address, sizeof(address));

    return sock;
}

int ssl_socket_read(struct ssl_information *ssl)
{
    int rc;
    char buffer[4096];

    if(waiting_socket_reading(ssl->sock, 200)) {
        rc = read(ssl->sock, buffer, sizeof(buffer));
        if(rc <= 0) return rc;
        rc = BIO_write(ssl->bio_read, buffer, rc);
        if(rc <= 0) return rc;
    } else return 0;

    return rc;
}

int ssl_socket_write(struct ssl_information *ssl)
{
  int rc;
  char buffer[4096];

  if(BIO_pending(ssl->bio_write)) {
      rc = BIO_read(ssl->bio_write, buffer, sizeof(buffer));
      if(rc <= 0) return rc;
      rc = write(ssl->sock, buffer, rc);
      if(rc <= 0) return rc;
  } else return 0;

  return rc;
}

struct ssl_information ssl_connection(char *ip, int port)
{
    int rc;
    static struct ssl_information ssl;

    ssl.sock = tcp_connection(ip, port);
    if(ssl.sock == -1) {
        ssl.connected = 0;
        return ssl;
    }

    ssl_connection_init();

    ssl.ctx = SSL_CTX_new(TLS_method());
    ssl.ssl = SSL_new(ssl.ctx);

    ssl.bio_read = BIO_new(BIO_s_mem());
    BIO_set_mem_eof_return(ssl.bio_read, -1);

    ssl.bio_write = BIO_new(BIO_s_mem());
    BIO_set_mem_eof_return(ssl.bio_write, -1);

    SSL_set_connect_state(ssl.ssl);
    SSL_set_bio(ssl.ssl, ssl.bio_read, ssl.bio_write);


    while(1) {
        rc = SSL_connect(ssl.ssl);

        switch(SSL_get_error(ssl.ssl, rc)) {
            case SSL_ERROR_WANT_READ:
            case SSL_ERROR_WANT_WRITE:
                rc = ssl_socket_read(&ssl);
                rc = ssl_socket_write(&ssl);

                continue;
            default:
                ssl_disconnect(&ssl);
                return ssl;
            case SSL_ERROR_NONE:
                ssl.connected = 1;
        }

        break;
    }

    return ssl;
}

int main()
{
    int rc, request_size;
    char *request;
    struct ssl_information ssl;
    char response[4096];

    ssl = ssl_connection("199.249.230.174", 443);

    if(!ssl.connected) {
        printf("DISCONNECTED\n");
        ssl_disconnect(&ssl);
        return 0;
    }
    printf("CONNECTED\n");

    rc = SSL_write(ssl.ssl, request, request_size); //request and request_size are set of course
    printf("Written %d unencrypted bytes to the bio.\n", rc);

    rc = ssl_socket_write(&ssl);
    printf("Written %d encrypted bytes to the socket.\n", rc);


    rc = ssl_socket_read(&ssl);
    printf("Read %d encrypted bytes from the socket.\n", rc);

    memset(response, 0, sizeof(response)); 
    rc = SSL_read(ssl.ssl, response, sizeof(response)); //it goes well untill this line
    printf("Read %d unencrypted bytes from the bio.\n", rc); //rc is set to -1
    printf("Error %d is given\n", SSL_get_error(ssl.ssl, rc)); //prints 2 i.e. SSL_ERROR_WANT_READ

    ssl_disconnect(&ssl);

    return 1;
}
John John
  • 35
  • 6
  • 1
    Does this answer your question? [what is the difference between BIO\_read/BIO\_write and SSL\_read/SSL\_write when the BIOs are memory BIOs and not socket BIOs?](https://stackoverflow.com/questions/38516584/what-is-the-difference-between-bio-read-bio-write-and-ssl-read-ssl-write-when-th) – Irelia Aug 08 '22 at 23:09
  • [memory bio](https://www.roxlu.com/2014/042/using-openssl-with-memory-bios) – Irelia Aug 08 '22 at 23:10
  • Probably needs to receive more data before it can decrypt anything. – Shawn Aug 08 '22 at 23:14
  • 3
    " I know it gives this error when a non-blocking socket is used and there is nothing to read": no you don't, This error means it wants you to execute *another* read. – user207421 Aug 09 '22 at 00:01
  • 1
    Agreed. `SSL_ERROR_WANT_READ` means that OpenSSL ran out of data to process and *wants* you to read more data from the socket. A read on the socket may return its own error indicating there is no data to read at that moment, in which case you have to wait for new data to arrive before OpenSSL can proceed. Likewise, `SSL_ERROR_WANT_WRITE` means OpenSSL has data it *wants* to send, but the underlying socket can't accept the data at that moment because there is no room for it, so you have to wait for the socket to successfully clear room and accept the data before OpenSSL can proceed. – Remy Lebeau Aug 09 '22 at 00:27
  • There was more data in the socket that I could read, so I call an extra read. There is now nothing to read anymore(I waited 10 seconds) and I still get the same error. – John John Aug 15 '22 at 16:48

0 Answers0