Unlocking LUKS volumes works

First complete technical round-trip complete, can unlock the LUKS
volumes described in the server/client databases successfully.
This commit is contained in:
Johannes Bauer 2019-10-25 12:19:01 +02:00
parent 849e3a5949
commit 3478fa4555
13 changed files with 76 additions and 32 deletions

View File

@ -10,6 +10,7 @@ CFLAGS += -ggdb3 -DDEBUG -fsanitize=address -fsanitize=undefined -fsanitize=leak
PYPGMOPTS := ../Python/pypgmopts/pypgmopts PYPGMOPTS := ../Python/pypgmopts/pypgmopts
LDFLAGS := `pkg-config --libs openssl` LDFLAGS := `pkg-config --libs openssl`
TEST_PREFIX := local
OBJS := \ OBJS := \
argparse_client.o \ argparse_client.o \
@ -18,9 +19,11 @@ OBJS := \
blacklist.o \ blacklist.o \
client.o \ client.o \
editor.o \ editor.o \
exec.o \
file_encryption.o \ file_encryption.o \
keydb.o \ keydb.o \
log.o \ log.o \
luks.o \
luksrku.o \ luksrku.o \
openssl.o \ openssl.o \
pgmopts.o \ pgmopts.o \
@ -45,10 +48,10 @@ clean:
rm -f $(OBJS) $(OBJS_CFG) luksrku rm -f $(OBJS) $(OBJS_CFG) luksrku
test_s: luksrku test_s: luksrku
./luksrku server -vv testdata/server.bin ./luksrku server -vv testdata/$(TEST_PREFIX)_server.bin
test_c: luksrku test_c: luksrku
./luksrku client -vv --no-luks testdata/client.bin ./luksrku client -vv --no-luks testdata/$(TEST_PREFIX)_client.bin
.c.o: .c.o:
$(CC) $(CFLAGS) -c -o $@ $< $(CC) $(CFLAGS) -c -o $@ $<

View File

@ -31,14 +31,14 @@
static struct blacklist_entry_t blacklist[BLACKLIST_ENTRY_COUNT]; static struct blacklist_entry_t blacklist[BLACKLIST_ENTRY_COUNT];
static bool blacklist_entry_expired(int index) { 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++) { for (int i = 0; i < BLACKLIST_ENTRY_COUNT; i++) {
if (blacklist_entry_expired(i)) { if (blacklist_entry_expired(i)) {
blacklist[i].ip = ip; blacklist[i].ip = ip;
blacklist[i].entered = now(); blacklist[i].timeout = now() + timeout_seconds;
return; return;
} }
} }

View File

@ -28,15 +28,14 @@
#include <stdbool.h> #include <stdbool.h>
#define BLACKLIST_ENTRY_COUNT 32 #define BLACKLIST_ENTRY_COUNT 32
#define BLACKLIST_ENTRY_TIMEOUT_SECS 15
struct blacklist_entry_t { struct blacklist_entry_t {
uint32_t ip; uint32_t ip;
double entered; double timeout;
}; };
/*************** AUTO GENERATED SECTION FOLLOWS ***************/ /*************** 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); bool is_ip_blacklisted(uint32_t ip);
/*************** AUTO GENERATED SECTION ENDS ***************/ /*************** AUTO GENERATED SECTION ENDS ***************/

View File

