简体   繁体   中英

Issue with network packet read on TLS1.3 | Openssl C program

openssl_1.1.1_perl_dump.txt

We are trying to upgrade on Openssl version 1.1.1, with security layer from TLS 1.2 to TLS 1.3.
Our code base performs SSL handshake, and later use send & read/recv function to transfer data. *Note - it is working fine for TLS 1.2. However when we switch to TLS 1.3, and data packets are not received for send & read/recv, but working only when we use SSL_write & SSL_read.

Below we have tried to make sample server & client program, where we use send & read/recv functions after successful SSL connection, here also it works fine for TLS 1.2, but in case of TLS 1.3 there is lag in packets received, the first response from server is blank, please refer outputs below. For out put of 'perl configdata.pm --dump' refer attachment. Used steps mentioned as per this reference article https://help.ubuntu.com/community/OpenSSL - to create SSL certificates.

server.c

#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <resolv.h>
#include "/usr/include/openssl/ssl.h"
#include "/usr/include/openssl/crypto.h"
#include "/usr/include/openssl/err.h"

#define FAIL    -1

int OpenListener(int port)
{   int sd;
    struct sockaddr_in addr;

    sd = socket(PF_INET, SOCK_STREAM, 0);
    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;
    if ( bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
    {
        perror("can't bind port");
        abort();
    }
    if ( listen(sd, 10) != 0 )
    {
        perror("Can't configure listening port");
        abort();
    }
    return sd;
}

int isRoot()
{
    if (getuid() != 0)
    {
        return 0;
    }
    else
    {
        return 1;
    }

}
SSL_CTX* InitServerCTX(void)
{   SSL_METHOD *method;
    SSL_CTX *ctx;

    OpenSSL_add_all_algorithms();  /* load & register all cryptos, etc. */
    SSL_load_error_strings();   /* load all error messages */

    char *imethod=getenv("TLS_METHOD");
    if(imethod!=NULL && strncmp(imethod, "1.3", 3)==0){
       printf("\nSetting TLS1.3 method\n");
       fflush(stdout);
       method = TLS_server_method();  /* create new server-method instance */
    }
    else{
       printf("\nSetting TLS1.2 method\n");
       fflush(stdout);
       method = TLSv1_2_server_method();  /* create new server-method instance */
    }
    ctx = SSL_CTX_new(method);   /* create new context from method */
    if ( ctx == NULL )
    {
        ERR_print_errors_fp(stderr);
        abort();
    }

    if(imethod!=NULL && strncmp(imethod, "1.3", 3)==0)
      SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2);

    return ctx;
}

void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
    SSL_CTX_load_verify_locations(ctx, "01.pem", "/home/qarun/aparopka/myCA/signedcerts");
    /* set the local certificate from CertFile */
    if ( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0 )
    {
        ERR_print_errors_fp(stderr);
        abort();
    }
    /* set the private key from KeyFile (may be the same as CertFile) */
    if ( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 )
    {
        ERR_print_errors_fp(stderr);
        abort();
    }
    /* verify private key */
    if ( !SSL_CTX_check_private_key(ctx) )
    {
        fprintf(stderr, "Private key does not match the public certificate\n");
        abort();
    }

    //New lines - Force the client-side have a certificate
    /*SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, NULL);
    SSL_CTX_set_verify_depth(ctx, 2);*/
    //End new lines
}

void ShowCerts(SSL* ssl)
{   X509 *cert;
    char *line;

    cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */
    if ( cert != NULL )
    {
        printf("Server certificates:\n");
        line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
        printf("Subject: %s\n", line);
        free(line);
        line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
        printf("Issuer: %s\n", line);
        free(line);
        X509_free(cert);
    }
    else
        printf("No certificates.\n");
}

void Servlet(SSL* ssl) /* Serve the connection -- threadable */
{   char buf[1024];
    char reply[1024];
    int sd, bytes;
    const char* HTMLecho="<html><body><pre>%s</pre></body></html>\n\n";

    sd = SSL_get_fd(ssl);       /* get socket connection */
    printf("\nsd: <%d>", sd);
    if ( SSL_accept(ssl) == FAIL )     /* do SSL-protocol accept */
        ERR_print_errors_fp(stderr);
    else
    {
        /*if(SSL_do_handshake(ssl) <= 0){
            printf("\n SSL_do_handshake failed....\n");
        }*/
        ShowCerts(ssl);        /* get any certificates */
        do{
                //bytes = SSL_read(ssl, buf, sizeof(buf)); /* get request */
                //bytes = read(sd, buf, sizeof(buf));
                bytes = recv(sd, buf, sizeof(buf), 0);
                if ( bytes > 0 )
                {
                        buf[bytes] = 0;
                        printf("Client msg: \"%s\"\n", buf);
                        sprintf(reply, HTMLecho, buf);   /* construct reply */
                        //SSL_write(ssl, reply, strlen(reply)); /* send reply */
                        send(sd, reply, strlen(reply), 0); // MSG_NOSIGNAL | MSG_DONTWAIT); //MSG_CONFIRM);
                }
                else
                        ERR_print_errors_fp(stderr);
        }while(bytes>0);
    }
    SSL_free(ssl);         /* release SSL state */
    close(sd);          /* close connection */
}

