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