NMilev NMilev - 2 months ago 46
C Question

OpenSSL openwrt failed socket bind

I am running openwrt on Dragino Yun. My wish is to run an openssl server on the Dragino that will be used for transferring some (not so large amounts of) data. However, after starting the server and loading the certificates, a call to

BIO_do_accept()
returns the value <= 0, indicating a failed bind. The port I am trying to bind is 5354, but I tried 8081, 8080, 443 even.

The error message I get from
ERR_print_errors_fp
is:

1998677064:error:0200407C:lib(2):func(4):reason(124):NA:0:port='5354'
1998677064:error:20069076:lib(32):func(105):reason(118):NA:0:


Could anybody explain why is my program unable to bind? I have tested it on Ubuntu - this is why I haven't posted any code - (the problem I have is with a cross-compiled version), OpenSSL version
OpenSSL 1.0.1f 6 Jan 2014
and, on Dragino, OpenSSL version is:
OpenSSL 1.0.1h 5 Jun 2014
.
Also, Dragino banner is:

____ ____ _ ____ ___ _ _ ___
| _ \| _ \ / \ / ___|_ _| \ | |/ _ \
| | | | |_) | / _ \| | _ | || \| | | | |
| |_| | _ < / ___ \ |_| || || |\ | |_| |
|____/|_| \_\/_/ \_\____|___|_| \_|\___/

W i F i, L i n u x, M C U, E m b e d d e d

Model: Dragino v2
Version: Dragino-v2 common-2.0.5
Build Tue Aug 18 17:10:10 CST 2015

www.dragino.com
----------------------------------------------------


I tried using s_server and the result is following:

root@dragino-70decb:~/certificates# openssl s_server -key server.key.pem -cert server.cert.pem -accept 8081
Enter pass phrase for server.key.pem:
Using default temp DH parameters
ACCEPT


which, when s_client is used, produced some communication, thus the socket bind was ok.

So, what is the problem and how do I make this work properly?
I tried Googling the error message but to no avail.

Also,
iptables -L
yields:

Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- anywhere anywhere

Chain FORWARD (policy ACCEPT)
target prot opt source destination
ACCEPT all -- anywhere anywhere

Chain OUTPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- anywhere anywhere


EDIT: Adding the code.

common.h:

#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
#include <openssl/rsa.h>
#include <openssl/objects.h>


#define PORT "5354"
#define SERVER "localhost"
#define CLIENT "localhost"
#define CIPHER_LIST "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"

#define int_error(msg) handle_error(__FILE__, __LINE__, msg)

#define SSL_METHOD_ SSLv23_method()
#define SEED_PRNG_() seed_prng(30)
#define SSL_CTX_FLAGS_ SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION
#define DEFAULT_DEPTH_ 4
#define DATA_SIZE_ 256


void handle_error(const char *file, int lineno, const char *msg) ;
void init_OpenSSL(void) ;
int seed_prng(int bytes) ;
int verify_callback(int ok, X509_STORE_CTX *store) ;
long post_connection_check(SSL *ssl, char *host) ;

int write_to_SSL(SSL *ssl, const char* msg, int length) ;
int read_from_SSL(SSL *ssl, char* msg, int length) ;


common.c:

#include "common.h"
void handle_error(const char *file, int lineno, const char *msg)
{
fprintf(stderr, "** %s:%i %s\n", file, lineno, msg);
ERR_print_errors_fp(stderr);
exit(-1);
}
void init_OpenSSL(void)
{
if (!SSL_library_init())
{
fprintf(stderr, "** OpenSSL initialization failed!\n");
exit(-1);
}
//Loads error strings for various SSL functions
SSL_load_error_strings();
}

//Not sure if this is good idea
int seed_prng(int bytes)
{
//Seeds PRNG (pseudo random number generator) with the contents of the /dev/urandom file
if (!RAND_load_file("/dev/urandom", bytes))
{
return 0;
}

return 1;
}

int verify_callback(int ok, X509_STORE_CTX *store)
{
char data[DATA_SIZE_];
if (!ok)
{
X509 *cert = X509_STORE_CTX_get_current_cert(store);
int depth = X509_STORE_CTX_get_error_depth(store);
int err = X509_STORE_CTX_get_error(store);
fprintf(stderr, "-Error with certificate at depth: %i\n", depth);
X509_NAME_oneline(X509_get_issuer_name(cert), data, DATA_SIZE_);
fprintf(stderr, " issuer = %s\n", data);
X509_NAME_oneline(X509_get_subject_name(cert), data, DATA_SIZE_);
fprintf(stderr, " subject = %s\n", data);
fprintf(stderr, " err %i:%s\n", err,
X509_verify_cert_error_string(err));
}
return ok;
}

