diff --git a/Makefile b/Makefile index f22ab4b507975406a2df32250dc3d184bd96951a..b1377c97f7c1094cb232391b7e51c6e3a11e1af0 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ TARGETS=cryptops-api CFLAGS=-O0 -g -Wall -D_GNU_SOURCE -Iincludes -LDLIBS=-L/test/libraries -lcryptsetup -lc -lulfius -lyder -lorcania -ljansson +LDLIBS=-L./libraries -lcryptsetup -lc -lulfius -lyder -lorcania -ljansson CC=gcc all: $(TARGETS) diff --git a/cryptops-api.c b/cryptops-api.c index 65339714361194a2e47cb413bf66959f3c61acd6..ad46f98e2d7c500ffe7bcf18fab5a82ed15fcf19 100644 --- a/cryptops-api.c +++ b/cryptops-api.c @@ -1,7 +1,6 @@ #include <string.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> +#include <sys/stat.h> +#include <fcntl.h> #define U_DISABLE_CURL #define U_DISABLE_WEBSOCKET @@ -9,55 +8,92 @@ #include <jansson.h> #include <libcryptsetup.h> -#define PORT 8000 +#define PORT 8537 #define PREFIX "/cryptops/v0" #define CONTAINER_DEVICE "/dev/xvda1" -#define MAPPED_DEVICE_NAME "rootdisk" +#define MAPPED_DEVICE_NAME "xvda1_crypt" + +#define FIFO_PATH "/tmp/cryptops-api-stop" /** * helper functions declaration */ char * read_file(const char * filename); +void stop_server(); +int send_simple_response +( + struct _u_response * response, + int http_status, + const char *field, + const char *value +); /** * 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 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 main(int argc, char **argv) { - int ret; - - // Set the framework port number + y_init_logs + ( + "cryptops-api", + Y_LOG_MODE_CONSOLE, + Y_LOG_LEVEL_DEBUG, + NULL, + "Starting cryptops-api" + ); + 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 + + // 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 + + // Add api endpoints. + ulfius_add_endpoint_by_val + ( + &instance, + "POST", + PREFIX, + "/encryption/unlock", + 0, + &callback_encryption_unlock, + NULL + ); + + // Add default endpoint. ulfius_set_default_endpoint(&instance, &callback_default, NULL); - - // Start the framework + + // Start the framework. + int ret; if (argc == 4 && strcmp("-secure", argv[1]) == 0) { - // If command-line options are -secure <key_file> <cert_file>, then open an https connection + // If command-line options are -secure <key_file> <cert_file>, + // then listen for https connections. 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); @@ -65,16 +101,36 @@ int main (int argc, char **argv) } else { - // Open an http connection + // Listen for http connections. 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); + 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(); + // Wait for signal from fifo to quit. + y_init_logs + ( + "cryptops-api", + Y_LOG_MODE_CONSOLE, + Y_LOG_LEVEL_DEBUG, + NULL, + "Waiting for fifo signal to quit" + ); + int fifo = 0; + char buf[4]; + char fifo_path[] = FIFO_PATH; + mkfifo(fifo_path, 0600); + fifo = open(fifo_path, O_RDONLY); + // This will block until the fifo is written to. + read(fifo, &buf, 4); } else { @@ -91,67 +147,86 @@ int main (int argc, char **argv) } /** - * Callback function that uses the given password to open the luks volume. + * Callback function that uses the password passed in the request to open the + * luks volume. */ -int callback_encryption_unlock(const struct _u_request * request, struct _u_response * response, void * user_data) +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")); + const char * password; + 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; + 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 + ( + 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; + return send_simple_response + ( + response, + 403, + "error", + "incorrect password" + ); } 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; + return send_simple_response(response, 500, "error", "error during unlocking"); } // 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; + stop_server(); + return send_simple_response(response, 200, "status", "ok"); } /** - * Default callback function called if no endpoint has a match + * Default callback function called if no other endpoint matches. */ -int callback_default(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 +) { ulfius_set_string_body_response(response, 404, "Unknown endpoint"); return U_CALLBACK_CONTINUE; } +/** + * Use a fifo to signal that we want to stop the api server. + */ +void stop_server() +{ + printf("Stopping cryptops-api server...\n"); + int fifo; + char fifo_path[] = FIFO_PATH; + fifo = open(fifo_path, O_WRONLY); + char * msg = "stop"; + write(fifo, msg, strlen(msg) + 1); +} + +/** + * Read a file completely into a string. + */ char * read_file(const char * filename) { char * buffer = NULL; @@ -180,17 +255,39 @@ char * read_file(const char * filename) } } -static int encryption_unlock(const char *path, const char *device_name, const char *password) +/** + * Respond to the request with a simple json structure '{$field: $value}'. + */ +int send_simple_response +( + struct _u_response * response, /* response struct to use */ + int http_status, /* HTTP status code to return */ + const char *field, /* name of the json field to return */ + const char *value /* json value to return */ +) +{ + json_t * json_body = NULL; + json_body = json_object(); + json_object_set_new(json_body, field, json_string(value)); + ulfius_set_json_body_response(response, http_status, json_body); + json_decref(json_body); + return U_CALLBACK_CONTINUE; +} + +/** + * Use cryptsetup to unlock the luks container. + * This will create `/dev/mapper/$device_name`. + */ +static int encryption_unlock +( + const char *path, /* path to the encrypted container */ + const char *device_name, /* name of the mapping */ + const char *password /* encryption password of the container */ +) { + // Let LUKS initialise the encrypted device. 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); + int r = crypt_init(&cd, path); if (r < 0) { printf("crypt_init() failed for %s.\n", path); @@ -198,27 +295,29 @@ static int encryption_unlock(const char *path, const char *device_name, const ch 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 */ + // Load the LUKS header from the block device into the crypt device context. + r = crypt_load + ( + cd, /* crypt device context */ + CRYPT_LUKS1, /* requested encryption type */ NULL /* additional parameters (not used) */ ); if (r < 0) { - printf("crypt_load() failed on device %s.\n", crypt_get_device_name(cd)); + 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( + // Device activation creates device-mapper devie mapping with namei + // 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) */