diff --git a/Makefile b/Makefile index f1965f1..a74efca 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ CFLAGS += -ggdb3 -DDEBUG -fsanitize=address -fsanitize=undefined -fsanitize=leak PYPGMOPTS := ../Python/pypgmopts/pypgmopts LDFLAGS := `pkg-config --libs openssl` +TEST_PREFIX := local OBJS := \ argparse_client.o \ @@ -18,9 +19,11 @@ OBJS := \ blacklist.o \ client.o \ editor.o \ + exec.o \ file_encryption.o \ keydb.o \ log.o \ + luks.o \ luksrku.o \ openssl.o \ pgmopts.o \ @@ -45,10 +48,10 @@ clean: rm -f $(OBJS) $(OBJS_CFG) luksrku test_s: luksrku - ./luksrku server -vv testdata/server.bin + ./luksrku server -vv testdata/$(TEST_PREFIX)_server.bin test_c: luksrku - ./luksrku client -vv --no-luks testdata/client.bin + ./luksrku client -vv --no-luks testdata/$(TEST_PREFIX)_client.bin .c.o: $(CC) $(CFLAGS) -c -o $@ $< diff --git a/blacklist.c b/blacklist.c index b2e87c7..2407919 100644 --- a/blacklist.c +++ b/blacklist.c @@ -31,14 +31,14 @@ static struct blacklist_entry_t blacklist[BLACKLIST_ENTRY_COUNT]; static bool blacklist_entry_expired(int index) { - return now() > blacklist[index].entered + BLACKLIST_ENTRY_TIMEOUT_SECS; + return now() > blacklist[index].timeout; } -void blacklist_ip(uint32_t ip) { +void blacklist_ip(uint32_t ip, unsigned int timeout_seconds) { for (int i = 0; i < BLACKLIST_ENTRY_COUNT; i++) { if (blacklist_entry_expired(i)) { blacklist[i].ip = ip; - blacklist[i].entered = now(); + blacklist[i].timeout = now() + timeout_seconds; return; } } diff --git a/blacklist.h b/blacklist.h index f358ea0..27bf2b3 100644 --- a/blacklist.h +++ b/blacklist.h @@ -28,15 +28,14 @@ #include #define BLACKLIST_ENTRY_COUNT 32 -#define BLACKLIST_ENTRY_TIMEOUT_SECS 15 struct blacklist_entry_t { uint32_t ip; - double entered; + double timeout; }; /*************** AUTO GENERATED SECTION FOLLOWS ***************/ -void blacklist_ip(uint32_t ip); +void blacklist_ip(uint32_t ip, unsigned int timeout_seconds); bool is_ip_blacklisted(uint32_t ip); /*************** AUTO GENERATED SECTION ENDS ***************/ diff --git a/client.c b/client.c index 21da4ab..9e21a39 100644 --- a/client.c +++ b/client.c @@ -43,6 +43,7 @@ #include "keydb.h" #include "uuid.h" #include "udp.h" +#include "luks.h" struct keyclient_t { const struct pgmopts_client_t *opts; @@ -60,17 +61,26 @@ static int psk_client_callback(SSL *ssl, const EVP_MD *md, const unsigned char * return openssl_tls13_psk_establish_session(ssl, key_client->keydb->hosts[0].tls_psk, PSK_SIZE_BYTES, EVP_sha256(), sessptr); } -static bool do_unlock_luks_volume(const struct volume_entry_t *volume, const struct msg_t *unlock_msg) { - return true; +static bool unlock_luks_volume(const struct volume_entry_t *volume, const struct msg_t *unlock_msg) { + bool success = true; + char luks_passphrase[LUKS_PASSPHRASE_TEXT_SIZE_BYTES]; + if (ascii_encode(luks_passphrase, sizeof(luks_passphrase), unlock_msg->luks_passphrase_raw, sizeof(unlock_msg->luks_passphrase_raw))) { + success = open_luks_device_pw(volume->volume_uuid, volume->devmapper_name, luks_passphrase, strlen(luks_passphrase)); + } else { + log_msg(LLVL_FATAL, "Failed to transcribe raw LUKS passphrase to text form."); + success = false; + } + OPENSSL_cleanse(luks_passphrase, sizeof(luks_passphrase)); + return success; } -static bool unlock_luks_volume(struct keyclient_t *keyclient, const struct msg_t *unlock_msg) { +static bool attempt_unlock_luks_volume(struct keyclient_t *keyclient, const struct msg_t *unlock_msg) { const struct host_entry_t *host = &keyclient->keydb->hosts[0]; const struct volume_entry_t* volume = keydb_get_volume_by_uuid(host, unlock_msg->volume_uuid); + char volume_uuid_str[ASCII_UUID_BUFSIZE]; + sprintf_uuid(volume_uuid_str, unlock_msg->volume_uuid); if (!volume) { - char volume_uuid_str[ASCII_UUID_BUFSIZE]; - sprintf_uuid(volume_uuid_str, unlock_msg->volume_uuid); - log_msg(LLVL_WARNING, "Keyserver provided key for unlocking volume UUID %s, but this volume does not need unlocking on the client side.", volume_uuid_str); + log_msg(LLVL_WARNING, "Keyserver provided key for unlocking volume UUID %s, but this volume is not known on the client side.", volume_uuid_str); return false; } @@ -80,7 +90,16 @@ static bool unlock_luks_volume(struct keyclient_t *keyclient, const struct msg_t if (keyclient->opts->no_luks) { keyclient->volume_unlocked[volume_index] = true; } else { - keyclient->volume_unlocked[volume_index] = do_unlock_luks_volume(volume, unlock_msg); + if (!keyclient->volume_unlocked[volume_index]) { + bool success = unlock_luks_volume(volume, unlock_msg); + keyclient->volume_unlocked[volume_index] = success; + if (!success) { + log_msg(LLVL_ERROR, "Unlocking of volume %s / %s failed with the server-provided passphrase.", volume->devmapper_name, volume_uuid_str); + return false; + } + } else { + log_msg(LLVL_WARNING, "Volume %s / %s already unlocked, not attemping to unlock again.", volume->devmapper_name, volume_uuid_str); + } } } else { log_msg(LLVL_FATAL, "Error calculating volume offset for volume %p from base %p.", volume, host->volumes); @@ -118,7 +137,7 @@ static bool contact_keyserver_socket(struct keyclient_t *keyclient, int sd) { char uuid_str[ASCII_UUID_BUFSIZE]; sprintf_uuid(uuid_str, msg.volume_uuid); log_msg(LLVL_TRACE, "Received LUKS key to unlock volume with UUID %s", uuid_str); - if (unlock_luks_volume(keyclient, &msg)) { + if (attempt_unlock_luks_volume(keyclient, &msg)) { log_msg(LLVL_DEBUG, "Successfully unlocked volume with UUID %s", uuid_str); } else { log_msg(LLVL_ERROR, "Failed to unlocked volume with UUID %s", uuid_str); @@ -187,14 +206,19 @@ static bool contact_keyserver_hostname(struct keyclient_t *keyclient, const char return success; } -static bool all_volumes_unlocked(struct keyclient_t *keyclient) { +static unsigned int locked_volume_count(struct keyclient_t *keyclient) { + unsigned int count = 0; const unsigned int volume_count = keyclient->keydb->hosts[0].volume_count; for (unsigned int i = 0; i < volume_count; i++) { if (!keyclient->volume_unlocked[i]) { - return false; + count++; } } - return true; + return count; +} + +static bool all_volumes_unlocked(struct keyclient_t *keyclient) { + return locked_volume_count(keyclient) == 0; } static bool abort_searching_for_keyserver(struct keyclient_t *keyclient) { @@ -206,7 +230,7 @@ static bool abort_searching_for_keyserver(struct keyclient_t *keyclient) { if (keyclient->opts->timeout_seconds) { double time_passed = now() - keyclient->broadcast_start_time; if (time_passed >= keyclient->opts->timeout_seconds) { - log_msg(LLVL_WARNING, "Could not unlock all volumes after %u seconds, giving up.", keyclient->opts->timeout_seconds); + log_msg(LLVL_WARNING, "Could not unlock all volumes after %u seconds, giving up. %d volumes still locked.", keyclient->opts->timeout_seconds, locked_volume_count(keyclient)); return true; } } @@ -234,9 +258,14 @@ static bool broadcast_for_keyserver(struct keyclient_t *keyclient) { }; struct udp_response_t response; if (wait_udp_response(sd, &response, &src)) { - log_msg(LLVL_DEBUG, "Potential keyserver found at %d.%d.%d.%d", PRINTF_FORMAT_IP(&src)); - if (!contact_keyserver_ipv4(keyclient, &src, keyclient->opts->port)) { - log_msg(LLVL_WARNING, "Keyserver announced at %d.%d.%d.%d, but connection to it failed.", PRINTF_FORMAT_IP(&src)); + if (!is_ip_blacklisted(src.sin_addr.s_addr)) { + log_msg(LLVL_INFO, "Keyserver found at %d.%d.%d.%d", PRINTF_FORMAT_IP(&src)); + blacklist_ip(src.sin_addr.s_addr, BLACKLIST_TIMEOUT_CLIENT); + if (!contact_keyserver_ipv4(keyclient, &src, keyclient->opts->port)) { + log_msg(LLVL_WARNING, "Keyserver announced at %d.%d.%d.%d, but connection to it failed.", PRINTF_FORMAT_IP(&src)); + } + } else { + log_msg(LLVL_DEBUG, "Potential keyserver at %d.%d.%d.%d ignored, blacklist in effect.", PRINTF_FORMAT_IP(&src)); } } @@ -281,10 +310,20 @@ bool keyclient_start(const struct pgmopts_client_t *opts) { break; } + /* Determine which of these volumes are already unlocked */ + for (unsigned int i = 0; i < host->volume_count; i++) { + keyclient.volume_unlocked[i] = is_luks_device_opened(host->volumes[i].devmapper_name); + } + if (all_volumes_unlocked(&keyclient)) { + log_msg(LLVL_INFO, "All %u volumes are unlocked already, not contacting luksrku key server.", host->volume_count); + break; + } else { + log_msg(LLVL_DEBUG, "%u of %u volumes are currently locked.", locked_volume_count(&keyclient), host->volume_count); + } + /* Transcribe the host UUID to ASCII so we only have to do this once */ sprintf_uuid((char*)keyclient.identifier, host->host_uuid); - 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); diff --git a/global.h b/global.h index 7728723..658e7b1 100644 --- a/global.h +++ b/global.h @@ -24,6 +24,10 @@ #ifndef __GLOBAL_H__ #define __GLOBAL_H__ +/* Blacklisting timeouts in seconds */ +#define BLACKLIST_TIMEOUT_CLIENT 3600 +#define BLACKLIST_TIMEOUT_SERVER 15 + /* Size in bytes of the PSK that is used for TLS */ #define PSK_SIZE_BYTES 32 diff --git a/luks.c b/luks.c index 0bb82ae..049562e 100644 --- a/luks.c +++ b/luks.c @@ -1,6 +1,6 @@ /* luksrku - Tool to remotely unlock LUKS disks using TLS. - Copyright (C) 2016-2016 Johannes Bauer + Copyright (C) 2016-2019 Johannes Bauer This file is part of luksrku. @@ -34,6 +34,7 @@ #include "log.h" #include "exec.h" #include "util.h" +#include "uuid.h" bool is_luks_device_opened(const char *mapping_name) { const char *command[] = { @@ -42,7 +43,7 @@ bool is_luks_device_opened(const char *mapping_name) { mapping_name, NULL, }; - struct runresult_t runresult = exec_command(command, should_log(LLVL_DEBUG)); + struct runresult_t runresult = exec_command(command, should_log(LLVL_TRACE)); return runresult.success && (runresult.returncode == 0); } @@ -86,7 +87,7 @@ static bool wipe_passphrase_file(const char *filename, int length) { return true; } -static const char *write_passphrase_file(const uint8_t *passphrase, int passphrase_length) { +static const char *write_passphrase_file(const void *passphrase, int passphrase_length) { //const char *filename = "/dev/shm/luksrku_passphrase.bin"; /* TODO make this variable */ const char *filename = "/tmp/luksrku_passphrase.bin"; int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0600); @@ -106,7 +107,7 @@ static const char *write_passphrase_file(const uint8_t *passphrase, int passphra return filename; } -bool open_luks_device_pw(const uint8_t *encrypted_device_uuid, const char *mapping_name, const uint8_t *passphrase, int passphrase_length) { +bool open_luks_device_pw(const uint8_t *encrypted_device_uuid, const char *mapping_name, const char *passphrase, unsigned int passphrase_length) { const char *pw_filename = write_passphrase_file(passphrase, passphrase_length); if (!pw_filename) { return false; diff --git a/luks.h b/luks.h index 3b647aa..749ae0d 100644 --- a/luks.h +++ b/luks.h @@ -30,7 +30,7 @@ /*************** AUTO GENERATED SECTION FOLLOWS ***************/ bool is_luks_device_opened(const char *mapping_name); bool open_luks_device(const uint8_t *encrypted_device_uuid, const char *mapping_name, const char *passphrase_file); -bool open_luks_device_pw(const uint8_t *encrypted_device_uuid, const char *mapping_name, const uint8_t *passphrase, int passphrase_length); +bool open_luks_device_pw(const uint8_t *encrypted_device_uuid, const char *mapping_name, const char *passphrase, unsigned int passphrase_length); /*************** AUTO GENERATED SECTION ENDS ***************/ #endif diff --git a/server.c b/server.c index 79b4dff..e1ba35a 100644 --- a/server.c +++ b/server.c @@ -187,7 +187,7 @@ static void udp_handler_thread(void *vctx) { if (is_ip_blacklisted(ipv4)) { continue; } - blacklist_ip(ipv4); + blacklist_ip(ipv4, BLACKLIST_TIMEOUT_SERVER); /* Check if we have this host in our database */ if (keydb_get_host_by_uuid(client->keydb, rx_msg.host_uuid)) { diff --git a/testdata/local_client.bin b/testdata/local_client.bin new file mode 100644 index 0000000..20dbbb3 Binary files /dev/null and b/testdata/local_client.bin differ diff --git a/testdata/local_server.bin b/testdata/local_server.bin new file mode 100644 index 0000000..29ccf9f Binary files /dev/null and b/testdata/local_server.bin differ diff --git a/testdata/client.bin b/testdata/random_client.bin similarity index 100% rename from testdata/client.bin rename to testdata/random_client.bin diff --git a/testdata/server.bin b/testdata/random_server.bin similarity index 100% rename from testdata/server.bin rename to testdata/random_server.bin diff --git a/udp.c b/udp.c index 6bfc1b8..6af8823 100644 --- a/udp.c +++ b/udp.c @@ -75,10 +75,8 @@ int create_udp_socket(unsigned int listen_port, bool send_broadcast, unsigned in return sd; } bool wait_udp_message(int sd, void *data, unsigned int length, struct sockaddr_in *source) { - fprintf(stderr, "RECV...\n"); socklen_t socklen = sizeof(struct sockaddr_in); ssize_t rx_bytes = recvfrom(sd,data, length, 0, (struct sockaddr*)source, &socklen); - fprintf(stderr, "RECV %ld\n", rx_bytes); return rx_bytes == length; }