diff --git a/Makefile b/Makefile index ab46c8e..ff13371 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ -.PHONY: all clean test testclient install parsers +.PHONY: all clean test_s test_c install parsers all: luksrku BUILD_REVISION := $(shell git describe --abbrev=10 --dirty --always --tags) INSTALL_PREFIX := /usr/local/ CFLAGS := -Wall -Wextra -Wshadow -Wswitch -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Werror=implicit-function-declaration -Werror=format -Wno-unused-parameter -CFLAGS += -O3 -std=c11 -pthread -D_POSIX_SOURCE -D_XOPEN_SOURCE=500 -DBUILD_REVISION='"$(BUILD_REVISION)"' +CFLAGS += -O3 -std=c11 -pthread -D_POSIX_SOURCE -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=500 -DBUILD_REVISION='"$(BUILD_REVISION)"' CFLAGS += `pkg-config --cflags openssl` CFLAGS += -ggdb3 -DDEBUG -fsanitize=address -fsanitize=undefined -fsanitize=leak PYPGMOPTS := ../Python/pypgmopts/pypgmopts @@ -26,14 +26,11 @@ install: all clean: rm -f $(OBJS) $(OBJS_CFG) luksrku -test: luksrku +test_s: luksrku ./luksrku server -vv base -gdb: luksrku - gdb --args ./luksrku -v --server-mode -k server_key.bin - -testclient: luksrku - ./luksrku -v --client-mode -k client_keys.bin +test_c: luksrku + ./luksrku client -vv export 127.0.0.1 .c.o: $(CC) $(CFLAGS) -c -o $@ $< diff --git a/client.c b/client.c index 38cf557..c260f7e 100644 --- a/client.c +++ b/client.c @@ -29,6 +29,10 @@ #include #include #include +#include +#include +#include + #include "log.h" #include "openssl.h" @@ -36,6 +40,7 @@ #include "msg.h" #include "client.h" #include "blacklist.h" +#include "keydb.h" #if 0 static const struct keydb_t *client_keydb; @@ -207,6 +212,142 @@ static bool tls_client(const struct keydb_t *keydb, const struct options_t *opti } #endif -bool keyclient_start(const struct pgmopts_client_t *opts) { +struct keyclient_t { + const struct pgmopts_client_t *opts; + struct keydb_t *keydb; + bool volume_unlocked[MAX_VOLUMES_PER_HOST]; +}; + +static int psk_client_callback(SSL *ssl, const EVP_MD *md, const unsigned char **id, size_t *idlen, SSL_SESSION **sess) { + fprintf(stderr, "CLIENT CALLBACK\n"); + return 0; +} + +static bool contact_keyserver_socket(struct keyclient_t *keyclient, int sd) { + struct generic_tls_ctx_t gctx; + if (!create_generic_tls_context(&gctx, false)) { + log_msg(LLVL_FATAL, "Failed to create OpenSSL client context."); + return false; + } + SSL_CTX_set_psk_use_session_callback(gctx.ctx, psk_client_callback); + + SSL *ssl = SSL_new(gctx.ctx); + if (ssl) { + SSL_set_fd(ssl, sd); + SSL_set_app_data(ssl, keyclient); + + if (SSL_connect(ssl) == 1) { + fprintf(stderr, "OK\n"); + } else { + log_openssl(LLVL_FATAL, "SSL_connect failed"); + } + + } else { + log_openssl(LLVL_FATAL, "Cannot establish SSL context when trying to connect to server"); + } + + SSL_free(ssl); + free_generic_tls_context(&gctx); return true; } + +static bool contact_keyserver_ipv4(struct keyclient_t *keyclient, struct sockaddr_in *sockaddr_in, unsigned int port) { + sockaddr_in->sin_port = htons(port); + + int sd = socket(sockaddr_in->sin_family, SOCK_STREAM, 0); + if (sd == -1) { + log_libc(LLVL_ERROR, "Failed to create socket(3)"); + return false; + } + + if (connect(sd, (struct sockaddr*)sockaddr_in, sizeof(struct sockaddr_in)) == -1) { + log_libc(LLVL_ERROR, "Failed to connect(3) to %d.%d.%d.%d:%d", PRINTF_FORMAT_IP(sockaddr_in), port); + close(sd); + return false; + } + + bool success = contact_keyserver_socket(keyclient, sd); + + shutdown(sd, SHUT_RDWR); + close(sd); + return success; +} + +static bool contact_keyserver_hostname(struct keyclient_t *keyclient, const char *hostname) { + struct addrinfo hints = { + .ai_family = AF_INET, + .ai_socktype = SOCK_STREAM, + }; + struct addrinfo *result; + int resolve_result = getaddrinfo(hostname, NULL, &hints, &result); + if (resolve_result) { + log_msg(LLVL_ERROR, "Failed to resolve hostname %s using getaddrinfo(3): %s", hostname, gai_strerror(resolve_result)); + return false; + } + + if (result->ai_addr->sa_family != AF_INET) { + freeaddrinfo(result); + log_msg(LLVL_ERROR, "getaddrinfo(3) returned non-IPv4 entry"); + return false; + } + + struct sockaddr_in *sin_address = (struct sockaddr_in*)result->ai_addr; + log_msg(LLVL_DEBUG, "Resolved %s to %d.%d.%d.%d", hostname, PRINTF_FORMAT_IP(sin_address)); + + bool success = contact_keyserver_ipv4(keyclient, sin_address, keyclient->opts->port); + + freeaddrinfo(result); + return success; +} + +bool keyclient_start(const struct pgmopts_client_t *opts) { + /* Load key database first */ + struct keyclient_t keyclient = { + .opts = opts, + }; + bool success = true; + + do { + keyclient.keydb = keydb_read(opts->filename); + if (!keyclient.keydb) { + log_msg(LLVL_FATAL, "Failed to load key database: %s", opts->filename); + success = false; + break; + } + + if (keyclient.keydb->server_database) { + log_msg(LLVL_FATAL, "Not an exported key database: %s -- this database contains LUKS passphrases, refusing to work with it!", opts->filename); + success = false; + break; + } + + if (keyclient.keydb->host_count != 1) { + log_msg(LLVL_FATAL, "Host count %d in %s -- expected exactly one host entry for an exported database.", keyclient.keydb->host_count, opts->filename); + success = false; + break; + } + + struct host_entry_t *host = &keyclient.keydb->hosts[0]; + if (host->volume_count == 0) { + log_msg(LLVL_FATAL, "No volumes found in exported database %s.", opts->filename); + success = false; + break; + } + + log_msg(LLVL_DEBUG, "Attempting to unlock %d volumes of host \"%s\".", host->volume_count, host->host_name); + if (opts->hostname) { + if (!contact_keyserver_hostname(&keyclient, opts->hostname)) { + log_msg(LLVL_ERROR, "Failed to contact key server: %s", opts->hostname); + success = false; + break; + } + } else { + /* TODO: Loop until keyserver found */ + } + } while (false); + + if (keyclient.keydb) { + keydb_free(keyclient.keydb); + } + return success; +} diff --git a/server.c b/server.c index ebcfd6f..422a337 100644 --- a/server.c +++ b/server.c @@ -330,92 +330,101 @@ static void client_handler_thread(void *vctx) { struct client_ctx_t *client = (struct client_ctx_t*)vctx; SSL *ssl = SSL_new(client->gctx->ctx); - SSL_set_fd(ssl, client->fd); - SSL_set_app_data(ssl, client); + if (ssl) { + SSL_set_fd(ssl, client->fd); + SSL_set_app_data(ssl, client); - if (SSL_accept(ssl) <= 0) { - ERR_print_errors_fp(stderr); - } else { - if (client->host) { - log_msg(LLVL_DEBUG, "Client \"%s\" connected, sending unlock data for %d volumes.", client->host->host_name, client->host->volume_count); - for (unsigned int i = 0; i < client->host->volume_count; i++) { - const struct volume_entry_t *volume = &client->host->volumes[i]; - - struct msg_t msg = { 0 }; - memcpy(msg.volume_uuid, volume->volume_uuid, 16); - memcpy(msg.luks_passphrase_raw, volume->luks_passphrase_raw, LUKS_PASSPHRASE_RAW_SIZE_BYTES); - - int txlen = SSL_write(ssl, &msg, sizeof(msg)); - OPENSSL_cleanse(&msg, sizeof(msg)); - if (txlen != sizeof(msg)) { - log_msg(LLVL_WARNING, "Tried to send message of %d bytes, but sent %d. Severing connection to client.", sizeof(msg), txlen); - break; - } - } + if (SSL_accept(ssl) <= 0) { + ERR_print_errors_fp(stderr); } else { - log_msg(LLVL_FATAL, "Client connected, but no host set."); - } - } + if (client->host) { + log_msg(LLVL_DEBUG, "Client \"%s\" connected, sending unlock data for %d volumes.", client->host->host_name, client->host->volume_count); + for (unsigned int i = 0; i < client->host->volume_count; i++) { + const struct volume_entry_t *volume = &client->host->volumes[i]; + struct msg_t msg = { 0 }; + memcpy(msg.volume_uuid, volume->volume_uuid, 16); + memcpy(msg.luks_passphrase_raw, volume->luks_passphrase_raw, LUKS_PASSPHRASE_RAW_SIZE_BYTES); + + int txlen = SSL_write(ssl, &msg, sizeof(msg)); + OPENSSL_cleanse(&msg, sizeof(msg)); + if (txlen != sizeof(msg)) { + log_msg(LLVL_WARNING, "Tried to send message of %d bytes, but sent %d. Severing connection to client.", sizeof(msg), txlen); + break; + } + } + } else { + log_msg(LLVL_FATAL, "Client connected, but no host set."); + } + } + } else { + log_openssl(LLVL_FATAL, "Cannot establish SSL context for connecting client"); + } SSL_free(ssl); shutdown(client->fd, SHUT_RDWR); close(client->fd); } bool keyserver_start(const struct pgmopts_server_t *opts) { - /* Load key database first */ - struct keydb_t* keydb = keydb_read(opts->filename); - if (!keydb) { - log_msg(LLVL_FATAL, "Failed to load key database: %s", opts->filename); - return false; - } - - if (!keydb->server_database) { - log_msg(LLVL_FATAL, "Not a server key database: %s", opts->filename); - keydb_free(keydb); - return false; - } - - struct generic_tls_ctx_t gctx; - if (!create_generic_tls_context(&gctx, true)) { - log_msg(LLVL_FATAL, "Failed to create OpenSSL server context."); - return false; - } - - SSL_CTX_set_psk_find_session_callback(gctx.ctx, psk_server_callback); - - int tcp_sock = create_tcp_server_socket(opts->port); - if (tcp_sock == -1) { - log_msg(LLVL_ERROR, "Cannot start server without server socket."); - free_generic_tls_context(&gctx); - return false; - } - - while (true) { - struct sockaddr_in addr; - unsigned int len = sizeof(addr); - int client = accept(tcp_sock, (struct sockaddr*)&addr, &len); - if (client < 0) { - log_libc(LLVL_ERROR, "Unable to accept(2)"); - close(tcp_sock); - free_generic_tls_context(&gctx); - return false; + bool success = true; + struct keydb_t* keydb = NULL; + struct generic_tls_ctx_t gctx = { 0 }; + do { + /* Load key database first */ + keydb = keydb_read(opts->filename); + if (!keydb) { + log_msg(LLVL_FATAL, "Failed to load key database: %s", opts->filename); + success = false; + break; } - /* Client has connected, fire up client thread. */ - struct client_ctx_t client_ctx = { - .gctx = &gctx, - .keydb = keydb, - .fd = client, - }; - if (!pthread_create_detached_thread(client_handler_thread, &client_ctx, sizeof(client_ctx))) { - log_libc(LLVL_FATAL, "Unable to pthread_attr_init(3)"); - close(tcp_sock); - free_generic_tls_context(&gctx); - return false; + if (!keydb->server_database) { + log_msg(LLVL_FATAL, "Not a server key database: %s", opts->filename); + success = false; + break; } - } + if (!create_generic_tls_context(&gctx, true)) { + log_msg(LLVL_FATAL, "Failed to create OpenSSL server context."); + success = false; + break; + } + + SSL_CTX_set_psk_find_session_callback(gctx.ctx, psk_server_callback); + + int tcp_sock = create_tcp_server_socket(opts->port); + if (tcp_sock == -1) { + log_msg(LLVL_ERROR, "Cannot start server without server socket."); + success = false; + break; + } + + while (true) { + struct sockaddr_in addr; + unsigned int len = sizeof(addr); + int client = accept(tcp_sock, (struct sockaddr*)&addr, &len); + if (client < 0) { + log_libc(LLVL_ERROR, "Unable to accept(2)"); + close(tcp_sock); + free_generic_tls_context(&gctx); + return false; + } + + /* Client has connected, fire up client thread. */ + struct client_ctx_t client_ctx = { + .gctx = &gctx, + .keydb = keydb, + .fd = client, + }; + if (!pthread_create_detached_thread(client_handler_thread, &client_ctx, sizeof(client_ctx))) { + log_libc(LLVL_FATAL, "Unable to pthread_attr_init(3)"); + close(tcp_sock); + free_generic_tls_context(&gctx); + return false; + } + } + } while (false); free_generic_tls_context(&gctx); - return true; + keydb_free(keydb); + return success; }