int main(int count, char *strings[])
{   SSL_CTX *ctx;
    int server;
    char *portnum;

    if(!isRoot())
    {
        printf("This program must be run as root/sudo user!!");
        //exit(0);
    }
    if ( count != 2 )
    {
        printf("Usage: %s <portnum>\n", strings[0]);
        exit(0);
    }
    SSL_library_init();

    portnum = strings[1];
    ctx = InitServerCTX();        /* initialize SSL */
    LoadCertificates(ctx, "/home/qarun/aparopka/myCA/server_crt.pem", "/home/qarun/aparopka/myCA/server_key.pem"); /* load certs */
    server = OpenListener(atoi(portnum));    /* create server socket */
    while (1)
    {   struct sockaddr_in addr;
        socklen_t len = sizeof(addr);
        SSL *ssl;

        int client = accept(server, (struct sockaddr*)&addr, &len);  /* accept connection as usual */
        printf("Connection: %s:%d\n",inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
        ssl = SSL_new(ctx);              /* get new SSL state with context */
        SSL_set_fd(ssl, client);      /* set connection socket to SSL state */
        Servlet(ssl);         /* service connection */
    }
    close(server);          /* close server socket */
    SSL_CTX_free(ctx);         /* release context */
}

client.c

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define FAIL    -1

//Added the LoadCertificates how in the server-side makes.
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
 /* set the local certificate from CertFile */
    if ( SSL_CTX_use_certificate_chain_file(ctx, CertFile ) <= 0 )
    {
        ERR_print_errors_fp(stderr);
        abort();
    }
    /* set the private key from KeyFile (may be the same as CertFile) */
    if ( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 )
    {
        ERR_print_errors_fp(stderr);
        abort();
    }
    /* verify private key */
    if ( !SSL_CTX_check_private_key(ctx) )
    {
        fprintf(stderr, "Private key does not match the public certificate\n");
        abort();
    }
}

int OpenConnection(const char *hostname, int port)
{   int sd;
    struct hostent *host;
    struct sockaddr_in addr;

    if ( (host = gethostbyname(hostname)) == NULL )
    {
        perror(hostname);
        abort();
    }
    sd = socket(PF_INET, SOCK_STREAM, 0);
    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = *(long*)(host->h_addr);
    if ( connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
    {
        close(sd);
        perror(hostname);
        abort();
    }
    return sd;
}

SSL_CTX* InitCTX(void)
{   SSL_METHOD *method;
    SSL_CTX *ctx;

    OpenSSL_add_all_algorithms();  /* Load cryptos, et.al. */
    SSL_load_error_strings();   /* Bring in and register error messages */
    char *imethod=getenv("TLS_METHOD");
    if(imethod!=NULL && strncmp(imethod, "1.3", 3)==0){
       printf("\nSetting TLS1.3 method\n");
       fflush(stdout);
       method = TLS_client_method();  /* create new server-method instance */
    }
    else{
       printf("\nSetting TLS1.2 method\n");
       fflush(stdout);
       method = TLSv1_2_client_method();  /* create new server-method instance */
    }

    ctx = SSL_CTX_new(method);   /* Create new context */
    if ( ctx == NULL )
    {
        ERR_print_errors_fp(stderr);
        abort();
    }

    if(imethod!=NULL && strncmp(imethod, "1.3", 3)==0)
        SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2);

    return ctx;
}

void ShowCerts(SSL* ssl)
{   X509 *cert;
    char *line;

    cert = SSL_get_peer_certificate(ssl); /* get the server's certificate */
    if ( cert != NULL )
    {
        printf("Server certificates:\n");
        line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
        printf("Subject: %s\n", line);
        free(line);       /* free the malloc'ed string */
        line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
        printf("Issuer: %s\n", line);
        free(line);       /* free the malloc'ed string */
        X509_free(cert);     /* free the malloc'ed certificate copy */
    }
    else
        printf("Info: No client certificates configured.\n");
}

