Implemented more useful commands

Implemented add/delete operations of hosts and volumes and rekeying of
both as well.
This commit is contained in:
Johannes Bauer 2019-10-21 21:30:29 +02:00
parent 0cb0e5d470
commit 722476e7fd
7 changed files with 215 additions and 63 deletions

135
editor.c
View File

@ -26,6 +26,7 @@
#include <string.h>
#include <strings.h>
#include <stdbool.h>
#include <openssl/crypto.h>
#include "editor.h"
#include "util.h"
#include "keydb.h"
@ -58,7 +59,10 @@ static enum cmd_returncode_t cmd_help(struct editor_context_t *ctx, const char *
static enum cmd_returncode_t cmd_new(struct editor_context_t *ctx, const char *cmdname, unsigned int param_cnt, char **params);
static enum cmd_returncode_t cmd_list(struct editor_context_t *ctx, const char *cmdname, unsigned int param_cnt, char **params);
static enum cmd_returncode_t cmd_add_host(struct editor_context_t *ctx, const char *cmdname, unsigned int param_cnt, char **params);
static enum cmd_returncode_t cmd_del_host(struct editor_context_t *ctx, const char *cmdname, unsigned int param_cnt, char **params);
static enum cmd_returncode_t cmd_rekey_host(struct editor_context_t *ctx, const char *cmdname, unsigned int param_cnt, char **params);
static enum cmd_returncode_t cmd_add_volume(struct editor_context_t *ctx, const char *cmdname, unsigned int param_cnt, char **params);
static enum cmd_returncode_t cmd_del_volume(struct editor_context_t *ctx, const char *cmdname, unsigned int param_cnt, char **params);
static enum cmd_returncode_t cmd_rekey_volume(struct editor_context_t *ctx, const char *cmdname, unsigned int param_cnt, char **params);
static enum cmd_returncode_t cmd_showkey_volume(struct editor_context_t *ctx, const char *cmdname, unsigned int param_cnt, char **params);
static enum cmd_returncode_t cmd_open(struct editor_context_t *ctx, const char *cmdname, unsigned int param_cnt, char **params);
@ -90,24 +94,40 @@ static const struct editor_command_t commands[] = {
},
{
.cmdnames = { "del_host" },
.callback = cmd_add_host,
.callback = cmd_del_host,
.param_names = "[hostname]",
.min_params = 1,
.max_params = 1,
.description = "Removes a host from the database file",
},
{
.cmdnames = { "rekey_host" },
.callback = cmd_rekey_host,
.param_names = "[hostname]",
.min_params = 1,
.max_params = 1,
.description = "Re-keys the TLS PSK of a given host",
},
{
.cmdnames = { "add_volume" },
.callback = cmd_add_volume,
.param_names = "[hostname] [volumename] [volume-UUID]",
.param_names = "[hostname] [devmappername] [volume-UUID]",
.min_params = 3,
.max_params = 3,
.description = "Add a new volume to the hostname",
},
{
.cmdnames = { "del_volume" },
.callback = cmd_del_volume,
.param_names = "[hostname] [devmappername]",
.min_params = 2,
.max_params = 2,
.description = "Removes a volume from the given host",
},
{
.cmdnames = { "rekey_volume" },
.callback = cmd_rekey_volume,
.param_names = "[hostname] [volumename]",
.param_names = "[hostname] [devmappername]",
.min_params = 2,
.max_params = 2,
.description = "Re-keys the LUKS passphrase of a volume of a given hostname",
@ -115,7 +135,7 @@ static const struct editor_command_t commands[] = {
{
.cmdnames = { "showkey_volume" },
.callback = cmd_showkey_volume,
.param_names = "[hostname] [volumename]",
.param_names = "[hostname] [devmappername]",
.min_params = 2,
.max_params = 2,
.description = "Shows the LUKS passphrase of a volume of a hostname",
@ -195,24 +215,78 @@ static enum cmd_returncode_t cmd_add_host(struct editor_context_t *ctx, const ch
return COMMAND_FAILURE;
}
}
bool success = keydb_add_host(&ctx->keydb, params[0]);
const char *host_name = params[0];
bool success = keydb_add_host(&ctx->keydb, host_name);
return success ? COMMAND_SUCCESS : COMMAND_FAILURE;
}
static enum cmd_returncode_t cmd_add_volume(struct editor_context_t *ctx, const char *cmdname, unsigned int param_cnt, char **params) {
static enum cmd_returncode_t cmd_del_host(struct editor_context_t *ctx, const char *cmdname, unsigned int param_cnt, char **params) {
if (!ctx->keydb) {
fprintf(stderr, "No key database loaded.\n");
return COMMAND_FAILURE;
}
const char *host_name = params[0];
bool success = keydb_del_host_by_name(&ctx->keydb, host_name);
return success ? COMMAND_SUCCESS : COMMAND_FAILURE;
}
static struct host_entry_t* cmd_gethost(struct editor_context_t *ctx, const char *host_name) {
if (!ctx->keydb) {
fprintf(stderr, "No key database loaded.\n");
return NULL;
}
struct host_entry_t *host = keydb_get_host_by_name(ctx->keydb, host_name);
if (!host) {
fprintf(stderr, "No such host: %s\n", host_name);
return NULL;
}
return host;
}
static struct volume_entry_t* cmd_getvolume(struct editor_context_t *ctx, const char *host_name, const char *devmapper_name) {
struct host_entry_t *host = cmd_gethost(ctx, host_name);
if (!host) {
return NULL;
}
struct volume_entry_t *volume = keydb_get_volume_by_name(host, devmapper_name);
if (!volume) {
fprintf(stderr, "No such volume \"%s\" for host \"%s\"\n", devmapper_name, host_name);
return NULL;
}
return volume;
}
static enum cmd_returncode_t cmd_rekey_host(struct editor_context_t *ctx, const char *cmdname, unsigned int param_cnt, char **params) {
const char *host_name = params[0];
struct host_entry_t *host = cmd_gethost(ctx, host_name);
return host && keydb_rekey_host(host) ? COMMAND_SUCCESS : COMMAND_FAILURE;
}
static enum cmd_returncode_t cmd_do_showkey_volume(struct volume_entry_t *volume) {
char luks_passphrase[PASSPHRASE_TEXT_SIZE_BYTES];
if (!keydb_get_volume_luks_passphrase(volume, luks_passphrase, sizeof(luks_passphrase))) {
OPENSSL_cleanse(luks_passphrase, sizeof(luks_passphrase));
fprintf(stderr, "Could not determine LUKS passphrase.\n");
return COMMAND_FAILURE;
}
char uuid[ASCII_UUID_BUFSIZE];
sprintf_uuid(uuid, volume->volume_uuid);
printf("LUKS passphrase of %s / %s: %s\n", volume->devmapper_name, uuid, luks_passphrase);
OPENSSL_cleanse(luks_passphrase, sizeof(luks_passphrase));
return COMMAND_SUCCESS;
}
static enum cmd_returncode_t cmd_add_volume(struct editor_context_t *ctx, const char *cmdname, unsigned int param_cnt, char **params) {
const char *host_name = params[0];
struct host_entry_t *host = cmd_gethost(ctx, host_name);
if (!host) {
return COMMAND_FAILURE;
}
const char *volume_name = params[1];
const char *devmapper_name = params[1];
const char *volume_uuid_str = params[2];
if (!is_valid_uuid(volume_uuid_str)) {
fprintf(stderr, "Not a valid UUID: %s\n", volume_uuid_str);
@ -220,29 +294,52 @@ static enum cmd_returncode_t cmd_add_volume(struct editor_context_t *ctx, const
}
uint8_t volume_uuid[16];
parse_uuid(volume_uuid, volume_uuid_str);
return keydb_add_volume(host, volume_name, volume_uuid) ? COMMAND_SUCCESS : COMMAND_FAILURE;
if (keydb_add_volume(host, devmapper_name, volume_uuid)) {
struct volume_entry_t *volume = cmd_getvolume(ctx, host_name, devmapper_name);
return cmd_do_showkey_volume(volume);
} else {
return COMMAND_FAILURE;
}
}
static enum cmd_returncode_t cmd_del_volume(struct editor_context_t *ctx, const char *cmdname, unsigned int param_cnt, char **params) {
const char *host_name = params[0];
const char *devmapper_name = params[1];
struct host_entry_t *host = cmd_gethost(ctx, host_name);
if (!host) {
return COMMAND_FAILURE;
}
if (!keydb_del_volume(host, devmapper_name)) {
return COMMAND_FAILURE;
}
return COMMAND_SUCCESS;
}
static enum cmd_returncode_t cmd_rekey_volume(struct editor_context_t *ctx, const char *cmdname, unsigned int param_cnt, char **params) {
return COMMAND_SUCCESS;
const char *host_name = params[0];
const char *devmapper_name = params[1];
struct volume_entry_t *volume = cmd_getvolume(ctx, host_name, devmapper_name);
if (!volume) {
return COMMAND_FAILURE;
}
if (!keydb_rekey_volume(volume)) {
return COMMAND_FAILURE;
}
return cmd_do_showkey_volume(volume);
}
static enum cmd_returncode_t cmd_showkey_volume(struct editor_context_t *ctx, const char *cmdname, unsigned int param_cnt, char **params) {
const char *host_name = params[0];
struct host_entry_t *host = keydb_get_host_by_name(ctx->keydb, host_name);
if (!host) {
fprintf(stderr, "No such host: %s\n", host_name);
return COMMAND_FAILURE;
}
const char *devmapper_name = params[1];
const char *volume_name = params[1];
struct volume_entry_t *volume = keydb_get_volume_by_name(host, volume_name);
struct volume_entry_t *volume = cmd_getvolume(ctx, host_name, devmapper_name);
if (!volume) {
fprintf(stderr, "No such volume: %s\n", volume_name);
return COMMAND_FAILURE;
}
return COMMAND_SUCCESS;
return cmd_do_showkey_volume(volume);
}
static enum cmd_returncode_t cmd_open(struct editor_context_t *ctx, const char *cmdname, unsigned int param_cnt, char **params) {
@ -355,7 +452,7 @@ void editor_start(void) {
}
#ifndef __TEST_EDITOR__
// gcc -D_POSIX_SOURCE -Wall -std=c11 -Wmissing-prototypes -Wstrict-prototypes -Werror=implicit-function-declaration -Wimplicit-fallthrough -Wshadow -pie -fPIE -fsanitize=address -fsanitize=undefined -fsanitize=leak -o editor editor.c util.c log.c keydb.c file_encryption.c -lasan -lubsan -lcrypto && ./editor
// gcc -O3 -ggdb3 -D_POSIX_SOURCE -Wall -std=c11 -Wmissing-prototypes -Wstrict-prototypes -Werror=implicit-function-declaration -Wimplicit-fallthrough -Wshadow -pie -fPIE -fsanitize=address -fsanitize=undefined -fsanitize=leak -o editor editor.c util.c log.c keydb.c file_encryption.c uuid.c -lcrypto && ./editor
int main(int argc, char **argv) {

View File

@ -39,6 +39,9 @@
/* How long a passphrase is (this is raw binary, not text) */
#define PASSPHRASE_SIZE_BYTES 32
/* How long a passphrase is in it's encoded form, storing it as a character array */
#define PASSPHRASE_TEXT_SIZE_BYTES ((((PASSPHRASE_SIZE_BYTES + 2) / 3) * 4) + 1)
/* How long in characters a cryptsetup device name mapping may be */
#define MAX_DEVMAPPER_NAME_LENGTH 63

90
keydb.c
View File

@ -53,6 +53,36 @@ void keydb_free(struct keydb_t *keydb) {
free(keydb);
}
static int keydb_get_volume_index_by_name(struct host_entry_t *host, const char *devmapper_name) {
for (unsigned int i = 0; i < host->volume_count; i++) {
struct volume_entry_t *volume = &host->volumes[i];
if (!strcasecmp(volume->devmapper_name, devmapper_name)) {
return i;
}
}
return -1;
}
static int keydb_get_host_index_by_name(struct keydb_t *keydb, const char *host_name) {
for (unsigned int i = 0; i < keydb->host_count; i++) {
struct host_entry_t *host = &keydb->hosts[i];
if (!strcasecmp(host->host_name, host_name)) {
return i;
}
}
return -1;
}
struct volume_entry_t *keydb_get_volume_by_name(struct host_entry_t *host, const char *devmapper_name) {
const int index = keydb_get_volume_index_by_name(host, devmapper_name);
return (index >= 0) ? &host->volumes[index] : NULL;
}
struct host_entry_t *keydb_get_host_by_name(struct keydb_t *keydb, const char *host_name) {
const int index = keydb_get_host_index_by_name(keydb, host_name);
return (index >= 0) ? &keydb->hosts[index] : NULL;
}
bool keydb_add_host(struct keydb_t **keydb, const char *host_name) {
struct keydb_t *old_keydb = *keydb;
if (keydb_get_host_by_name(old_keydb, host_name)) {
@ -73,7 +103,7 @@ bool keydb_add_host(struct keydb_t **keydb, const char *host_name) {
return false;
}
strncpy(host->host_name, host_name, sizeof(host->host_name) - 1);
if (!buffer_randomize(host->tls_psk, sizeof(host->tls_psk))) {
if (!keydb_rekey_host(host)) {
/* We keep the reallocation but do not increase the host count */
return false;
}
@ -82,6 +112,24 @@ bool keydb_add_host(struct keydb_t **keydb, const char *host_name) {
return true;
}
bool keydb_del_host_by_name(struct keydb_t **keydb, const char *host_name) {
struct keydb_t *old_keydb = *keydb;
int host_index = keydb_get_host_index_by_name(old_keydb, host_name);
if (host_index == -1) {
log_msg(LLVL_ERROR, "No such host: \"%s\"", host_name);
return false;
}
/* We keep the memory for now and do not realloc */
array_remove(old_keydb->hosts, sizeof(struct host_entry_t), old_keydb->host_count, host_index);
old_keydb->host_count--;
return true;
}
bool keydb_rekey_host(struct host_entry_t *host) {
return buffer_randomize(host->tls_psk, sizeof(host->tls_psk));
}
bool keydb_add_volume(struct host_entry_t *host, const char *devmapper_name, const uint8_t volume_uuid[static 16]) {
if (host->volume_count == MAX_VOLUMES_PER_HOST) {
log_msg(LLVL_ERROR, "Host \"%s\" already has maximum number of volumes (%d).", host->host_name, MAX_VOLUMES_PER_HOST);
@ -103,38 +151,26 @@ bool keydb_add_volume(struct host_entry_t *host, const char *devmapper_name, con
return true;
}
int keydb_get_volume_index_by_name(struct host_entry_t *host, const char *devmapper_name) {
for (unsigned int i = 0; i < host->volume_count; i++) {
struct volume_entry_t *volume = &host->volumes[i];
if (!strcasecmp(volume->devmapper_name, devmapper_name)) {
return i;
bool keydb_del_volume(struct host_entry_t *host, const char *devmapper_name) {
int index = keydb_get_volume_index_by_name(host, devmapper_name);
if (index == -1) {
log_msg(LLVL_ERROR, "No such volume \"%s\" for host \"%s\".", devmapper_name, host->host_name);
return false;
}
if (!array_remove(host->volumes, sizeof(struct volume_entry_t), host->volume_count, index)) {
log_msg(LLVL_ERROR, "Failed to remove \"%s\" of host \"%s\".", devmapper_name, host->host_name);
return false;
}
return -1;
host->volume_count--;
return true;
}
struct volume_entry_t *keydb_get_volume_by_name(struct host_entry_t *host, const char *devmapper_name) {
const int index = keydb_get_volume_index_by_name(host, devmapper_name);
return (index >= 0) ? &host->volumes[index] : NULL;
bool keydb_rekey_volume(struct volume_entry_t *volume) {
return buffer_randomize(volume->luks_passphrase, sizeof(volume->luks_passphrase));
}
int keydb_get_host_index_by_name(struct keydb_t *keydb, const char *host_name) {
for (unsigned int i = 0; i < keydb->host_count; i++) {
struct host_entry_t *host = &keydb->hosts[i];
if (!strcasecmp(host->host_name, host_name)) {
return i;
}
}
return -1;
}
bool keydb_get_volume_luks_passphrase(const struct volume_entry_t *volume, char *dest) {
return ascii_encode(dest, volume->luks_passphrase, sizeof(volume->luks_passphrase));
}
struct host_entry_t *keydb_get_host_by_name(struct keydb_t *keydb, const char *host_name) {
const int index = keydb_get_host_index_by_name(keydb, host_name);
return (index >= 0) ? &keydb->hosts[index] : NULL;
bool keydb_get_volume_luks_passphrase(const struct volume_entry_t *volume, char *dest, unsigned int dest_buffer_size) {
return ascii_encode(dest, dest_buffer_size, volume->luks_passphrase, sizeof(volume->luks_passphrase));
}
bool keydb_write(const struct keydb_t *keydb, const char *filename, const char *passphrase) {

12
keydb.h
View File

@ -56,13 +56,15 @@ struct keydb_t {
/*************** AUTO GENERATED SECTION FOLLOWS ***************/
struct keydb_t* keydb_new(void);
void keydb_free(struct keydb_t *keydb);
bool keydb_add_host(struct keydb_t **keydb, const char *host_name);
bool keydb_add_volume(struct host_entry_t *host, const char *devmapper_name, const uint8_t volume_uuid[static 16]);
int keydb_get_volume_index_by_name(struct host_entry_t *host, const char *devmapper_name);
struct volume_entry_t *keydb_get_volume_by_name(struct host_entry_t *host, const char *devmapper_name);
int keydb_get_host_index_by_name(struct keydb_t *keydb, const char *host_name);
bool keydb_get_volume_luks_passphrase(const struct volume_entry_t *volume, char *dest);
struct host_entry_t *keydb_get_host_by_name(struct keydb_t *keydb, const char *host_name);
bool keydb_add_host(struct keydb_t **keydb, const char *host_name);
bool keydb_del_host_by_name(struct keydb_t **keydb, const char *host_name);
bool keydb_rekey_host(struct host_entry_t *host);
bool keydb_add_volume(struct host_entry_t *host, const char *devmapper_name, const uint8_t volume_uuid[static 16]);
bool keydb_del_volume(struct host_entry_t *host, const char *devmapper_name);
bool keydb_rekey_volume(struct volume_entry_t *volume);
bool keydb_get_volume_luks_passphrase(const struct volume_entry_t *volume, char *dest, unsigned int dest_buffer_size);
bool keydb_write(const struct keydb_t *keydb, const char *filename, const char *passphrase);
struct keydb_t* keydb_read(const char *filename);
/*************** AUTO GENERATED SECTION ENDS ***************/

33
util.c
View File

@ -142,32 +142,43 @@ bool buffer_randomize(uint8_t *buffer, unsigned int length) {
return true;
}
bool array_remove(void *base, unsigned int element_length, unsigned int element_count, unsigned int remove_element_index) {
bool array_remove(void *base, unsigned int element_size, unsigned int element_count, unsigned int remove_element_index) {
if (remove_element_index >= element_count) {
return false;
}
uint8_t *bytebase = (uint8_t*)base;
const unsigned int destination_offset = remove_element_index * element_length;
const unsigned int source_offset = (remove_element_index + 1) * element_length;
const unsigned int copy_length = ((element_count - 1) - remove_element_index) * element_length;
const unsigned int destination_offset = remove_element_index * element_size;
const unsigned int source_offset = (remove_element_index + 1) * element_size;
const unsigned int copy_length = ((element_count - 1) - remove_element_index) * element_size;
if (copy_length) {
memcpy(bytebase + destination_offset, bytebase + source_offset, copy_length);
}
/* Then, wipe the last element */
const unsigned int last_element_offset = element_size * (element_count - 1);
memset(base + last_element_offset, 0, element_size);
return true;
}
bool ascii_encode(char *dest, unsigned int dest_buffer_size, const uint8_t *source_data, unsigned int source_data_length) {
static uint8_t get_array_value(const uint8_t *array, unsigned int array_length, unsigned int array_index) {
if (array_index < array_length) {
return array[array_index];
} else {
return 0;
}
}
const char *alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+";
if ((source_data_length % 4) != 0) {
log_libc(LLVL_FATAL, "Can only encode binary data of which length is divisible by four, %d is not.", source_data_length);
bool ascii_encode(char *dest, unsigned int dest_buffer_size, const uint8_t *source_data, unsigned int source_data_length) {
const unsigned int require_dest_size = ((source_data_length + 2) / 3) * 4 + 1;
if (dest_buffer_size < require_dest_size) {
log_msg(LLVL_FATAL, "Encoding of %d bytes takes a %d byte buffer, but only %d bytes provided.", source_data_length, require_dest_size, dest_buffer_size);
return false;
}
const unsigned int require_dest_size = source_data_length / 4
const char *alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for (unsigned int i = 0; i < source_data_length; i += 3) {
uint32_t word = (source_data[i + 0] << 0) | (source_data[i + 1] << 8) | (source_data[i + 2] << 16);
for (unsigned int shift = 0; shift < 24; shift += 6) {
uint32_t word = ((get_array_value(source_data, source_data_length, i + 0) << 16) | (get_array_value(source_data, source_data_length, i + 1) << 8) | (get_array_value(source_data, source_data_length, i + 2) << 0));
for (int shift = 18; shift >= 0; shift -= 6) {
*dest++ = alphabet[(word >> shift) & 0x3f];
}
}

2
util.h
View File

@ -39,7 +39,7 @@ int parse_hexstr(const char *hexstr, uint8_t *data, int maxlen);
bool truncate_crlf(char *string);
bool buffer_randomize(uint8_t *buffer, unsigned int length);
bool array_remove(void *base, unsigned int element_length, unsigned int element_count, unsigned int remove_element_index);
bool ascii_encode(char *dest, const uint8_t *source_data, unsigned int source_data_length);
bool ascii_encode(char *dest, unsigned int dest_buffer_size, const uint8_t *source_data, unsigned int source_data_length);
/*************** AUTO GENERATED SECTION ENDS ***************/
#endif

3
uuid.h
View File

@ -27,6 +27,9 @@
#include <stdint.h>
#include <stdbool.h>
/* Already includes zero termination */
#define ASCII_UUID_BUFSIZE 37
/*************** AUTO GENERATED SECTION FOLLOWS ***************/
bool is_valid_uuid(const char *ascii_uuid);
bool parse_uuid(uint8_t *uuid, const char *ascii_uuid);