@ -43,6 +43,7 @@
#include "keydb.h" #include "keydb.h"
#include "uuid.h" #include "uuid.h"
#include "udp.h" #include "udp.h"
#include "luks.h"
struct keyclient_t { struct keyclient_t {
const struct pgmopts_client_t *opts; 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); 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) { static bool unlock_luks_volume(const struct volume_entry_t *volume, const struct msg_t *unlock_msg) {
return true; 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 host_entry_t *host = &keyclient->keydb->hosts[0];
const struct volume_entry_t* volume = keydb_get_volume_by_uuid(host, unlock_msg->volume_uuid); 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) { if (!volume) {
char volume_uuid_str[ASCII_UUID_BUFSIZE]; 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);
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);
return false; return false;
} }
@ -80,7 +90,16 @@ static bool unlock_luks_volume(struct keyclient_t *keyclient, const struct msg_t
if (keyclient->opts->no_luks) { if (keyclient->opts->no_luks) {
keyclient->volume_unlocked[volume_index] = true; keyclient->volume_unlocked[volume_index] = true;
} else { } 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 { } else {
log_msg(LLVL_FATAL, "Error calculating volume offset for volume %p from base %p.", volume, host->volumes); 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]; char uuid_str[ASCII_UUID_BUFSIZE];
sprintf_uuid(uuid_str, msg.volume_uuid); sprintf_uuid(uuid_str, msg.volume_uuid);
log_msg(LLVL_TRACE, "Received LUKS key to unlock volume with UUID %s", uuid_str); 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); log_msg(LLVL_DEBUG, "Successfully unlocked volume with UUID %s", uuid_str);
} else { } else {
log_msg(LLVL_ERROR, "Failed to unlocked volume with UUID %s", uuid_str); 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; 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; const unsigned int volume_count = keyclient->keydb->hosts[0].volume_count;
for (unsigned int i = 0; i < volume_count; i++) { for (unsigned int i = 0; i < volume_count; i++) {
if (!keyclient->volume_unlocked[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) { 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) { if (keyclient->opts->timeout_seconds) {
double time_passed = now() - keyclient->broadcast_start_time; double time_passed = now() - keyclient->broadcast_start_time;
if (time_passed >= keyclient->opts->timeout_seconds) { 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; return true;
} }
} }
@ -234,9 +258,14 @@ static bool broadcast_for_keyserver(struct keyclient_t *keyclient) {
}; };
struct udp_response_t response; struct udp_response_t response;
if (wait_udp_response(sd, &response, &src)) { if (wait_udp_response(sd, &response, &src)) {
log_msg(LLVL_DEBUG, "Potential keyserver found at %d.%d.%d.%d", PRINTF_FORMAT_IP(&src)); if (!is_ip_blacklisted(src.sin_addr.s_addr)) {
if (!contact_keyserver_ipv4(keyclient, &src, keyclient->opts->port)) { log_msg(LLVL_INFO, "Keyserver found at %d.%d.%d.%d", PRINTF_FORMAT_IP(&src));
log_msg(LLVL_WARNING, "Keyserver announced at %d.%d.%d.%d, but connection to it failed.", 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; 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 */ /* Transcribe the host UUID to ASCII so we only have to do this once */
sprintf_uuid((char*)keyclient.identifier, host->host_uuid); 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 (opts->hostname) {
if (!contact_keyserver_hostname(&keyclient, opts->hostname)) { if (!contact_keyserver_hostname(&keyclient, opts->hostname)) {
log_msg(LLVL_ERROR, "Failed to contact key server: %s", opts->hostname); log_msg(LLVL_ERROR, "Failed to contact key server: %s", opts->hostname);

View File

@ -24,6 +24,10 @@
#ifndef __GLOBAL_H__ #ifndef __GLOBAL_H__
#define __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 */ /* Size in bytes of the PSK that is used for TLS */
#define PSK_SIZE_BYTES 32 #define PSK_SIZE_BYTES 32

9
luks.c
View File

@ -1,6 +1,6 @@
/* /*
luksrku - Tool to remotely unlock LUKS disks using TLS. 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. This file is part of luksrku.
@ -34,6 +34,7 @@
#include "log.h" #include "log.h"
#include "exec.h" #include "exec.h"
#include "util.h" #include "util.h"
#include "uuid.h"
bool is_luks_device_opened(const char *mapping_name) { bool is_luks_device_opened(const char *mapping_name) {
const char *command[] = { const char *command[] = {
@ -42,7 +43,7 @@ bool is_luks_device_opened(const char *mapping_name) {
mapping_name, mapping_name,
NULL, 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); return runresult.success && (runresult.returncode == 0);
} }
@ -86,7 +87,7 @@ static bool wipe_passphrase_file(const char *filename, int length) {
return true; 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 = "/dev/shm/luksrku_passphrase.bin"; /* TODO make this variable */
const char *filename = "/tmp/luksrku_passphrase.bin"; const char *filename = "/tmp/luksrku_passphrase.bin";
int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0600); 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; 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); const char *pw_filename = write_passphrase_file(passphrase, passphrase_length);
if (!pw_filename) { if (!pw_filename) {
return false; return false;

2
luks.h
View File

@ -30,7 +30,7 @@
/*************** AUTO GENERATED SECTION FOLLOWS ***************/ /*************** AUTO GENERATED SECTION FOLLOWS ***************/
bool is_luks_device_opened(const char *mapping_name); 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(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 ***************/ /*************** AUTO GENERATED SECTION ENDS ***************/
#endif #endif

View File

@ -187,7 +187,7 @@ static void udp_handler_thread(void *vctx) {
if (is_ip_blacklisted(ipv4)) { if (is_ip_blacklisted(ipv4)) {
continue; continue;
} }
blacklist_ip(ipv4); blacklist_ip(ipv4, BLACKLIST_TIMEOUT_SERVER);
/* Check if we have this host in our database */ /* Check if we have this host in our database */
if (keydb_get_host_by_uuid(client->keydb, rx_msg.host_uuid)) { if (keydb_get_host_by_uuid(client->keydb, rx_msg.host_uuid)) {

BIN
testdata/local_client.bin vendored Normal file

Binary file not shown.

BIN
testdata/local_server.bin vendored Normal file

Binary file not shown.

2
udp.c
View File

@ -75,10 +75,8 @@ int create_udp_socket(unsigned int listen_port, bool send_broadcast, unsigned in
return sd; return sd;
} }
bool wait_udp_message(int sd, void *data, unsigned int length, struct sockaddr_in *source) { 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); socklen_t socklen = sizeof(struct sockaddr_in);
ssize_t rx_bytes = recvfrom(sd,data, length, 0, (struct sockaddr*)source, &socklen); ssize_t rx_bytes = recvfrom(sd,data, length, 0, (struct sockaddr*)source, &socklen);
fprintf(stderr, "RECV %ld\n", rx_bytes);
return rx_bytes == length; return rx_bytes == length;
} }