Using LibreSSL to make a HTTPS call
12 Aug 2018
↪ What is OpenSSL?
Back in 1998 the OpenSSL project released an open source library that contains software routines and algorithms to setup and secure internet communication. Twenty years later, in 2018, OpenSSL is still widely spread around in operating systems, browsers, e-mail clients and so on. According to GitHub the library is mostly written in C (77%) and Perl (19%), some core functionality is written in Assembly (0.7%).
↪ Why LibreSSL?
In April 2014 a security bug - publicly called Heartbleed - affected OpenSSL’s implementation of the TLS protocol.
Due to missing boundary checks within the input validation of a change already introduced in December 2011 it was possible to remotely exploit memory of the server containing sensitive information, most notably private keys. Since the library was (and probably is) used in all major *nix distributions and derivates the list of the affected operating systems was long.
As on of the main reasons for the unspotted security hole the OpenBSD project critized OpenSSL’s old and outdated codebase, a good example is the following line of C Code:
#if defined(OPENSSL_SYS_MSDOS) || defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_WIN16) || defined(OPENSSL_SYS_NETWARE)
It was one of the very first lines that were removed by the OpenBSD fork of OpenSSL - called LibreSSL. The project goals were clear:
- Modernize the codebase
- Improve security
- Apply OpenBSD’s best practice development process
↪ The Code
As a C novice I always wondered how complicated it could be to basically use the LibreSSL library compared to the OpenSSL one.
For a project at work I wanted to do some basic HTTP 1.1 GET calls to a remote webserver. In C this basically has the following steps:
- Create a socket(2) and connect(2) to it
- Write a static buffer containing the actuall http request using send(2).
(Btw. in my case I do not give a dime about the reply the server sends.)
Since our actual goal is to run a request using https we have to include either OpenSSL or LibreSSL into our program, I found some examples using OpenSSL on the web:
- sslconnect.c - which connects fine but lacks of encrypted send functionality, also very long
- Stackoverlow - Server implementation, also very long
- Another Stackoverflow - Very long and flaky user implementation of a client, very low motivation to read this
↪ LibreSSL to the rescue
Clone the git repository
% git clone https://github.com/libressl-portable/portable.git
Cloning into 'portable'...
remote: Counting objects: 4582, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 4582 (delta 4), reused 9 (delta 3), pack-reused 4569
Receiving objects: 100% (4582/4582), 1.28 MiB | 984.00 KiB/s, done.
Resolving deltas: 100% (2804/2804), done.
Checking connectivity... done.
Within the project’s root run ./autogen which automatically checks out files from the OpenBSD source tree that are needed to build the library:
% cd portable
portable % ./autogen.sh
pulling upstream openbsd source
Cloning into 'openbsd'...
remote: Counting objects: 94602, done.
remote: Compressing objects: 100% (276/276), done.
remote: Total 94602 (delta 282), reused 440 (delta 255), pack-reused 94016
Receiving objects: 100% (94602/94602), 30.41 MiB | 1.60 MiB/s, done.
Resolving deltas: 100% (69075/69075), done.
Checking connectivity... done.
Already on 'master'
Your branch is up-to-date with 'origin/master'.
Current branch master is up to date.
libcrypto version 43:1:0
libssl version 45:1:0
libtls version 17:1:0
LibreSSL version 2.8.0
copying libcrypto source
generating ASM source for elf
[...]
- Run ./configure and make
- No need to globally install, just link your program and do not forget to extend your library path
% clang $(CFLAGS) -o run main.c -L<PATH_TO_LIBRESSL>/portable/tls/.libs -ltls % export LD_LIBRARY_PATH=<PATH_TO_LIBRESSL>/portable/tls/.libs:$LD_LIBRARY_PATH
↪ The program
#include "libressl/include/tls.h"
#define BUF_SIZ 4096
int
main (void)
{
// The HTTP request
char request[BUF_SIZ] = "GET / HTTP/1.1\r\nHost: localhost\r\nConnection: Close\r\n\r\n";
// LibreSSL config & client aka context
struct tls_config *config;
struct tls *ctx;
// TLS Setup
config = tls_config_new();
// Since localhost does neither have a valid cert
// nor does the common name match we disable
// all X.509 validation
tls_config_insecure_noverifycert(config);
tls_config_insecure_noverifyname(config);
tls_config_insecure_noverifytime(config);
// Setup our TLS client with the config form above
ctx = tls_client();
tls_configure(ctx, config);
// Creates the encrypted socket
tls_connect(ctx, "localhost", "443");
// Writes to the encrypted socket aka send the request
tls_write(ctx, request, BUF_SIZ);
// Cleanup
tls_close(ctx);
tls_free(ctx);
return 0;
}
After compiling and running the program your webserver log should state something like this:
127.0.0.1 - - [03/Aug/2018:18:52:57 +0200] "GET / HTTP/1.1" 200 2022 "-" "-"
↪ Lessons learned
- Less is more, within ~20 lines of code we successfully ran a HTTPS request against our webserver
- I do now know how to link a library which has not been installed globally
- The documentation of the LibreSSL project is great and easy to understand
- No need to use OpenSSL anymore