long post_connection_check(SSL *ssl, char *host)
{
X509 *cert;
X509_NAME *subj;
char data[DATA_SIZE_];
//int extcount;
int ok = 1;

//Get peer certificate retrieves
if (!(cert = SSL_get_peer_certificate(ssl)) || !host)
{
if (cert)
{
X509_free(cert);
}
return X509_V_ERR_APPLICATION_VERIFICATION;
}
if (!ok && (subj = X509_get_subject_name(cert)) && X509_NAME_get_text_by_NID(subj, NID_commonName, data, DATA_SIZE_) > 0)
{
data[DEFAULT_DEPTH_ - 1] = 0;
if (strcasecmp(data, host) != 0)
{
if (cert)
{
X509_free(cert);
}

return X509_V_ERR_APPLICATION_VERIFICATION;
}
}
X509_free(cert);
return SSL_get_verify_result(ssl);
}

int write_to_SSL(SSL *ssl, const char* msg, int length)
{
int writtenbytes = 0;
int err = 0;

while(err >= 0 && writtenbytes < length)
{
err = SSL_write(ssl, msg + writtenbytes, length - writtenbytes);
if(err < 0)
{
return err;
}
else
{
writtenbytes += err;
}
}

return writtenbytes ;
}
int read_from_SSL(SSL *ssl, char* msg, int length)
{
int err = 0, readbytes = 0;

while(err > 0 && readbytes < length)
{
err = SSL_read(ssl, msg + readbytes, length - readbytes);

if(err < 0)
{
return err;
}
else
{
readbytes += err ;
}
}
return readbytes;
}


server.h:

#include "common.h"
#include <signal.h>

//If the key and the certificate are in the same file, these two can be the same
#define CERTFILE "/root/certificates/server.cert.pem"
#define KEYFILE "/root/certificates/server.key.pem"

//One of the two values below can be NULL but not both
#define CAFILE "/root/certificates/ca-chain.cert.pem"
#define CADIR NULL

//These are defined here so that we can do a cleanup in case of a SIGTERM signal
SSL_CTX *ctx = NULL;
BIO *acc = NULL;

void cleanup_(void) ;

//Does the setup of the server (loading SSL libraries, loading certificates, etc)
SSL_CTX *setup_server_ctx_(void) ;

//Exchange of data with the clien
int exchange_data_(SSL *ssl) ;

//Does the whole communication once the connection is established
void communicate_(SSL *ssl) ;

//Waits for clients, establishes the connection and then proceeds to
//call communicate_()
void run_server_(void) ;


server.c:

#include "server.h"
#include "logger.h"
#include "uartlib.h"
#include <sys/time.h>

void cleanup_(void)
{
if(ctx != NULL)
{
SSL_CTX_free(ctx);
}

if(acc != NULL)
{
BIO_free(acc);
}
}

static void signals_(int signo)
{
//Sigint to be removed if the server is daemon
printf("\nClosing the server!\n");
signal(SIGINT, signals_);
signal(SIGTERM, signals_);
fflush(stdout);
cleanup_();
exit(EXIT_FAILURE);
}

SSL_CTX *setup_server_ctx_(void)
{
SSL_CTX *ctx;

init_OpenSSL();

//This is my function, gotta investigate it and see what should be there (maybe I got it right?)
SEED_PRNG_();

// This specifies that either SSL or TLS can be used
// Later, we will "filter" out SSLv2
ctx = SSL_CTX_new(SSL_METHOD_);

// NULL return value indicates a failure in creation of SSL_CTX object
if(ctx == NULL)
{
int_error("Setup error: The creation of a new SSL_CTX object failed.");
}
SSL_CTX_set_options(ctx, SSL_CTX_FLAGS_);

// These two functions are used to load trusted CAs
if (SSL_CTX_load_verify_locations(ctx, CAFILE, CADIR) != 1)
{
int_error("Setup error: Error loading CA file and/or directory");
}
if (SSL_CTX_set_default_verify_paths(ctx) != 1)
{
int_error("Setup error: Error loading default CA file and/or directory");
}

// This loads a certificate from a file
if (SSL_CTX_use_certificate_chain_file(ctx, CERTFILE) != 1)
{
int_error("Setup error: Error loading certificate from file");
}
// This loads a private key (can be the same file as certificate)
if (SSL_CTX_use_PrivateKey_file(ctx, KEYFILE, SSL_FILETYPE_PEM) != 1)
{
int_error("Setup error: Error loading private key from file");
}
if (SSL_CTX_set_cipher_list(ctx, CIPHER_LIST) != 1)
{
int_error("Error setting cipher list (no valid ciphers)");
}
// Setting the verify options for ctx context
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback);
// Setting the maximum allowed depth for CA verification
SSL_CTX_set_verify_depth(ctx, DEFAULT_DEPTH_);

