diff --git a/src/api/encryption_add.c b/src/api/encryption_add.c index 554300d31de5871506d3b5d953a4c75e61c9f71b..44faf35fdd5138511659070843a46e3b0422746b 100644 --- a/src/api/encryption_add.c +++ b/src/api/encryption_add.c @@ -1,25 +1,36 @@ /** * Callback function for encrypting an unencrypted device. * It proceeds as follows: - * 1. check if the device isn't already encrypted; - * 2. mount the unencrypted device; - * 3. determine the total size of the files on the device, and guess if they - * will fit into memory; - * 4. rsync all files from the unencrypted device to memory; - * 5. unmount the unencrypted device; - * 6. initialise a new encrypted container on the same device, replacing the - unencrypted contents, and unlock it for use; - * 7. create a new filesystem inside the new container, and mount it; - * 8. rsync all files back from memory to this newly encrypted device; - * 9. unmount the new filesystem, and lock the encrypted container. + * 1. check if the device isn't already encrypted; + * 2. mount the unencrypted device; + * 3. determine the total size of the files on the device, and guess if they + * will fit into memory; + * 4. rsync all files from the unencrypted device to memory; + * 5. check if an authorized_keys file can be found on the root device; + * 6. unmount the unencrypted device; + * 7. repartition the device: + * . an info partition to hold unencrypted data for use from the initrd, + * like the dropbear ssh host keys and user's ssh public keys; + * . a data partition for the actual encrypted root device; + * 8. create a filesystem on the info partition; + * 9. copy ssh host keys and the authorized_keys file from the initrd + * to the info partition; + * 10. initialise a new encrypted container on the data partition, and unlock it + * for use; + * 11. create a new filesystem on this new encrypted data partition, + * and mount it; + * 12. rsync all files back from memory to the encrypted data partition; + * 13. unmount the new filesystem, and lock the encrypted container; + * 14. reboot. * @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 + * @param[in] user_data extra data to pass between handler and main thread * @return internal status code */ int callback_encryption_add(const struct _u_request * request, struct _u_response * response, void * user_data) { + bool * reboot = (bool *)user_data; int r; // Read the encryption password from the request body. @@ -32,7 +43,7 @@ int callback_encryption_add(const struct _u_request * request, } // Check if the device isn't already encrypted. - if (is_encrypted_device(CONTAINER_DEVICE)) + if (is_encrypted_device(ROOT_DEVICE)) { // The device is already encrypted; we don't want to encrypt it again. return send_simple_response(response, 500, "error", @@ -40,13 +51,13 @@ int callback_encryption_add(const struct _u_request * request, } // Mount the filesystem on the unencrypted device. - r = temporary_mount(CONTAINER_DEVICE, UNENCRYPTED_MOUNTPOINT, + r = temporary_mount(ROOT_DEVICE, UNENCRYPTED_MOUNTPOINT, FILESYSTEM_TYPE); if (r != 0) { - printf("mounting root disk failed: return code %d", r); + printf("mounting root device failed: return code %d\n", r); return send_simple_response(response, 500, "error", - "mounting root disk failed"); + "mounting root device failed"); } // Determine the filesystem usage of the device to be encrypted. @@ -54,11 +65,11 @@ int callback_encryption_add(const struct _u_request * request, if (size < 0) { // Something went wrong in determining the filesystem usage. - printf("determining filesystem usage failed: return code %lu", size); + printf("determining filesystem usage failed: return code %lu\n", size); return send_simple_response(response, 500, "error", "determining filesystem usage failed"); } - printf("root disk usage: %lu bytes\n", size); + printf("root device usage: %lu bytes\n", size); // Determine the available memory. unsigned long memory = available_memory(); @@ -73,16 +84,16 @@ int callback_encryption_add(const struct _u_request * request, } // Copy device contents to temporary filesystem. - printf("copying existing root disk contents to memory\n"); + printf("copying existing root device contents to memory\n"); char * command = NULL; asprintf(&command, "rsync -a %s/ %s", UNENCRYPTED_MOUNTPOINT, TMP_LOCATION); r = system(command); if(r != 0) { - printf("copying rootdisk contents into memory failed: return code %d", - r); + printf("copying root device contents into memory failed" + ": return code %d\n", r); return send_simple_response(response, 500, "error", - "copying rootdisk contents into memory failed"); + "copying root device contents into memory failed"); } // Unmount unencrypted device. @@ -90,27 +101,144 @@ int callback_encryption_add(const struct _u_request * request, r = umount(UNENCRYPTED_MOUNTPOINT); if (r != 0) { - printf("unmounting encrypted device failed: return code %d", r); + printf("unmounting encrypted device failed: return code %d\n", r); return send_simple_response(response, 500, "error", - "unmounting unencrypted disk failed"); + "unmounting unencrypted device failed"); } - // Initialise encrypted container, overwriting existing device. - printf("creating encrypted container at %s\n", CONTAINER_DEVICE); - r = create_encrypted_device(CONTAINER_DEVICE, password); - if(r != 0) + // Check whether the authorized_keys file exists on root device. + char * authorized_keys_path = NULL; + asprintf(&authorized_keys_path, "%s%s", + TMP_LOCATION, AUTHORIZED_KEYS_PATH); + struct stat st = {0}; + if (stat(authorized_keys_path, &st) == -1) + { + printf("authorized_keys not found on root device at %s\n", + authorized_keys_path); + return send_simple_response(response, 500, "error", + "authorized_keys not found on root device"); + } + + // Re-partition device. + printf("repartitioning device %s\n", ROOT_DEVICE); + command = NULL; + asprintf(&command, "sgdisk -a 8192 -n 1:0:48M -N 2 %s", ROOT_DEVICE); + r = system(command); + if (r != 0) + { + printf("partitioning root device failed: return code %d\n", r); + return send_simple_response(response, 500, "error", + "partitioning root device failed"); + } + + // Inform kernel of partitioning changes. + printf("running partprobe\n"); + command = NULL; + asprintf(&command, "partprobe"); + r = system(command); + if (r != 0) { - printf("creating encrypted container failed: return code %d", r); + printf("partprobe: return code %d\n", r); + return send_simple_response(response, 500, "error", + "partprobe failed"); + } + // Sleep for a little while. + // This seems necessary for the newly created partitions to appear as + // devices. + printf("waiting a bit...\n"); + sleep(2); + + // Create filesystem on the info partition. + printf("creating filesystem on info partition\n"); + command = NULL; + asprintf(&command, "mkfs -t %s %s", FILESYSTEM_TYPE, + INFO_PARTITION_DEVICE); + printf("command: %s\n", command); + r = system(command); + if (r != 0) + { + printf("creating filesystem failed: return code %d\n", r); + return send_simple_response(response, 500, "error", + "creating filesystem inside encrypted container failed"); + } + + // Mount the info partition. + printf("mounting info partition\n"); + r = temporary_mount(INFO_PARTITION_DEVICE, INFO_MOUNTPOINT, + FILESYSTEM_TYPE); + if (r != 0) + { + printf("mounting encrypted device failed: return code %d\n", r); + return send_simple_response(response, 500, "error", + "mounting encrypted root device failed"); + } + + // Create some directories in the info partition. + printf("creating directories in info partition\n"); + command = NULL; + asprintf(&command, "mkdir -p %s%s %s%s", INFO_MOUNTPOINT, + AUTHORIZED_KEYS_DIR, INFO_MOUNTPOINT, SSH_HOST_KEY_DIR); + r = system(command); + if (r != 0) + { + printf("creating directories failed: return code %d\n", r); + return send_simple_response(response, 500, "error", + "creating directories in info partition failed"); + } + + // Copy authorized_keys file to the info partition. + printf("copying authorized_keys to info partition\n"); + command = NULL; + asprintf(&command, "cp %s %s%s", + AUTHORIZED_KEYS_PATH, INFO_MOUNTPOINT, AUTHORIZED_KEYS_PATH); + r = system(command); + if (r != 0) + { + printf("copying authorized_keys failed: return code %d\n", r); + return send_simple_response(response, 500, "error", + "copying authorized_keys failed"); + } + + // Copy dropbear ssh host keys from the initrd to the info partition. + printf("copying dropbear ssh host keys\n"); + command = NULL; + asprintf(&command, "cp /etc/dropbear/* %s%s/", INFO_MOUNTPOINT, + SSH_HOST_KEY_DIR); + r = system(command); + if (r != 0) + { + printf("copying dropbear ssh host keys failed: return code %d\n", r); + return send_simple_response(response, 500, "error", + "copying dropbear ssh host keys failed"); + } + + // Unmount info partition. + printf("unmounting info partition at %s\n", INFO_MOUNTPOINT); + r = umount(INFO_MOUNTPOINT); + if (r != 0) + { + printf("unmounting failed: return code %d\n", r); + return send_simple_response(response, 500, "error", + "unmounting configuration partition failed"); + } + + // Initialise encrypted container on data partition. + printf("creating encrypted container at %s\n", DATA_PARTITION_DEVICE); + r = create_encrypted_device(DATA_PARTITION_DEVICE, password); + if (r != 0) + { + printf("creating encrypted container failed: return code %d\n", r); 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) + r = encryption_unlock(DATA_PARTITION_DEVICE, MAPPED_DEVICE_NAME, + password); + if (r != 0) { - printf("unlocking encrypted container failed: return code %d", r); + printf("unlocking encrypted container failed: return code %d\n", r); return send_simple_response(response, 500, "error", "unlocking new encryption container failed"); } @@ -120,56 +248,61 @@ int callback_encryption_add(const struct _u_request * request, command = NULL; asprintf(&command, "mkfs -t %s %s", FILESYSTEM_TYPE, MAPPED_DEVICE_PATH); r = system(command); - if(r != 0) + if (r != 0) { - printf("creating filesystem failed: return code %d", r); + printf("creating filesystem failed: return code %d\n", r); 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, + r = temporary_mount(MAPPED_DEVICE_PATH, DATA_MOUNTPOINT, FILESYSTEM_TYPE); if (r != 0) { - printf("mounting encrypted disk failed: return code %d", r); + printf("mounting encrypted device failed: return code %d\n", r); return send_simple_response(response, 500, "error", - "mounting encrypted root disk failed"); + "mounting encrypted root device failed"); } // Copy device contents from temporary filesystem to encrypted container. - printf("copying root disk contents from memory\n"); + printf("copying root device contents from memory\n"); command = NULL; - asprintf(&command, "rsync -a %s/ %s", TMP_LOCATION, ENCRYPTED_MOUNTPOINT); + asprintf(&command, "rsync -a %s/ %s", TMP_LOCATION, DATA_MOUNTPOINT); r = system(command); - if(r != 0) + if (r != 0) { - printf("copying from memory to encrypted disk failed: return code %d", - r); + printf("copying from memory to encrypted device failed:" + " return code %d\n", r); return send_simple_response(response, 500, "error", - "copying rootdisk contents from memory failed"); + "copying root device contents from memory failed"); } - // Unmount encrypted device. - printf("unmounting encrypted device at %s\n", ENCRYPTED_MOUNTPOINT); - r = umount(ENCRYPTED_MOUNTPOINT); + // Unmount filesystem on encrypted data partition. + printf("unmounting encrypted device at %s\n", DATA_MOUNTPOINT); + r = umount(DATA_MOUNTPOINT); if (r != 0) { - printf("unmounting encrypted disk failed: return code %d", r); + printf("unmounting encrypted device failed: return code %d\n", r); return send_simple_response(response, 500, "error", - "unmounting encrypted disk failed"); + "unmounting encrypted device failed"); } // Lock the container. printf("locking encrypted device\n"); r = encryption_lock(MAPPED_DEVICE_NAME); - if(r != 0) + if (r != 0) { - printf("locking encrypted container failed: return code %d", r); + printf("locking encrypted container failed: return code %d\n", r); return send_simple_response(response, 500, "error", "locking container failed"); } - return send_simple_response(response, 200, "status", "ok"); + // Record that we want to reboot the machine. + *reboot = true; + + r = send_simple_response(response, 200, "status", "ok"); + stop_server(); + return r; } diff --git a/src/api/encryption_unlock.c b/src/api/encryption_unlock.c index fbc7f790b229ae07c4c13877408bed34639a3025..fd20287806e66b2579528fae256760feaf02681d 100644 --- a/src/api/encryption_unlock.c +++ b/src/api/encryption_unlock.c @@ -18,8 +18,8 @@ int callback_encryption_unlock(const struct _u_request * request, return send_simple_response(response, 400, "error", "missing password"); } - int unlock_status = encryption_unlock(CONTAINER_DEVICE, MAPPED_DEVICE_NAME, - password); + int unlock_status = encryption_unlock(DATA_PARTITION_DEVICE, + MAPPED_DEVICE_NAME, password); if (unlock_status == -1) { diff --git a/src/auxiliary.c b/src/auxiliary.c index fcbcde6bf3a2d194558401224ccd308d175660e8..6226e2a62e7e782c4c5c97c30be2d4c2d35635f6 100644 --- a/src/auxiliary.c +++ b/src/auxiliary.c @@ -131,3 +131,21 @@ int temporary_mount(char * device_path, char * mount_path, mount_flags, ""); return r; } + +/** + * Reboot the system. We cannot simply use the `reboot` command because + * we're running as init (pid 0). + */ +void reboot_initrd() +{ + pid_t pid; + pid = vfork(); + if (pid == 0) + { + // Child. + reboot(RB_AUTOBOOT); + _exit(EXIT_SUCCESS); + } + // Parent (init) waits. + while (1); +} diff --git a/src/cryptops-api.c b/src/cryptops-api.c index 48fbb92498794b8964f0e5b24355abd78145ab4a..5c348cf93b2dd418301e9a885fc0276ea2facd67 100644 --- a/src/cryptops-api.c +++ b/src/cryptops-api.c @@ -35,8 +35,9 @@ int main(int argc, char ** argv) instance.max_post_body_size = 1024; // Add api endpoints. + bool reboot = false; ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, "/encryption/add", - 0, &callback_encryption_add, NULL); + 0, &callback_encryption_add, &reboot); ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, "/encryption/unlock", 0, &callback_encryption_unlock, NULL); ulfius_add_endpoint_by_val(&instance, "GET" , PREFIX, "/ssh/keys", @@ -90,6 +91,13 @@ int main(int argc, char ** argv) ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); - + + // Check if the encryption/add said that we should reboot. + if (reboot) + { + printf("rebooting..."); + reboot_initrd(); + } + return 0; } diff --git a/src/encryption_functions.c b/src/encryption_functions.c index 7f1f842a9d1c59490166850737a86e63c4fc135c..2a47b409af7e6f9903f6b6d546ed27fcc1fc5895 100644 --- a/src/encryption_functions.c +++ b/src/encryption_functions.c @@ -10,6 +10,14 @@ static int container_initialise(struct crypt_device ** cd, const char * path, const bool debug) { + // Check if the device exists. + struct stat st = {0}; + if (stat(path, &st) == -1) + { + printf("device does not exist: %s.\n", path); + return 1; + } + // Let LUKS initialise the encrypted device. int r = crypt_init(cd, path); if (r < 0) @@ -65,9 +73,10 @@ static int encryption_unlock(const char * path, const char * device_name, int r = container_initialise(&cd, path, true); if (r < 0) { - printf("crypt_load() failed on device %s.\n", - crypt_get_device_name(cd)); crypt_free(cd); + } + if (r != 0) + { return r; } diff --git a/src/includes/common-includes.h b/src/includes/common-includes.h index 44b0c7f1680774d9a8b307143b00ca955aaa6cec..aa3f9d62dd4c3d8254f218741125c37320e9bfad 100644 --- a/src/includes/common-includes.h +++ b/src/includes/common-includes.h @@ -9,6 +9,8 @@ #include <fcntl.h> #include <netinet/in.h> #include <arpa/inet.h> +#include <unistd.h> +#include <sys/reboot.h> #define U_DISABLE_CURL #define U_DISABLE_WEBSOCKET diff --git a/src/includes/settings.h b/src/includes/settings.h index 8418c58f712a942b05633e39e1f2996d5789a4ed..8352c51513477907acf8d2148e30fb6807203cbf 100644 --- a/src/includes/settings.h +++ b/src/includes/settings.h @@ -1,13 +1,17 @@ #define PREFIX "/cryptops/v0" #define PORT 8000 #define BIND_ADDRESS "127.0.0.1" -#define CONTAINER_DEVICE "/dev/xvda1" +#define ROOT_DEVICE "/dev/xvda" +#define INFO_PARTITION_DEVICE ROOT_DEVICE "1" +#define DATA_PARTITION_DEVICE ROOT_DEVICE "2" #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 MEMORY_USAGE 0.9 #define UNENCRYPTED_MOUNTPOINT "/tmp/mnt-plain" -#define ENCRYPTED_MOUNTPOINT "/tmp/mnt-encrypted" +#define INFO_MOUNTPOINT "/tmp/mnt-info" +#define DATA_MOUNTPOINT "/tmp/mnt-data" #define TMP_LOCATION "/tmp/" MAPPED_DEVICE_NAME #define AUTHORIZED_KEYS_DIR "/root/.ssh" #define AUTHORIZED_KEYS_PATH AUTHORIZED_KEYS_DIR "/authorized_keys" +#define SSH_HOST_KEY_DIR "/dropbear" \ No newline at end of file