From 3c3480a4a0cb79c7a34b658ef7ed8d59e0e01c25 Mon Sep 17 00:00:00 2001
From: Arie Peterson <arie@greenhost.nl>
Date: Thu, 6 Jul 2017 12:05:35 +0200
Subject: [PATCH] Add endpoint for changing encryption passwords

---
 src/api/encryption_keys_put.c  | 85 ++++++++++++++++++++++++++++++++++
 src/auxiliary.c                | 22 +++++++++
 src/cryptops-api.c             | 13 ++++--
 src/encryption_functions.c     |  4 +-
 src/includes/common-includes.h |  1 +
 5 files changed, 120 insertions(+), 5 deletions(-)
 create mode 100644 src/api/encryption_keys_put.c

diff --git a/src/api/encryption_keys_put.c b/src/api/encryption_keys_put.c
new file mode 100644
index 0000000..023b639
--- /dev/null
+++ b/src/api/encryption_keys_put.c
@@ -0,0 +1,85 @@
+/**
+ * Callback function to change a luks encryption password.
+ * @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_keys_put(const struct _u_request * request,
+    struct _u_response * response, void * user_data)
+{
+    int r;
+
+    // Read in json request body.
+    json_t * json_input = ulfius_get_json_body_request(request, NULL);
+
+    // Read (current) password from request.
+    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");
+    }
+
+    // Read new password from request.
+    const char * new_password;
+    new_password = json_string_value(json_object_get(json_input,
+        "new-password"));
+    if (new_password == NULL)
+    {
+        return send_simple_response(response, 400, "error",
+            "missing new password");
+    }
+
+    // Read keyslot from request URI.
+    const char * keyslot_string = u_map_get(request->map_url, "slot");
+    if (keyslot_string == NULL)
+    {
+        return send_simple_response(response, 500, "error",
+            "missing url parameter `slot`");
+    }
+    int keyslot;
+    r = parse_int(keyslot_string, &keyslot);
+    if (r != 0)
+    {
+        printf("invalid url parameter `slot`: %s\n", keyslot_string);
+        return send_simple_response(response, 500, "error",
+            "invalid url parameter `slot`");
+    }
+
+    // Initialise encrypted container.
+    struct crypt_device * cd = NULL;
+    r = container_initialise(&cd, DATA_PARTITION_DEVICE, true);
+    if (r < 0)
+    {
+        crypt_free(cd);
+    }
+    if (r != 0)
+    {
+        printf("container_initialise failed with status %d\n", r);
+        return send_simple_response(response, 500, "error",
+            "initialising encrypted container failed");
+    }
+
+    // Add encryption password.
+    r = crypt_keyslot_change_by_passphrase(cd, keyslot, keyslot,
+        password, strlen(password), new_password, strlen(new_password));
+  
+    if (r == -1)
+    {
+        // Experience learns that -1 is returned when the password is wrong.
+        return send_simple_response(response, 403, "error",
+           "incorrect password");
+    }
+  
+    if (r < 0)
+    {
+        // Something else went wrong.
+        printf("crypt_keyslot_add_by_passphrase failed with status %d\n", r);
+        return send_simple_response(response, 500, "error",
+            "error changing password");
+    }
+  
+    // If we reach this point, apparently everything went well.
+    return send_simple_response(response, 200, "status", "ok");
+}
diff --git a/src/auxiliary.c b/src/auxiliary.c
index 6226e2a..797839d 100644
--- a/src/auxiliary.c
+++ b/src/auxiliary.c
@@ -149,3 +149,25 @@ void reboot_initrd()
     // Parent (init) waits.
     while (1);
 }
+
+/**
+ * Parse an integer written in decimal.
+ * @param[in]  input  String to parse.
+ * @param[out] result Resulting integer, or NULL on failure.
+ * @return            Return code: 0 on success, nonzero on failure.
+ */
+int parse_int(const char * input, int * result)
+{
+    if (input[0] == '\0' || isspace((unsigned char) input[0]))
+    {
+        return -1;
+    }
+    char * end;
+    long l = strtol(input, &end, 10);
+    if (*end != '\0')
+    {
+        return -1;
+    }
+    *result = l;
+    return 0;
+}
diff --git a/src/cryptops-api.c b/src/cryptops-api.c
index 5c348cf..5d80f1a 100644
--- a/src/cryptops-api.c
+++ b/src/cryptops-api.c
@@ -8,6 +8,7 @@
 #include <api/default.c>
 #include <api/encryption_add.c>
 #include <api/encryption_unlock.c>
+#include <api/encryption_keys_put.c>
 #include <api/ssh_keys_get.c>
 
 int main(int argc, char ** argv)
@@ -36,11 +37,17 @@ int main(int argc, char ** argv)
 
     // Add api endpoints.
     bool reboot = false;
-    ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, "/encryption/add",
+    ulfius_add_endpoint_by_val(&instance, "POST", PREFIX,
+        "/encryption/add",
         0, &callback_encryption_add, &reboot);
-    ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, "/encryption/unlock",
+    ulfius_add_endpoint_by_val(&instance, "POST", PREFIX,
+        "/encryption/unlock",
         0, &callback_encryption_unlock, NULL);
-    ulfius_add_endpoint_by_val(&instance, "GET" , PREFIX, "/ssh/keys",
+    ulfius_add_endpoint_by_val(&instance, "PUT" , PREFIX,
+        "/encryption/keys/:slot",
+        0, &callback_encryption_keys_put, NULL);
+    ulfius_add_endpoint_by_val(&instance, "GET" , PREFIX,
+        "/ssh/keys",
         0, &callback_ssh_keys_get, NULL);
 
     // Add default endpoint.
diff --git a/src/encryption_functions.c b/src/encryption_functions.c
index 2a47b40..1bc128a 100644
--- a/src/encryption_functions.c
+++ b/src/encryption_functions.c
@@ -3,12 +3,12 @@
  * It will not be opened (decrypted) yet, but it does check if the container
  * seems usable.
  * @param crypt_device struct to store crypt device context
- * @param crypt_device path to the encrypted container
+ * @param path         path to the encrypted container
  * @param debug        whether to print debug messages
  * @return             status code
  */
 static int container_initialise(struct crypt_device ** cd, const char * path,
-     const bool debug)
+    const bool debug)
 {
     // Check if the device exists.
     struct stat st = {0};
diff --git a/src/includes/common-includes.h b/src/includes/common-includes.h
index aa3f9d6..a350af7 100644
--- a/src/includes/common-includes.h
+++ b/src/includes/common-includes.h
@@ -1,5 +1,6 @@
 #include <string.h>
 #include <stdbool.h>
+#include <ctype.h>
 #include <sys/stat.h>
 #include <sys/sysinfo.h>
 #include <sys/ioctl.h>
-- 
GitLab