return ctx;
}

int exchange_data_(SSL *ssl)
{
int err;
err = write_to_SSL(ssl, "Hello, client!", strlen("Hello, client!"));

if (err <= 0)
{
printf("An unsuccessful write!");
}
else
{
printf("Sent %d bytes.\n", err);
}

// SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN != 0 indicate that the shutdown notification
// was sent from the peer (in this case, the client)
//close(uart_fd);
return (SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) ? 1 : 0;
}


void communicate_(SSL *ssl)
{
long err;
struct timeval tval_before, tval_after, tval_result;
//accepting connection from ssl object (structure)

if (SSL_accept(ssl) <= 0)
{
int_error("Error accepting SSL connection");
}
if ((err = post_connection_check(ssl, CLIENT)) != X509_V_OK)
{
int_error("Error checking SSL object after connection-Error: peer certificate");
//X509_verify_cert_error_string(err) takodje treba iskoristiti
}
fprintf(stderr, "SSL Connection opened\n");

if (exchange_data_(ssl))
{
SSL_shutdown(ssl);
}
else
{
SSL_clear(ssl);
}

fprintf(stderr, "SSL Connection closed\n");
SSL_free(ssl);
}


void run_server_(void)
{
BIO *client;
SSL *ssl;

//This call does the setup of the server context (see the function for more info)
ctx = setup_server_ctx_();

// Creates BIO and sets the accept port
acc = BIO_new_accept(PORT);
BIO_set_bind_mode(acc, BIO_BIND_REUSEADDR_IF_UNUSED);
if (!acc)
{
int_error("Error creating server socket");
}
//The first call to BIO_do_accept() binds to the given port
if (BIO_do_accept(acc) <= 0)
{
int_error("Error binding server socket");
}
for (;;)
{
//The second BIO_do_accept() call listens on the acc BIO
if (BIO_do_accept(acc) <= 0)
{
int_error("Error accepting connection from client");
}
client = BIO_pop(acc);
if (!(ssl = SSL_new(ctx)))
{
int_error("Error creating SSL context");
}
SSL_set_bio(ssl, client, client);
communicate_(ssl);
}


}

int main(int argc, char *argv[])
{
run_server_();
return 1;
}


Note: The code is mostly taken from O'Reilly book "Network Security with OpenSSL".
Also, this is not the whole code I have but it is the whole code related to OpenSSL so I didn't deem the other code relevant.
There could be some mistakes in the code due to the copy/paste.

Answer

After contacting the OpenSSL team, they concluded that the problem with the OpenSSL call BIO_do_accept() was in a call to socket(). The peculiar thing was that the socket() call seemed to be working fine when I wrote a simple server application of my own. So, they proposed a workaround: instead of using BIO calls, I am now using system calls. This reduced the portability of the code but, for my purposes, it doesn't matter.

The run_server_() function now looks like this:

void run_server_(void)
{
    BIO  *client;
    SSL *ssl;

    int     listenfd, connfd;
    socklen_t clilen;
    struct sockaddr_in cliaddr, servaddr;

    ctx = setup_server_ctx_();

    listenfd = socket (AF_INET, SOCK_STREAM, PROTOCOL);
    if(listenfd < 0)
    {
        printf("serv_socket unsuccessful\n");
        exit(EXIT_FAILURE);
    }
    printf("Created socket!\n");
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
    servaddr.sin_port = htons (atoi(PORT));

    if(bind(listenfd, (const struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
    {
        printf("bind() error\n");
        exit(EXIT_FAILURE);
    }
    printf("Binded port/socket!\n");

    if(listen(listenfd, LISTENQ) < 0)
    {
        printf("listen() error\n");
        exit(EXIT_FAILURE);
    }
    printf("Listening!\n");

    for(;;)
    {
        clilen = sizeof(cliaddr);
        connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen);
        if(connfd < 0)
        {
            printf("accept() error\n");
        }
        printf("Accepted!\n");
        client = BIO_new(BIO_s_socket());

    if (client == NULL)
        {
            printf("error creating BIO\n");
    }
    BIO_set_fd(client, connfd, BIO_NOCLOSE);
        if (!(ssl = SSL_new(ctx)))
        {
            server_error_("Error creating SSL context");
        }
        SSL_set_bio(ssl, client, client);
        communicate_(ssl);

        close(connfd);
    }
}
Comments