From 95c7e713d4241b6ca9ce797faef7d36b218adef2 Mon Sep 17 00:00:00 2001 From: Arie Peterson <arie@greenhost.nl> Date: Tue, 13 Jun 2017 14:29:54 +0200 Subject: [PATCH] Create main program with unlock endpoint --- cryptops-api.c | 242 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 cryptops-api.c diff --git a/cryptops-api.c b/cryptops-api.c new file mode 100644 index 0000000..6533971 --- /dev/null +++ b/cryptops-api.c @@ -0,0 +1,242 @@ +#include <string.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#define U_DISABLE_CURL +#define U_DISABLE_WEBSOCKET +#include <ulfius.h> +#include <jansson.h> +#include <libcryptsetup.h> + +#define PORT 8000 +#define PREFIX "/cryptops/v0" +#define CONTAINER_DEVICE "/dev/xvda1" +#define MAPPED_DEVICE_NAME "rootdisk" + +/** + * helper functions declaration + */ +char * read_file(const char * filename); + +/** + * callback functions declaration + */ +int callback_encryption_unlock (const struct _u_request * request, struct _u_response * response, void * user_data); + +int callback_default (const struct _u_request * request, struct _u_response * response, void * user_data); + +static int encryption_unlock(const char *path, const char *device_name, const char *password); + +int main (int argc, char **argv) +{ + int ret; + + // Set the framework port number + struct _u_instance instance; + + y_init_logs("cryptops-api", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting cryptops-api"); + + if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) + { + y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort"); + return(1); + } + + u_map_put(instance.default_headers, "Access-Control-Allow-Origin", "*"); + + // Maximum body size sent by the client is 1 Kb + instance.max_post_body_size = 1024; + + // Endpoint list declaration + ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, "/encryption/unlock", 0, &callback_encryption_unlock, NULL); + + // default_endpoint declaration + ulfius_set_default_endpoint(&instance, &callback_default, NULL); + + // Start the framework + if (argc == 4 && strcmp("-secure", argv[1]) == 0) + { + // If command-line options are -secure <key_file> <cert_file>, then open an https connection + char * key_pem = read_file(argv[2]), * cert_pem = read_file(argv[3]); + ret = ulfius_start_secure_framework(&instance, key_pem, cert_pem); + o_free(key_pem); + o_free(cert_pem); + } + else + { + // Open an http connection + ret = ulfius_start_framework(&instance); + } + + if (ret == U_OK) + { + y_log_message(Y_LOG_LEVEL_DEBUG, "Start %sframework on port %d", ((argc == 4 && strcmp("-secure", argv[1]) == 0)?"secure ":""), instance.port); + + // Wait for the user to press <enter> on the console to quit the application + getchar(); + } + else + { + y_log_message(Y_LOG_LEVEL_DEBUG, "Error starting framework"); + } + y_log_message(Y_LOG_LEVEL_DEBUG, "End framework"); + + y_close_logs(); + + ulfius_stop_framework(&instance); + ulfius_clean_instance(&instance); + + return 0; +} + +/** + * Callback function that uses the given password to open the luks volume. + */ +int callback_encryption_unlock(const struct _u_request * request, struct _u_response * response, void * user_data) +{ + json_t * json_input = ulfius_get_json_body_request(request, NULL); + // json_t * password = json_object_get(json_input, "password"); + const char * password = json_string_value(json_object_get(json_input, "password")); + + if (password == NULL) + { + json_t * json_body = NULL; + json_body = json_object(); + json_object_set_new(json_body, "error", json_string("missing password")); + ulfius_set_json_body_response(response, 400, json_body); + json_decref(json_body); + return U_CALLBACK_CONTINUE; + } + + int unlock_status = encryption_unlock(CONTAINER_DEVICE, MAPPED_DEVICE_NAME, password); + + if (unlock_status == -1) + { + // Experience learns that -1 is returned when the password is wrong. + json_t * json_body = NULL; + json_body = json_object(); + json_object_set_new(json_body, "error", json_string("incorrect password")); + ulfius_set_json_body_response(response, 403, json_body); + json_decref(json_body); + return U_CALLBACK_CONTINUE; + } + + if (unlock_status != 0) + { + // Something else went wrong with unlocking. + printf("encryption_unlock failed with status %d\n", unlock_status); + json_t * json_body = NULL; + json_body = json_object(); + json_object_set_new(json_body, "error", json_string("error during unlocking")); + ulfius_set_json_body_response(response, 500, json_body); + json_decref(json_body); + return U_CALLBACK_CONTINUE; + } + + // If we reach this point, apparently everything went well. + json_t * json_body = NULL; + json_body = json_object(); + json_object_set_new(json_body, "status", json_string("ok")); + ulfius_set_json_body_response(response, 200, json_body); + json_decref(json_body); + return U_CALLBACK_CONTINUE; +} + +/** + * Default callback function called if no endpoint has a match + */ +int callback_default(const struct _u_request * request, struct _u_response * response, void * user_data) +{ + ulfius_set_string_body_response(response, 404, "Unknown endpoint"); + return U_CALLBACK_CONTINUE; +} + +char * read_file(const char * filename) +{ + char * buffer = NULL; + long length; + FILE * f = fopen(filename, "rb"); + if (filename != NULL) + { + if (f) + { + fseek(f, 0, SEEK_END); + length = ftell (f); + fseek(f, 0, SEEK_SET); + buffer = o_malloc(length + 1); + if (buffer) + { + fread(buffer, 1, length, f); + } + buffer[length] = '\0'; + fclose (f); + } + return buffer; + } + else + { + return NULL; + } +} + +static int encryption_unlock(const char *path, const char *device_name, const char *password) +{ + struct crypt_device *cd; + int r; + + /* + * LUKS device activation example. + * It's sequence of sub-steps: device initialization, LUKS header load + * and the device activation itself. + */ + r = crypt_init(&cd, path); + if (r < 0) + { + printf("crypt_init() failed for %s.\n", path); + printf("status: %d.\n", r); + return r; + } + + /* + * crypt_load() is used to load the LUKS header from block device + * into crypt_device context. + */ + r = crypt_load( + cd, /* crypt context */ + CRYPT_LUKS1, /* requested type */ + NULL /* additional parameters (not used) */ + ); + + if (r < 0) + { + printf("crypt_load() failed on device %s.\n", crypt_get_device_name(cd)); + crypt_free(cd); + return r; + } + + /* + * Device activation creates device-mapper devie mapping with name device_name. + */ + r = crypt_activate_by_passphrase( + cd, /* crypt context */ + device_name, /* device name to activate */ + CRYPT_ANY_SLOT, /* which slot use (ANY - try all) */ + password, strlen(password), /* passphrase */ + CRYPT_ACTIVATE_READONLY /* flags */ + ); + if (r < 0) + { + printf("Device %s activation failed.\n", device_name); + crypt_free(cd); + return r; + } + + printf("LUKS device %s/%s is active.\n", crypt_get_dir(), device_name); + printf("\tcipher used: %s\n", crypt_get_cipher(cd)); + printf("\tcipher mode: %s\n", crypt_get_cipher_mode(cd)); + printf("\tdevice UUID: %s\n", crypt_get_uuid(cd)); + + crypt_free(cd); + return 0; +} -- GitLab