int main(int count, char *strings[])
{   SSL_CTX *ctx;
    int server;
    SSL *ssl;
    char buf[1024];
    int bytes;
    char *hostname, *portnum;

    if ( count != 3 )
    {
        printf("usage: %s <hostname> <portnum>\n", strings[0]);
        exit(0);
    }
    SSL_library_init();
    hostname=strings[1];
    portnum=strings[2];

    ctx = InitCTX();
    LoadCertificates(ctx, "/home/qarun/aparopka/myCA/server_crt.pem", "/home/qarun/aparopka/myCA/server_key.pem");
    server = OpenConnection(hostname, atoi(portnum));
    ssl = SSL_new(ctx);      /* create new SSL connection state */
    SSL_set_fd(ssl, server);    /* attach the socket descriptor */
    int sd = SSL_get_fd(ssl);
    int flip=1;
    printf("\nsd: <%d>", sd);
    if ( SSL_connect(ssl) == FAIL )   /* perform the connection */
        ERR_print_errors_fp(stderr);
    else
    {   //char *msg = "Hello???";
        if(SSL_do_handshake(ssl) <= 0){
            printf("\n client SSL_do_handshake failed....\n");
        }
        char msg[1024];
        printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
        ShowCerts(ssl);        /* get any certs */
        while(1){
                bzero(msg, 1024);
                bzero(buf, 1024);
                printf("\nEnter message: ");
                fgets(msg, 1024, stdin);
                //SSL_write(ssl, msg, strlen(msg));   /* encrypt & send message */
                send(sd, msg, strlen(msg),  0); //MSG_NOSIGNAL | MSG_DONTWAIT); //MSG_CONFIRM);

                //bytes = SSL_read(ssl, buf, sizeof(buf)); /* get reply & decrypt */
                //bytes = read(sd, buf, sizeof(buf));
                bytes = recv(sd, buf, sizeof(buf), 0);

                  /* read again for TLS 1.3 only for first time */
                  /*char *imethod=getenv("TLS_METHOD");
                  if(imethod!=NULL && strncmp(imethod, "1.3", 3)==0
                       && flip){
                      bytes = read(sd, buf, sizeof(buf));
                      flip=0;
                  }*/

                buf[bytes] = 0;
                printf("Received: \"%s\"\n", buf);
        }
        SSL_free(ssl);        /* release connection state */
    }
    close(server);         /* close socket */
    SSL_CTX_free(ctx);        /* release context */
    return 0;
}

Server & client Output with TLS 1.2

**$ ./ssl_server_working 2023**
Setting TLS1.2 method
Connection: 10.250.14.23:41152

sd: <4>No certificates.
Client msg: "first
"
Client msg: "second
"
Client msg: "third
"

 **./ssl_client_working 10.250.14.23 2023**

Setting TLS1.2 method

sd: <3>Connected with ECDHE-RSA-AES256-GCM-SHA384 encryption
Server certificates:
Subject: /CN=MyOwn Root Certificate Authority/ST=CA/C=US/emailAddress=amit.paropkari@quest.com/O=SharePlex/OU=IT Department
Issuer: /CN=MyOwn Root Certificate Authority/ST=CA/C=US/emailAddress=amit.paropkari@quest.com/O=SharePlex/OU=IT Department

Enter message: first
Received: "<html><body><pre>first
</pre></body></html>

"

Enter message: second
Received: "<html><body><pre>second
</pre></body></html>

"

Enter message: third
Received: "<html><body><pre>third
</pre></body></html>

"

Server & client output for TLS 1.3: Note the response for first client request is blank, which is received in next response for second request.

 export TLS_METHOD=1.3
 **./ssl_server_working 2024**
Setting TLS1.3 method
Connection: 10.250.14.23:34012

sd: <4>No certificates.
Client msg: "first
"
Client msg: "second
"
Client msg: "third
"

**./ssl_client_working 10.250.14.23 2024**

Setting TLS1.3 method

sd: <3>Connected with TLS_AES_256_GCM_SHA384 encryption
Server certificates:
Subject: /CN=MyOwn Root Certificate Authority/ST=CA/C=US/emailAddress=amit.paropkari@quest.com/O=SharePlex/OU=IT Department
Issuer: /CN=MyOwn Root Certificate Authority/ST=CA/C=US/emailAddress=amit.paropkari@quest.com/O=SharePlex/OU=IT Department

Enter message: first
Received: ""

Enter message: second
Received: "<html><body><pre>first
</pre></body></html>

"

Enter message: third
Received: "<html><body><pre>second
</pre></body></html>

"

We got the answer for this. Turns out TLS 1.3 has added one more handshake step, where after Client sends Server finish message, server verifies it, & again sends post-handshake protocol level message, called as 'NewSessionTicket message'. We can disable it with SSL_CTX_set_num_tickets method, before initiating SSL server.

    SSL_set_num_tickets(ssl, 0);  /* Disable post-handshake token */
    sd = SSL_get_fd(ssl);       /* get socket connection */
    if ( SSL_accept(ssl) == FAIL )     /* do SSL-protocol accept */
        ERR_print_errors_fp(stderr);

As for people concerned about this not being secure, in our case of requirement we only need initial secure authentication, and then we do session tunneling. After that if we use SSL_write SSL_read for every transaction (which are in huge volume & time constraint), the encryption decryption step would degrade the performance. So we are using send & recv for sending data.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM