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