diff --git a/src/api/encryption_get.c b/src/api/encryption_get.c new file mode 100644 index 0000000000000000000000000000000000000000..cc64e6c214514d323a2befa0d0ac33e37da1ee42 --- /dev/null +++ b/src/api/encryption_get.c @@ -0,0 +1,65 @@ +/** + * Callback function that shows the current encryption status (encrypted or + * unencrypted). + * + * Example output: + * {"encryption-status":"encrypted"} + * + * @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_get(const struct _u_request * request, + struct _u_response * response, void * user_data) +{ + bool encrypted; + bool * can_encrypt_nullable = NULL; + bool can_encrypt = true; + json_t * messages = json_array(); + + if (path_exists(DATA_PARTITION_DEVICE) && + is_encrypted_device(DATA_PARTITION_DEVICE)) + { + encrypted = true; + } + else + { + encrypted = false; + can_encrypt_nullable = &can_encrypt; + + // Mount the filesystem on the unencrypted device. + int r = temporary_mount(ROOT_DEVICE, UNENCRYPTED_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"); + } + bool fits = filesystem_fits_in_memory(UNENCRYPTED_MOUNTPOINT, + MEMORY_USAGE); + umount(UNENCRYPTED_MOUNTPOINT); + if (! fits) + { + can_encrypt = false; + json_array_append_new(messages, json_string( + "existing files do not fit in memory")); + } + } + + json_t * json_body = NULL; + json_body = json_object(); + + json_object_set_new(json_body, "encrypted", + encrypted ? json_true() : json_false()); + + json_object_set_new(json_body, "can-encrypt", + can_encrypt_nullable == NULL ? json_null() : + (can_encrypt ? json_true() : json_false())); + json_object_set_new(json_body, "messages", messages); + + ulfius_set_json_body_response(response, 200, json_body); + json_decref(json_body); + return U_CALLBACK_CONTINUE; +} diff --git a/src/api/encryption_init.c b/src/api/encryption_init_post.c similarity index 93% rename from src/api/encryption_init.c rename to src/api/encryption_init_post.c index 17b1a0b4211f831c0d9351ba3ac63cd4a2c83d28..c68e10fb4086cba48247410de5dab651e1a1ef79 100644 --- a/src/api/encryption_init.c +++ b/src/api/encryption_init_post.c @@ -27,7 +27,7 @@ * @param[in] user_data extra data to pass between handler and main thread * @return internal status code */ -int callback_encryption_init(const struct _u_request * request, +int callback_encryption_init_post(const struct _u_request * request, struct _u_response * response, void * user_data) { bool * reboot = (bool *)user_data; @@ -43,7 +43,9 @@ int callback_encryption_init(const struct _u_request * request, } // Check if the device isn't already encrypted. - if (is_encrypted_device(ROOT_DEVICE)) + // Actually we check if the device has any partitions; if so, we're not in + // the expected situation of an unpartitioned unencrypted root device. + if (path_exists(INFO_PARTITION_DEVICE)) { // The device is already encrypted; we don't want to encrypt it again. return send_simple_response(response, 500, "error", @@ -61,22 +63,9 @@ int callback_encryption_init(const struct _u_request * request, } // Determine the filesystem usage of the device to be encrypted. - unsigned long size = filesystem_usage(UNENCRYPTED_MOUNTPOINT); - if (size < 0) - { - // Something went wrong in determining the filesystem usage. - printf("determining filesystem usage failed: return code %lu\n", size); - return send_simple_response(response, 500, "error", - "determining filesystem usage failed"); - } - printf("root device 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) + bool fits = filesystem_fits_in_memory(UNENCRYPTED_MOUNTPOINT, + MEMORY_USAGE); + if (! fits) { // Projected memory usage is really high, so abort. return send_simple_response(response, 500, "error", @@ -88,7 +77,7 @@ int callback_encryption_init(const struct _u_request * request, char * command = NULL; asprintf(&command, "rsync -a %s/ %s", UNENCRYPTED_MOUNTPOINT, TMP_LOCATION); r = system(command); - if(r != 0) + if (r != 0) { printf("copying root device contents into memory failed" ": return code %d\n", r); diff --git a/src/api/encryption_unlock.c b/src/api/encryption_unlock_post.c similarity index 95% rename from src/api/encryption_unlock.c rename to src/api/encryption_unlock_post.c index fd20287806e66b2579528fae256760feaf02681d..83340837691ef09686d9929c74e996571247b5b9 100644 --- a/src/api/encryption_unlock.c +++ b/src/api/encryption_unlock_post.c @@ -6,7 +6,7 @@ * @param[in] user_data extra data to pass between main thread and callbacks * @return internal status code */ -int callback_encryption_unlock(const struct _u_request * request, +int callback_encryption_unlock_post(const struct _u_request * request, struct _u_response * response, void * user_data) { json_t * json_input = ulfius_get_json_body_request(request, NULL); diff --git a/src/auxiliary.c b/src/auxiliary.c index 18b5e0f96b0f9a88dce220065863af83b4c9c77d..90ab5ab253028c37bb66b2d4ca18a17594cda284 100644 --- a/src/auxiliary.c +++ b/src/auxiliary.c @@ -131,6 +131,34 @@ unsigned long filesystem_usage(const char * path) return (s.f_blocks - s.f_bfree) * s.f_bsize; } +/** + * Check if the files on the given filesystem would fit into memory. + * @param path Path of the mounted filesystem. + * @param safety_margin Fraction (between zero and one) of free memory to + deem available for containing the files. + * @return Whether the filesystem seems to fit into memory. + */ +bool filesystem_fits_in_memory(const char * path, double safety_margin) +{ + // Determine the filesystem usage of the device to be encrypted. + unsigned long size = filesystem_usage(path); + if (size < 0) + { + // Something went wrong in determining the filesystem usage. + // Return false as a precaution. + printf("Determining file system usage failed (size: %lu)\n", size); + return false; + } + printf("file system 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); + return (projected_usage <= safety_margin); +} + /** * Mount a filesystem; create the mount target if it doesn't exist. * @param device_path Path to the device to mount. @@ -283,13 +311,25 @@ int replace_ssh_key(int id, const char * ssh_key) /** * Add the SSH_COMMAND string in front of ssh_key unless it's already there * because people have seen it being used in ssh_keys_list. - * @param[in] ssh_key a valid ssh key string + * @param[out] ssh_key_with_command the ssh_key with the command prepended + * if needed + * @param[in] ssh_key a valid ssh key string */ int add_ssh_command(char ** ssh_key_with_command, const char * ssh_key) { - if(strncmp(SSH_COMMAND, ssh_key, strlen(SSH_COMMAND)) != 0) - asprintf(ssh_key_with_command, "%s %s", SSH_COMMAND, ssh_key); + if (strncmp(SSH_COMMAND, ssh_key, strlen(SSH_COMMAND)) != 0) + return asprintf(ssh_key_with_command, "%s %s", SSH_COMMAND, ssh_key); else - asprintf(ssh_key_with_command, "%s", ssh_key); - return 0; + return asprintf(ssh_key_with_command, "%s", ssh_key); +} + +/* + * Check if the given path already exists (as a (special) file or directory). + * @param path Path to test. + * @return `true` if path exists, `false` otherwise. + */ +bool path_exists(const char * path) +{ + struct stat st = {0}; + return (stat(path, &st) == 0); } diff --git a/src/cryptops-api.c b/src/cryptops-api.c index de28f6c4c48129e98d58bc8cadd8c99fe331c038..14a8a5348e7e9bd3f2fc599bf1fedbfef44076a6 100644 --- a/src/cryptops-api.c +++ b/src/cryptops-api.c @@ -6,8 +6,9 @@ #include <auxiliary.c> #include <encryption_functions.c> #include <api/default.c> -#include <api/encryption_init.c> -#include <api/encryption_unlock.c> +#include <api/encryption_get.c> +#include <api/encryption_init_post.c> +#include <api/encryption_unlock_post.c> #include <api/encryption_keys_put.c> #include <api/ssh_keys_get.c> #include <api/ssh_keys_put.c> @@ -40,12 +41,15 @@ int main(int argc, char ** argv) // Add api endpoints. bool reboot = false; + ulfius_add_endpoint_by_val(&instance, "GET" , PREFIX, + "/encryption", + 0, &callback_encryption_get, NULL); ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, "/encryption/init", - 0, &callback_encryption_init, &reboot); + 0, &callback_encryption_init_post, &reboot); ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, "/encryption/unlock", - 0, &callback_encryption_unlock, NULL); + 0, &callback_encryption_unlock_post, NULL); ulfius_add_endpoint_by_val(&instance, "PUT" , PREFIX, "/encryption/keys/:slot", 0, &callback_encryption_keys_put, NULL); diff --git a/src/encryption_functions.c b/src/encryption_functions.c index 1bc128a48732056facee36e187adabbf7ba156cb..9bbceecd4fa95ce917855f20a35eecadf50ed392 100644 --- a/src/encryption_functions.c +++ b/src/encryption_functions.c @@ -11,8 +11,7 @@ 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) + if (! path_exists(path)) { printf("device does not exist: %s.\n", path); return 1;