diff --git a/Makefile b/Makefile index b1377c97f7c1094cb232391b7e51c6e3a11e1af0..fa3cc983e6d9dfe14db13b53dee3200669a62511 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,32 @@ -TARGETS=cryptops-api -CFLAGS=-O0 -g -Wall -D_GNU_SOURCE -Iincludes -LDLIBS=-L./libraries -lcryptsetup -lc -lulfius -lyder -lorcania -ljansson +TARGET=cryptops-api + +CFLAGS=-O0 -g -Wall -D_GNU_SOURCE -Iincludes -Isrc +LFLAGS=-L./libraries -lcryptsetup -lc -lulfius -lyder -lorcania -ljansson CC=gcc +LINKER=gcc -all: $(TARGETS) +SRCDIR = src +OBJDIR = obj +BINDIR = bin -cryptops-api: cryptops-api.o - $(CC) -o $@ $^ $(LDLIBS) +SOURCES := $(SRCDIR)/$(TARGET).c +INCLUDES := $(wildcard $(SRCDIR)/*.h) +OBJECTS := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o) -clean: - rm -f *.o *~ core $(TARGETS) +$(BINDIR)/$(TARGET): $(OBJECTS) + @$(LINKER) $(OBJECTS) $(LFLAGS) -o $@ + @echo "Linking complete." + +$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c + @$(CC) $(CFLAGS) -c $< -o $@ + @echo "Compiled "$<" successfully." .PHONY: clean +clean: + @rm -f $(OBJECTS) + @echo "Cleanup complete." + +.PHONY: remove +remove: clean + @rm -f $(BINDIR)/$(TARGET) + @echo "Executable removed." diff --git a/cryptops-api.c b/cryptops-api.c deleted file mode 100644 index e25c5577af6c003b09d4e66b3b76b52c32cfab4b..0000000000000000000000000000000000000000 --- a/cryptops-api.c +++ /dev/null @@ -1,336 +0,0 @@ -#include <string.h> -#include <sys/stat.h> -#include <fcntl.h> - -#define U_DISABLE_CURL -#define U_DISABLE_WEBSOCKET -#include <ulfius.h> -#include <jansson.h> -#include <libcryptsetup.h> - -#define PORT 8537 -#define PREFIX "/cryptops/v0" -#define CONTAINER_DEVICE "/dev/xvda1" -#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 -); -static int container_initialise -( - struct crypt_device **cd, - const char *path -); - -int main(int argc, char **argv) -{ - y_init_logs("cryptops-api", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, - NULL, "Starting cryptops-api"); - - struct _u_instance instance; - 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; - - // 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. - int ret; - if (argc == 4 && strcmp("-secure", argv[1]) == 0) - { - // 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); - o_free(cert_pem); - } - else - { - // 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); - - // 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 - { - 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 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 -) -{ - 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"); - } - - 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. - 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); - return send_simple_response(response, 500, "error", - "error during unlocking"); - } - - // If we reach this point, apparently everything went well. - stop_server(); - return send_simple_response(response, 200, "status", "ok"); -} - -/** - * Default callback function called if no other endpoint matches. - */ -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; - 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; - } -} - -/** - * 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 = container_initialise(&cd, path); - 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 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) */ - 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; -} - -/** - * Use cryptsetup to initialise the luks container. - * It will not be opened (decrypted) yet, but it does check if the container - * seems usable. - */ -static int container_initialise -( - struct crypt_device **cd, /* struct to store crypt device context */ - const char *path /* path to the encrypted container */ -) -{ - // Let LUKS initialise the encrypted device. - int r = crypt_init(cd, path); - if (r < 0) - { - printf("crypt_init() failed for %s.\n", path); - printf("status: %d.\n", r); - return r; - } - - // 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)); - } - - return r; -} diff --git a/src/api/default.c b/src/api/default.c new file mode 100644 index 0000000000000000000000000000000000000000..b581ffb722473089a91654971c0e4cda6795d119 --- /dev/null +++ b/src/api/default.c @@ -0,0 +1,13 @@ +/** + * Default callback function called if no other endpoint matches. + */ +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; +} diff --git a/src/api/encryption_unlock.c b/src/api/encryption_unlock.c new file mode 100644 index 0000000000000000000000000000000000000000..1a57ef48803d5a144f45cce1965136da3c2bc3e8 --- /dev/null +++ b/src/api/encryption_unlock.c @@ -0,0 +1,47 @@ +/** + * 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 +) +{ + 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"); + } + + 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. + 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); + return send_simple_response(response, 500, "error", + "error during unlocking"); + } + + // If we reach this point, apparently everything went well. + int r = send_simple_response(response, 200, "status", "ok"); + stop_server(); + return r; +} diff --git a/src/auxiliary.c b/src/auxiliary.c new file mode 100644 index 0000000000000000000000000000000000000000..613d070f2a6ecc5238333ce809cc5bce42901e55 --- /dev/null +++ b/src/auxiliary.c @@ -0,0 +1,62 @@ +/** + * 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; + 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; + } +} + +/** + * 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; +} diff --git a/src/cryptops-api.c b/src/cryptops-api.c new file mode 100644 index 0000000000000000000000000000000000000000..88e576cd918874128506d8a8f2a148c697d0599b --- /dev/null +++ b/src/cryptops-api.c @@ -0,0 +1,81 @@ +#include <includes.h> +#include <settings.h> + +#define FIFO_PATH "/tmp/cryptops-api-stop" + +#include <auxiliary.c> +#include <encryption_functions.c> +#include <api/default.c> +#include <api/encryption_unlock.c> + +int main(int argc, char **argv) +{ + y_init_logs("cryptops-api", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, + NULL, "Starting cryptops-api"); + + struct _u_instance instance; + 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; + + // 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. + int ret; + if (argc == 4 && strcmp("-secure", argv[1]) == 0) + { + // 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); + o_free(cert_pem); + } + else + { + // 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); + + // 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 + { + 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; +} diff --git a/src/encryption_functions.c b/src/encryption_functions.c new file mode 100644 index 0000000000000000000000000000000000000000..6b7dc4d967f4760493e8a91b8539a90e257700c8 --- /dev/null +++ b/src/encryption_functions.c @@ -0,0 +1,83 @@ +/** + * Use cryptsetup to initialise the luks container. + * It will not be opened (decrypted) yet, but it does check if the container + * seems usable. + */ +static int container_initialise +( + struct crypt_device **cd, /* struct to store crypt device context */ + const char *path /* path to the encrypted container */ +) +{ + // Let LUKS initialise the encrypted device. + int r = crypt_init(cd, path); + if (r < 0) + { + printf("crypt_init() failed for %s.\n", path); + printf("status: %d.\n", r); + return r; + } + + // 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)); + } + + return r; +} + +/** + * 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 = container_initialise(&cd, path); + 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 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) */ + 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; +} diff --git a/src/includes.h b/src/includes.h new file mode 100644 index 0000000000000000000000000000000000000000..910cd619c913769648c973216a08fe6ca0fad744 --- /dev/null +++ b/src/includes.h @@ -0,0 +1,9 @@ +#include <string.h> +#include <sys/stat.h> +#include <fcntl.h> + +#define U_DISABLE_CURL +#define U_DISABLE_WEBSOCKET +#include <ulfius.h> +#include <jansson.h> +#include <libcryptsetup.h> diff --git a/src/settings.h b/src/settings.h new file mode 100644 index 0000000000000000000000000000000000000000..10122d0d82a84dda308221ff02a8db4c2ef4e71f --- /dev/null +++ b/src/settings.h @@ -0,0 +1,4 @@ +#define PREFIX "/cryptops/v0" +#define PORT 8000 +#define CONTAINER_DEVICE "/dev/xvda1" +#define MAPPED_DEVICE_NAME "xvda1_crypt"