blob: f5b038f0cfe175ad5fa7f54dbe1d3fd3f654c32f [file] [log] [blame] [raw]
/*
copyright: Boaz segev, 2016
license: MIT
Feel free to copy, use and enjoy according to the license provided.
*/
#include "lib-tls-server.h"
/* Only available if we have OpenSSL */
#ifdef SSL_VERIFY_PEER
#define PRINT_MESSAGES 1
/* We have OpenSSL - let's do this :-) */
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
/*******************************************************************************
Function declerations
*/
/** Used to initialize the TLS/SSL hooks from within the on_init callback. */
static void init_server(server_pt srv);
/** Used to update the sever settings with a new TLS/SSL Protocol and hooks.
* (this will also setup the on_init callback and make sure that the original
* callback is called as well).
*/
static void update_settings(struct ServerSettings* settings);
/*******************************************************************************
The API object
*/
struct TLSServer_API___ TLSServer = {
.init_server = init_server,
.update_settings = update_settings,
};
/*******************************************************************************
The TLS Protocol and it's components
*/
struct TLSConnectionData {
/* temp data feilds. */
int fd;
/* state flags require only a single bit... */
};
struct TLSProtocol {
/** The TLS protocol must be the first element, for poiner inheritance. */
struct Protocol tls;
/** The clear-text protocol to be wrapped in the TLS layer. */
struct Protocol* original_protocol;
/** The original on_init callback, if any. */
void (*on_init)(server_pt server);
void (*on_finish)(server_pt server);
/** The connection data array. */
struct TLSConnectionData* data;
/** The original timeout (to be used after the handshake). */
unsigned char timeout;
};
#define tls_protocol(srv) \
((struct TLSProtocol*)(Server.settings(srv)->protocol))
/*******************************************************************************
Protocol and Server callbacks
*/
static ssize_t writing_hook(server_pt srv, int fd, void* data, size_t len) {
if (PRINT_MESSAGES)
fprintf(stderr, "Sending in the clear (%ld bytes): %.*s\n", len, (int)len,
(char*)data);
int sent = write(fd, data, len);
if (sent < 0 && (errno & (EWOULDBLOCK | EAGAIN | EINTR)))
sent = 0;
return sent;
}
static ssize_t reading_hook(server_pt srv, int fd, void* buffer, size_t size) {
ssize_t read = 0;
if ((read = recv(fd, buffer, size, 0)) > 0) {
if (PRINT_MESSAGES)
fprintf(stderr, "Got clear text (%ld bytes): %.*s\n", read, (int)read,
(char*)buffer);
return read;
} else {
if (read && (errno & (EWOULDBLOCK | EAGAIN)))
return 0;
}
return -1;
}
static void on_open(server_pt srv, int fd) {
if (PRINT_MESSAGES)
fprintf(stderr, "A new TLS connection? sorry, not implemented\n");
}
static void on_close(server_pt srv, int fd) {
if (PRINT_MESSAGES)
fprintf(stderr, "SSL/TLS handshake failed.\n");
}
static void on_data(server_pt srv, int fd) {
if (PRINT_MESSAGES)
fprintf(stderr, "TLS connections aren't implemented, updating protocol.\n");
struct TLSProtocol* tls = tls_protocol(srv);
if (!tls)
return;
// hook read-write
Server.rw_hooks(srv, fd, reading_hook, writing_hook);
// give the connection control to the original protocol
if (Server.set_protocol(srv, fd, tls->original_protocol)) {
if (PRINT_MESSAGES)
fprintf(stderr, "SSL/TLS ERROR: cannot set the connection's protocol\n");
return;
};
// set the correct timeout
Server.set_timeout(srv, fd, tls->timeout);
// call callbacks
if (tls->original_protocol->on_open)
tls->original_protocol->on_open(srv, fd);
if (tls->original_protocol->on_data) // in case there's data to be read.
tls->original_protocol->on_data(srv, fd);
}
static void on_ready(server_pt srv, int fd) {
// Should we continue the OpenSSL `accept` here? ... it requires writing as
// well as reading...
if (PRINT_MESSAGES)
fprintf(stderr,
"SSL/TLS handshake should continue? only if it isn't running in "
"parallel...\n");
}
static void on_finish(server_pt srv) {
struct TLSProtocol* tls = tls_protocol(srv);
if (tls->on_finish)
tls->on_finish(srv);
// free the connection data array
if (tls->data) {
free(tls->data);
tls->data = NULL;
}
// free the protocol
free(tls);
}
/*******************************************************************************
Global initialization
*/
static void global_library_init() {
volatile static char initialized = 0;
if (initialized)
return;
// this isn't a mutex, but it's a good enough solution for the unlikely
// chance of a race condition.
for (size_t i = 0; i < 8; i++) {
if (initialized >> i)
return;
initialized |= (1 << i);
}
if (PRINT_MESSAGES)
fprintf(stderr, "Initializing TLS library.\n");
// call the initialization functions
SSL_library_init(); // or OPENSSL_init_ssl() in version 1.1.0
// OpenSSL_add_all_algorithms(); // - do we need this?
}
/*******************************************************************************
Function implementation
*/
/** Used to initialize the TLS/SSL hooks from within the on_init callback. */
static void init_server(server_pt srv) {
if (PRINT_MESSAGES)
fprintf(stderr, "Initializing TLS layer.\n");
struct ServerSettings* settings = Server.settings(srv);
settings->on_init = NULL;
update_settings(settings);
// call the original on_init callback, unless it's the same as this function.
if (tls_protocol(srv)->on_init && tls_protocol(srv)->on_init != init_server)
tls_protocol(srv)->on_init(srv);
}
/** Used to update the sever settings with a new TLS/SSL Protocol and hooks.
* (this will also setup the on_init callback and make sure that the original
* callback is called as well).
*/
static void update_settings(struct ServerSettings* settings) {
if (settings->on_init == init_server) // settings already updated.
return;
// initialize the library
global_library_init();
// create a new TLSProtocol object and initialize the data
struct TLSProtocol* tls = malloc(sizeof(*tls));
if (!tls) {
perror("Cannot allocate memory");
exit(1); // we do not fail quitely.
}
tls->data = calloc(Server.capacity(), sizeof(*(tls->data)));
if (!tls->data) {
free(tls);
perror("Cannot allocate memory");
exit(1); // we do not fail quitely.
}
// initialize the TLSProtocol data.
tls->tls.on_open = on_open;
tls->tls.on_data = on_data;
tls->tls.on_ready = on_ready;
tls->tls.on_close = on_close;
tls->on_init = settings->on_init;
tls->on_finish = settings->on_finish;
tls->original_protocol = settings->protocol;
tls->timeout = settings->timeout;
// update the settings
settings->protocol = (void*)tls;
settings->on_finish = on_finish;
settings->on_init = init_server;
settings->timeout = 5;
}
/* End OpenSSL available section*/
#endif /* SSL_VERIFY_PEER */