csslopensslnetwork-programminghandshake

How to add a custom extension in ClientHello message from C client application?


I am trying to add a custom extension in ClientHello from a C client socket program which sends HTTP request over TLSv1.3 to Apache web server. I have tried calling the SSL_CTX_add_custom_ext() function from OpenSSL to add a random extension type of 65280, but am not sure on how to correctly define the add_cb callback. The documentation (https://www.openssl.org/docs/manmaster/man3/SSL_CTX_add_custom_ext.html) says to set out to the extension data, set outlen to extension data length and return 1, which i tried (most likely wrong). The extension data is just a string, "testing".

Using wireshark, i can see an unknown extension type 65280 under the handshake protocol, but the length is 0 and data is shown as missing, as shown below. How do i properly add the custom extension such that it shows up with the data in wireshark.

missing data

Below is my client C code that sends a HTTP request to Apache web server over TLSv1.3:

#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include "openssl/ssl.h"
#include "openssl/err.h"

SSL_CTX* InitCTX(void)
{
    const SSL_METHOD *method;
    SSL_CTX *ctx;
    OpenSSL_add_all_algorithms();  
    SSL_load_error_strings();   
    method = TLS_client_method();  
    ctx = SSL_CTX_new(method);  
    if (ctx == NULL) {
        ERR_print_errors_fp(stderr);
        abort();
    }
    return ctx;
}

static int ext_add_cb(SSL *s, unsigned int ext_type, unsigned int context, const unsigned char **out, size_t *outlen, X509 *x, size_t chainidx, int *al, void *add_arg)
{
    printf("in ext_add_cb\n");
    unsigned char data[] = "testing";
    unsigned char *pdata = &data;
    size_t len = strlen(data) + 1;
    out = (const unsigned char **)&pdata;
    outlen = &len;
    return 1;
}

static void ext_free_cb(SSL *s, unsigned int ext_type, unsigned int context, const unsigned char *out, void *add_arg)
{
    printf("in ext_free_cb\n");
    OPENSSL_free((unsigned char *)out);
}

static int ext_parse_cb(SSL *s, unsigned int ext_type, unsigned int context, const unsigned char *in, size_t inlen, X509 *x, size_t chainidx, int *al, void *parse_arg)
{
    printf("in ext_parse_cb\n");
    return 1;
}


int main()
{
    struct sockaddr_in servaddr; 
    char *host = "127.0.0.1";
    char *port = "443";
    int sockfd = 0; 
    SSL_CTX * ctx = NULL;
    SSL * ssl = NULL;

    servaddr.sin_family = AF_INET; 
    servaddr.sin_port = htons(atoi(port)); 
    servaddr.sin_addr.s_addr = inet_addr(host); 

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("socket error \n");
        return -1;
    }

    if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
        printf("connection failed \n");
        return -1;
    }

    SSL_library_init(); 
    OpenSSL_add_all_algorithms();  
    SSL_load_error_strings();  
    ctx = InitCTX();

    int status = SSL_CTX_add_custom_ext(ctx, 65280, SSL_EXT_CLIENT_HELLO, 
                                        ext_add_cb, ext_free_cb, NULL, ext_parse_cb, NULL);
    if (status != 1) {
        printf("SSL_CTX_add_custom_ext() failed\n");
    }

    ssl = SSL_new(ctx);     
    SSL_set_fd(ssl, sockfd);   

    if (SSL_connect(ssl) == -1) {   
        printf("SSL_connect() failed\n");
        return -1;
    }

    // ssl write HTTP request
    char msg[] = "GET / HTTP/1.1\r\nHost: 127.0.0.1\r\nAccept: */*\r\n\r\n";
    int bytes = SSL_write(ssl, msg, strlen(msg));

    // ssl read HTTP response
    char buf[1024] = {0};
    bytes = SSL_read(ssl, buf, sizeof(buf) - 1);
    buf[bytes] = '\0';
    printf("Received: %s\n", buf);


    // closing the connected socket
    SSL_free(ssl);
    close(sockfd);
    SSL_CTX_free(ctx);
    return 0;
}


Solution

  • I managed to fix the issue. The issue is indeed with my extension callback add_cb() definition. OPENSSL_malloc() needs to be called to allocate memory and return a pointer to the allocated memory. This pointer should be set to an unsigned char * pointer variable and thereafter data may be assigned to the memory.

    Below is a simple example of setting extension data in a add_cb() callback.

    static int ext_add_cb(SSL *s, unsigned int ext_type, unsigned int context, const unsigned char **out, size_t *outlen, X509 *x, size_t chainidx, int *al, void *add_arg)
    {
        unsigned char *p_data = NULL;
        p_data = OPENSSL_malloc(sizeof(*p_data));
        if (p_data == NULL) {
            return 0;
        }
        *p_data = 1;
        *out = p_data;
        *outlen = sizeof(*p_data);
        return 1;
    }