diff --git a/src/api/encryption_add.c b/src/api/encryption_add.c new file mode 100644 index 0000000000000000000000000000000000000000..57132fb1ff29eadda9d8f725886cc606ff1e98a5 --- /dev/null +++ b/src/api/encryption_add.c @@ -0,0 +1,145 @@ +/** + * Callback function for encrypting an unencrypted device. + * @param[in] request incoming HTTP request + * @param[out] response HTTP response to the request + * @param[in] user_data extra data to pass between main thread and callbacks + * @return internal status code + */ +int callback_encryption_add(const struct _u_request * request, + struct _u_response * response, void * user_data) +{ + int r; + + // Read the encryption password from the request body. + json_t * json_input = ulfius_get_json_body_request(request, NULL); + const char * password = password = json_string_value( + json_object_get(json_input, "password")); + if (password == NULL) + { + return send_simple_response(response, 400, "error", "missing password"); + } + + // Check if the device isn't already encrypted. + bool device_encrypted = is_encrypted_device(CONTAINER_DEVICE); + if (device_encrypted) + { + // The device is already encrypted; we don't want to encrypt it again. + return send_simple_response(response, 500, "error", + "already encrypted"); + } + + // Create temporary mountpoint. + r = temporary_mount(CONTAINER_DEVICE, UNENCRYPTED_MOUNTPOINT, + FILESYSTEM_TYPE); + if (r != 0) + { + return send_simple_response(response, 500, "error", + "mounting root disk failed"); + } + + // Determine the filesystem usage of the device to be encrypted. + unsigned long size = filesystem_usage(UNENCRYPTED_MOUNTPOINT); + printf("root disk usage: %lu bytes\n", size); + + // Determine the available memory. + unsigned long memory = available_memory(); + + double projected_usage = (double)size / (double)memory; + printf("projected memory usage: %.3f\n", projected_usage); + if (projected_usage > MEMORY_USAGE) + { + // Projected memory usage is really high, so abort. + return send_simple_response(response, 500, "error", + "device too large"); + } + + // Copy device contents to temporary filesystem. + printf("copying existing root disk contents to memory\n"); + char * command = NULL; + asprintf(&command, "rsync -a %s/ %s", UNENCRYPTED_MOUNTPOINT, TMP_LOCATION); + r = system(command); + if(r != 0) + { + return send_simple_response(response, 500, "error", + "copying rootdisk contents into memory failed"); + } + + // Unmount unencrypted device. + printf("unmounting unencrypted device at %s\n", UNENCRYPTED_MOUNTPOINT); + r = umount(UNENCRYPTED_MOUNTPOINT); + if (r != 0) + { + return send_simple_response(response, 500, "error", + "unmounting unencrypted disk failed"); + } + + // Initialise encrypted container, overwriting existing device. + printf("creating encrypted device at %s\n", CONTAINER_DEVICE); + r = create_encrypted_device(CONTAINER_DEVICE, password); + if(r != 0) + { + return send_simple_response(response, 500, "error", + "creating encryption container failed"); + } + + // Unlock the new container. + printf("unlocking encrypted device\n"); + r = encryption_unlock(CONTAINER_DEVICE, MAPPED_DEVICE_NAME, password); + if(r != 0) + { + return send_simple_response(response, 500, "error", + "unlocking new encryption container failed"); + } + + // Create filesystem in the new container. + printf("creating filesystem in new container\n"); + command = NULL; + asprintf(&command, "mkfs -t %s %s", FILESYSTEM_TYPE, MAPPED_DEVICE_PATH); + r = system(command); + if(r != 0) + { + return send_simple_response(response, 500, "error", + "creating filesystem inside encrypted container failed"); + } + + // Mount the unlocked container. + printf("mounting new filesystem\n"); + r = temporary_mount(MAPPED_DEVICE_PATH, ENCRYPTED_MOUNTPOINT, + FILESYSTEM_TYPE); + if (r != 0) + { + return send_simple_response(response, 500, "error", + "mounting encrypted root disk failed"); + } + + // Copy device contents from temporary filesystem to encrypted container. + printf("copying root disk contents from memory\n"); + command = NULL; + asprintf(&command, "rsync -a %s/ %s", TMP_LOCATION, ENCRYPTED_MOUNTPOINT); + r = system(command); + if(r != 0) + { + return send_simple_response(response, 500, "error", + "copying rootdisk contents from memory failed"); + } + + // Unmount encrypted device. + printf("unmounting encrypted device at %s\n", ENCRYPTED_MOUNTPOINT); + r = umount(ENCRYPTED_MOUNTPOINT); + if (r != 0) + { + return send_simple_response(response, 500, "error", + "unmounting encrypted disk failed"); + } + + // Lock the container. + printf("locking encrypted device\n"); + r = encryption_lock(MAPPED_DEVICE_NAME); + if(r != 0) + { + return send_simple_response(response, 500, "error", + "locking container failed"); + } + + return send_simple_response(response, 200, "status", "ok"); +} diff --git a/src/auxiliary.c b/src/auxiliary.c index b025dddd734718554a07a9c363fdd7df5106a5ae..722b3589d408adccfa47fffce012ef5a7765a702 100644 --- a/src/auxiliary.c +++ b/src/auxiliary.c @@ -61,3 +61,69 @@ int send_simple_response(struct _u_response * response, int http_status, json_decref(json_body); return U_CALLBACK_CONTINUE; } + +/** + * Determine the size of a block device. + * @param device_path Path to the block device. + * @return Size of the device in bytes. + */ +unsigned long device_size(char * device_path) +{ + int fd; + unsigned long numblocks = 0; + + fd = open(device_path, O_RDONLY); + ioctl(fd, BLKGETSIZE, &numblocks); + close(fd); + printf("Number of blocks: %lu, this makes %.3f GB\n", + numblocks, (double)numblocks * 512.0 / (1024 * 1024 * 1024)); + return (numblocks * 512); +} + +/** + * Determine available memory. + * @return Available memory in bytes. + */ +unsigned long available_memory() +{ + struct sysinfo myinfo; + unsigned long free_bytes; + sysinfo(&myinfo); + free_bytes = myinfo.mem_unit * myinfo.freeram; + printf("Free memory: %lu B, %lu MB\n", + free_bytes, free_bytes / 1024 / 1024); + return free_bytes; +} + +/** + * Determine the usage of the given filesystem. + * @param path Path of the mounted filesystem. + * @return Number of bytes used. + */ +unsigned long filesystem_usage(const char * path) +{ + struct statvfs s; + statvfs(path, &s); + return (s.f_blocks - s.f_bfree) * s.f_bsize; +} + +/** + * Mount a filesystem; create the mount target if it doesn't exist. + * @param device_path Path to the device to mount. + * @param mount_path Path to the mount target. + * @return Status code. + */ +int temporary_mount(char * device_path, char * mount_path, + char * filesystem_type) +{ + struct stat st = {0}; + if (stat(mount_path, &st) == -1) + { + mkdir(mount_path, 0700); + } + // Mount the device. + unsigned long mount_flags = MS_NOATIME | MS_NODEV | MS_NOEXEC; + int r = mount(device_path, mount_path, filesystem_type, + mount_flags, ""); + return r; +} diff --git a/src/cryptops-api.c b/src/cryptops-api.c index 3ae3b6de924276b46f58954bdc81350f604281c5..e9c560f220c3262fbfc6477181690376d1a5c441 100644 --- a/src/cryptops-api.c +++ b/src/cryptops-api.c @@ -6,6 +6,7 @@ #include <auxiliary.c> #include <encryption_functions.c> #include <api/default.c> +#include <api/encryption_add.c> #include <api/encryption_unlock.c> int main(int argc, char ** argv) @@ -26,6 +27,8 @@ int main(int argc, char ** argv) instance.max_post_body_size = 1024; // Add api endpoints. + ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, "/encryption/add", + 0, &callback_encryption_add, NULL); ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, "/encryption/unlock", 0, &callback_encryption_unlock, NULL); diff --git a/src/encryption_functions.c b/src/encryption_functions.c index 3f5502d9216c1b3356246ad71f8eea993ffd4473..313b65cbf69452d34f4d59c53d2b9db8448e1a5f 100644 --- a/src/encryption_functions.c +++ b/src/encryption_functions.c @@ -28,6 +28,24 @@ static int container_initialise(struct crypt_device **cd, const char *path) return r; } +/** + * Check if the given device is actually an encrypted container. + * @param path path to the encrypted container + */ +static bool is_encrypted_device (const char *path) +{ + // Let LUKS initialise the encrypted device. + struct crypt_device *cd; + int r = container_initialise(&cd, path); + crypt_free(cd); + if (r < 0) + { + return false; + } + + return true; +} + /** * Use cryptsetup to unlock the luks container. * This will create `/dev/mapper/$device_name`. @@ -68,3 +86,96 @@ static int encryption_unlock(const char *path, const char *device_name, crypt_free(cd); return 0; } + +/** + * Use cryptsetup to lock the luks container again. + * @param device_name name of the mapping + * @return status code + */ +static int encryption_lock(const char *device_name) +{ + struct crypt_device *cd; + int r; + + r = crypt_init_by_name(&cd, device_name); + if (r < 0) + { + printf("crypt_init_by_name() failed for %s.\n", device_name); + return r; + } + + if (crypt_status(cd, device_name) == CRYPT_ACTIVE) + { + printf("Device %s is still active.\n", device_name); + } + else + { + printf("Something failed perhaps, device %s is not active.\n", + device_name); + crypt_free(cd); + return -1; + } + + r = crypt_deactivate(cd, device_name); + if (r < 0) + { + printf("crypt_deactivate() failed.\n"); + crypt_free(cd); + return r; + } + + printf("Device %s is now deactivated.\n", device_name); + crypt_free(cd); + return 0; +} + +/** + * Create a new encrypted container on a given device. + * This overwrites the current contents of the device. + * @param path path to the device + * @param password password of the new container + * @return status code + */ +static int create_encrypted_device(const char * path, const char * password) +{ + struct crypt_device * cd; + struct crypt_params_luks1 params; + int r; + + r = crypt_init(&cd, path); + if (r < 0 ) + { + printf("crypt_init() failed for %s.\n", path); + return r; + } + + printf("Context is attached to block device %s.\n", + crypt_get_device_name(cd)); + + params.hash = "sha1"; + params.data_alignment = 0; + params.data_device = NULL; + r = crypt_format(cd, CRYPT_LUKS1, "aes", "xts-plain64", NULL, NULL, 256 / 8, + ¶ms); + + if (r < 0) + { + printf("crypt_format() failed on device %s\n", + crypt_get_device_name(cd)); + crypt_free(cd); + return r; + } + + r = crypt_keyslot_add_by_volume_key(cd, CRYPT_ANY_SLOT, NULL, 0, password, + strlen(password)); + if (r < 0) + { + printf("Adding keyslot failed.\n"); + crypt_free(cd); + return r; + } + printf("The first keyslot is initialized.\n"); + + crypt_free(cd); + return 0; +} diff --git a/src/includes/common-includes.h b/src/includes/common-includes.h index 910cd619c913769648c973216a08fe6ca0fad744..b54967d00ee393e07612081f88f765adb3291033 100644 --- a/src/includes/common-includes.h +++ b/src/includes/common-includes.h @@ -1,5 +1,11 @@ #include <string.h> +#include <stdbool.h> #include <sys/stat.h> +#include <sys/sysinfo.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/statvfs.h> +#include <linux/fs.h> #include <fcntl.h> #define U_DISABLE_CURL diff --git a/src/includes/settings.h b/src/includes/settings.h index 10122d0d82a84dda308221ff02a8db4c2ef4e71f..33fdc3c1e3e980760ff6a49e1f69455cbbaadfe1 100644 --- a/src/includes/settings.h +++ b/src/includes/settings.h @@ -2,3 +2,9 @@ #define PORT 8000 #define CONTAINER_DEVICE "/dev/xvda1" #define MAPPED_DEVICE_NAME "xvda1_crypt" +#define MAPPED_DEVICE_PATH "/dev/mapper/" MAPPED_DEVICE_NAME +#define FILESYSTEM_TYPE "xfs" +#define MEMORY_USAGE 0.8 +#define UNENCRYPTED_MOUNTPOINT "/tmp/mnt-plain" +#define ENCRYPTED_MOUNTPOINT "/tmp/mnt-encrypted" +#define TMP_LOCATION "/tmp/" MAPPED_DEVICE_NAME