c - OpenSSL openwrt 套接字绑定(bind)失败

标签 c linux sockets ssl openssl

我在 Dragino Yun 上运行 openwrt。我的愿望是在 Dragino 上运行一个 openssl 服务器,用于传输一些(不是很多)数据。但是,在启动服务器并加载证书后,对 BIO_do_accept() 的调用返回值 <= 0,表明绑定(bind)失败。我尝试绑定(bind)的端口是 5354,但我什至尝试了 8081、8080、443。

我从 ERR_print_errors_fp 得到的错误信息是:

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

谁能解释为什么我的程序无法绑定(bind)?我已经在 Ubuntu 上对其进行了测试——这就是我没有发布任何代码的原因——(我遇到的问题是交叉编译版本)、OpenSSL 版本 OpenSSL 1.0.1f 6 Jan 2014 以及,在 Dragino 上,OpenSSL 版本为:OpenSSL 1.0.1h 2014 年 6 月 5 日。 此外,Dragino 版本(来自横幅)是 Dragino-v2 common-2.0.5

我尝试使用 s_server 结果如下:

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

当使用 s_client 时,它产生了一些通信,因此套接字绑定(bind)正常。

那么,问题是什么以及如何使它正常工作? 我尝试用谷歌搜索错误消息,但无济于事。

此外,我更改了 iptables,使其在 INPUT、FORWARD 和 OUTPUT 链上具有 ACCEPT 默认策略。

编辑:添加代码。

通用.h:

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

#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 init_OpenSSL(void) ;
int seed_prng(int bytes) ;
int verify_callback(int ok, X509_STORE_CTX *store) ;

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

通用.c:

#include "common.h"
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;
}


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;
}

服务器.h:

#include "common.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

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) ;

服务器.c:

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


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");
    }
    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;
}

注意:代码主要取自 O'Reilly 的书“Network Security with OpenSSL”。 此外,这不是我拥有的全部代码,而是与 OpenSSL 相关的全部代码,因此我认为其他代码不相关。 由于复制/粘贴,代码中可能存在一些错误。

最佳答案

在联系 OpenSSL 团队后,他们得出结论,OpenSSL 调用 BIO_do_accept() 的问题出在对 socket() 的调用中。奇怪的是,当我自己编写一个简单的服务器应用程序时,socket() 调用似乎工作正常。因此,他们提出了一个解决方法:我现在不使用 BIO 调用,而是使用系统调用。这降低了代码的可移植性,但就我的目的而言,这无关紧要。

run_server_() 函数现在看起来像这样:

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);
    }
}

关于c - OpenSSL openwrt 套接字绑定(bind)失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39141883/

相关文章:

c - 如何在 C 中处理给定的外部函数

c - 优化文字解析器

php - Laravel View 在 Linux 服务器上不工作

mysql - Django 与远程 MySQL

java - Java 中的非阻塞套接字写入与阻塞套接字写入

android - linux客户端与linux/windows服务器与android之间的socket编程

c - 使用 c 数组时堆栈溢出

C 队列,按排序顺序复制元素

PHP5.3(作为Apache模块)无法写入/var/www/<project-name>/<document-root>/cache

java - 通过套接字发送文件 - 缓冲区大小