diff --git a/src/api/encryption_get.c b/src/api/encryption_get.c index cc64e6c214514d323a2befa0d0ac33e37da1ee42..6ae099df4f212e42a4d51a98f0bca1a319ec0087 100644 --- a/src/api/encryption_get.c +++ b/src/api/encryption_get.c @@ -29,7 +29,7 @@ int callback_encryption_get(const struct _u_request * request, can_encrypt_nullable = &can_encrypt; // Mount the filesystem on the unencrypted device. - int r = temporary_mount(ROOT_DEVICE, UNENCRYPTED_MOUNTPOINT, + int r = temporary_mount(ROOT_DEVICE, UNENCRYPTED_TMP_MOUNTPOINT, FILESYSTEM_TYPE); if (r != 0) { @@ -37,9 +37,9 @@ int callback_encryption_get(const struct _u_request * request, return send_simple_response(response, 500, "error", "mounting root device failed"); } - bool fits = filesystem_fits_in_memory(UNENCRYPTED_MOUNTPOINT, + bool fits = filesystem_fits_in_memory(UNENCRYPTED_TMP_MOUNTPOINT, MEMORY_USAGE); - umount(UNENCRYPTED_MOUNTPOINT); + umount(UNENCRYPTED_TMP_MOUNTPOINT); if (! fits) { can_encrypt = false; diff --git a/src/api/encryption_init_post.c b/src/api/encryption_init_post.c index 14d6742dc34a04fdc104cd80352ef30b09840bd8..aeef25d1a835ff05c8e3cda53527521707ff8073 100644 --- a/src/api/encryption_init_post.c +++ b/src/api/encryption_init_post.c @@ -53,7 +53,7 @@ int callback_encryption_init_post(const struct _u_request * request, } // Mount the filesystem on the unencrypted device. - r = temporary_mount(ROOT_DEVICE, UNENCRYPTED_MOUNTPOINT, + r = temporary_mount(ROOT_DEVICE, UNENCRYPTED_TMP_MOUNTPOINT, FILESYSTEM_TYPE); if (r != 0) { @@ -62,8 +62,8 @@ int callback_encryption_init_post(const struct _u_request * request, "mounting root device failed"); } - // Determine the filesystem usage of the device to be encrypted. - bool fits = filesystem_fits_in_memory(UNENCRYPTED_MOUNTPOINT, + // Determine if the files on the unencrypted device would fit in memory. + bool fits = filesystem_fits_in_memory(UNENCRYPTED_TMP_MOUNTPOINT, MEMORY_USAGE); if (! fits) { @@ -75,7 +75,8 @@ int callback_encryption_init_post(const struct _u_request * request, // Copy device contents to temporary filesystem. printf("copying existing root device contents to memory\n"); char * command = NULL; - asprintf(&command, "rsync -a %s/ %s", UNENCRYPTED_MOUNTPOINT, TMP_LOCATION); + asprintf(&command, "rsync -a %s/ %s", UNENCRYPTED_TMP_MOUNTPOINT, + TMP_LOCATION); r = system(command); if(r != 0) { @@ -86,8 +87,8 @@ int callback_encryption_init_post(const struct _u_request * request, } // Unmount unencrypted device. - printf("unmounting unencrypted device at %s\n", UNENCRYPTED_MOUNTPOINT); - r = umount(UNENCRYPTED_MOUNTPOINT); + printf("unmounting unencrypted device at %s\n", UNENCRYPTED_TMP_MOUNTPOINT); + r = umount(UNENCRYPTED_TMP_MOUNTPOINT); if (r != 0) { printf("unmounting encrypted device failed: return code %d\n", r); @@ -153,7 +154,7 @@ int callback_encryption_init_post(const struct _u_request * request, // Mount the info partition. printf("mounting info partition\n"); - r = temporary_mount(INFO_PARTITION_DEVICE, INFO_MOUNTPOINT, + r = temporary_mount(INFO_PARTITION_DEVICE, INFO_TMP_MOUNTPOINT, FILESYSTEM_TYPE); if (r != 0) { @@ -165,8 +166,8 @@ int callback_encryption_init_post(const struct _u_request * request, // 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); + asprintf(&command, "mkdir -p %s%s %s%s", INFO_TMP_MOUNTPOINT, + AUTHORIZED_KEYS_DIR, INFO_TMP_MOUNTPOINT, SSH_HOST_KEY_DIR); r = system(command); if (r != 0) { @@ -179,7 +180,7 @@ int callback_encryption_init_post(const struct _u_request * request, printf("copying authorized_keys to info partition\n"); command = NULL; asprintf(&command, "cp %s %s%s", - AUTHORIZED_KEYS_PATH, INFO_MOUNTPOINT, AUTHORIZED_KEYS_PATH); + AUTHORIZED_KEYS_PATH, INFO_TMP_MOUNTPOINT, AUTHORIZED_KEYS_PATH); r = system(command); if (r != 0) { @@ -191,7 +192,7 @@ int callback_encryption_init_post(const struct _u_request * request, // 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, + asprintf(&command, "cp /etc/dropbear/* %s%s/", INFO_TMP_MOUNTPOINT, SSH_HOST_KEY_DIR); r = system(command); if (r != 0) @@ -202,8 +203,8 @@ int callback_encryption_init_post(const struct _u_request * request, } // Unmount info partition. - printf("unmounting info partition at %s\n", INFO_MOUNTPOINT); - r = umount(INFO_MOUNTPOINT); + printf("unmounting info partition at %s\n", INFO_TMP_MOUNTPOINT); + r = umount(INFO_TMP_MOUNTPOINT); if (r != 0) { printf("unmounting failed: return code %d\n", r); @@ -246,7 +247,7 @@ int callback_encryption_init_post(const struct _u_request * request, // Mount the unlocked container. printf("mounting new filesystem\n"); - r = temporary_mount(MAPPED_DEVICE_PATH, DATA_MOUNTPOINT, + r = temporary_mount(MAPPED_DEVICE_PATH, DATA_TMP_MOUNTPOINT, FILESYSTEM_TYPE); if (r != 0) { @@ -258,7 +259,7 @@ int callback_encryption_init_post(const struct _u_request * request, // Copy device contents from temporary filesystem to encrypted container. printf("copying root device contents from memory\n"); command = NULL; - asprintf(&command, "rsync -a %s/ %s", TMP_LOCATION, DATA_MOUNTPOINT); + asprintf(&command, "rsync -a %s/ %s", TMP_LOCATION, DATA_TMP_MOUNTPOINT); r = system(command); if (r != 0) { @@ -269,8 +270,8 @@ int callback_encryption_init_post(const struct _u_request * request, } // Unmount filesystem on encrypted data partition. - printf("unmounting encrypted device at %s\n", DATA_MOUNTPOINT); - r = umount(DATA_MOUNTPOINT); + printf("unmounting encrypted device at %s\n", DATA_TMP_MOUNTPOINT); + r = umount(DATA_TMP_MOUNTPOINT); if (r != 0) { printf("unmounting encrypted device failed: return code %d\n", r); diff --git a/src/api/encryption_remove_post.c b/src/api/encryption_remove_post.c new file mode 100644 index 0000000000000000000000000000000000000000..452a223aee1e79d826eb76dd41cdd5202ff6df75 --- /dev/null +++ b/src/api/encryption_remove_post.c @@ -0,0 +1,194 @@ +/** + * Callback function for permanently decrypting an encrypted device. + * It proceeds as follows: + * 1. read the encryption password from the request body; + * 2. check if the device isn't already encrypted; + * 3. unlock the encrypted container; + * 4. mount the filesystem on the encrypted device; + * 5. check if there is enough available memory to contain all files currently + * stored on the device; + * 6. copy device contents to temporary filesystem; + * 7. unmount encrypted device; + * 8. lock the container; + * 9. unmount info partition; + * 10. remove all partitions from the device; + * 11. create a new filesystem on the unencrypted device; + * 12. mount the new filesystem; + * 13. copy device contents from memory to encrypted container; + * 14. unmount the new filesystem; + * 15. reboot. + * @param[in] request incoming HTTP request + * @param[out] response HTTP response to the request + * @param[in] user_data extra data to pass between handler and main thread + * @return internal status code + */ +int callback_encryption_remove_post(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. + 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 is encrypted. + if (! is_encrypted_device(DATA_PARTITION_DEVICE)) + { + // The device is not encrypted, so this command does not make sense. + return send_simple_response(response, 400, "error", + "not encrypted"); + } + + // Unlock the container. + printf("unlocking encrypted device\n"); + r = encryption_unlock(DATA_PARTITION_DEVICE, MAPPED_DEVICE_NAME, + password); + if (r == -1) + { + // The return code -1 signals that the password is wrong. + return send_simple_response(response, 403, "error", + "incorrect password"); + } + if (r != 0) + { + printf("unlocking encrypted container failed: return code %d\n", r); + return send_simple_response(response, 500, "error", + "unlocking encrypted container failed"); + } + + // Mount the filesystem on the encrypted data partition. + r = temporary_mount(MAPPED_DEVICE_PATH, DATA_TMP_MOUNTPOINT, + FILESYSTEM_TYPE); + if (r != 0) + { + printf("mounting root device failed: return code %d\n", r); + return send_simple_response(response, 500, "error", + "mounting root device failed"); + } + + // Determine the filesystem usage of the encrypted data partition. + bool fits = filesystem_fits_in_memory(DATA_TMP_MOUNTPOINT, MEMORY_USAGE); + if (! fits) + { + // 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 device contents to memory\n"); + char * command = NULL; + asprintf(&command, "rsync -a %s/ %s", DATA_TMP_MOUNTPOINT, TMP_LOCATION); + r = system(command); + if(r != 0) + { + printf("copying root device contents into memory failed" + ": return code %d\n", r); + return send_simple_response(response, 500, "error", + "copying root device contents into memory failed"); + } + + // Unmount encrypted data partition. + printf("unmounting encrypted device at %s\n", DATA_TMP_MOUNTPOINT); + r = umount(DATA_TMP_MOUNTPOINT); + if (r != 0) + { + printf("unmounting encrypted device failed: return code %d\n", r); + return send_simple_response(response, 500, "error", + "unmounting encrypted device failed"); + } + + // Lock the container. + printf("locking encrypted device\n"); + r = encryption_lock(MAPPED_DEVICE_NAME); + if (r != 0) + { + printf("locking encrypted container failed: return code %d\n", r); + return send_simple_response(response, 500, "error", + "locking encrypted container failed"); + } + + // Unmount info partition. + // We didn't mount this in cryptops-api, but it was mounted by the initrd + // scripts for use by dropbear. + printf("unmounting info partition at %s\n", INFO_MOUNTPOINT); + r = umount(INFO_MOUNTPOINT); + if (r != 0) + { + printf("unmounting info partition failed: return code %d\n", r); + return send_simple_response(response, 500, "error", + "unmounting info partition failed"); + } + + // Remove all partitions from the device. + printf("removing partitions from device %s\n", ROOT_DEVICE); + command = NULL; + asprintf(&command, "sgdisk -Z %s", ROOT_DEVICE); + r = system(command); + if (r != 0) + { + printf("removing partitions failed: return code %d\n", r); + return send_simple_response(response, 500, "error", + "removing partitions failed"); + } + + // Create filesystem on the unencrypted device. + printf("creating filesystem on unencrypted device\n"); + command = NULL; + asprintf(&command, "mkfs -t %s %s", FILESYSTEM_TYPE, ROOT_DEVICE); + r = system(command); + if (r != 0) + { + printf("creating filesystem failed: return code %d\n", r); + return send_simple_response(response, 500, "error", + "creating filesystem on unencrypted device failed"); + } + + // Mount the new filesystem. + printf("mounting new filesystem\n"); + r = temporary_mount(ROOT_DEVICE, UNENCRYPTED_TMP_MOUNTPOINT, + FILESYSTEM_TYPE); + if (r != 0) + { + printf("mounting unencrypted device failed: return code %d\n", r); + return send_simple_response(response, 500, "error", + "mounting unencrypted root device failed"); + } + + // Copy device contents from memory to the unencrypted device. + printf("copying root device contents from memory\n"); + command = NULL; + asprintf(&command, "rsync -a %s/ %s", + TMP_LOCATION, UNENCRYPTED_TMP_MOUNTPOINT); + r = system(command); + if (r != 0) + { + printf("copying from memory to unencrypted device failed:" + " return code %d\n", r); + return send_simple_response(response, 500, "error", + "copying root device contents from memory failed"); + } + + // Unmount filesystem on unencrypted device. + printf("unmounting unencrypted device at %s\n", UNENCRYPTED_TMP_MOUNTPOINT); + r = umount(UNENCRYPTED_TMP_MOUNTPOINT); + if (r != 0) + { + printf("unmounting unencrypted device failed: return code %d\n", r); + return send_simple_response(response, 500, "error", + "unmounting unencrypted device failed"); + } + + // 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/cryptops-api.c b/src/cryptops-api.c index 389f9934efe2da0b960bc0f43bced136bcba947a..114850423a3f74cb710a1a3edb661e0f6160b9d7 100644 --- a/src/cryptops-api.c +++ b/src/cryptops-api.c @@ -8,6 +8,7 @@ #include <api/default.c> #include <api/encryption_get.c> #include <api/encryption_init_post.c> +#include <api/encryption_remove_post.c> #include <api/encryption_unlock_post.c> #include <api/encryption_keys_put.c> #include <api/ssh_keys_get.c> @@ -44,6 +45,9 @@ int main(int argc, char ** argv) ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, "/encryption/init", 0, &callback_encryption_init_post, &reboot); + ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, + "/encryption/remove", + 0, &callback_encryption_remove_post, &reboot); ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, "/encryption/unlock", 0, &callback_encryption_unlock_post, NULL); @@ -97,6 +101,10 @@ int main(int argc, char ** argv) y_log_message(Y_LOG_LEVEL_DEBUG, "Error starting framework"); } y_log_message(Y_LOG_LEVEL_DEBUG, "End framework"); + + // Give request handlers that have called for the stop a chance to + // send their response to the client. + sleep(1); y_close_logs(); diff --git a/src/includes/settings.h b/src/includes/settings.h index 8352c51513477907acf8d2148e30fb6807203cbf..8035b1024dfa8b91d5fb4c76c4f878f682ad9266 100644 --- a/src/includes/settings.h +++ b/src/includes/settings.h @@ -1,17 +1,31 @@ +// Web interface settings. #define PREFIX "/cryptops/v0" #define PORT 8000 #define BIND_ADDRESS "127.0.0.1" + +// Device paths. #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 + +// Filesystem parameters. #define FILESYSTEM_TYPE "xfs" + +// Mountpoint paths. +#define INFO_MOUNTPOINT "/conf/persistent" + +// Resource usage. #define MEMORY_USAGE 0.9 -#define UNENCRYPTED_MOUNTPOINT "/tmp/mnt-plain" -#define INFO_MOUNTPOINT "/tmp/mnt-info" -#define DATA_MOUNTPOINT "/tmp/mnt-data" + +// Temporary mountpoints. +#define UNENCRYPTED_TMP_MOUNTPOINT "/tmp/mnt-plain" +#define INFO_TMP_MOUNTPOINT "/tmp/mnt-info" +#define DATA_TMP_MOUNTPOINT "/tmp/mnt-data" #define TMP_LOCATION "/tmp/" MAPPED_DEVICE_NAME + +// Ssh configuration file locations. #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 +#define SSH_HOST_KEY_DIR "/dropbear"