diff --git a/src/api/ssh_keys_delete.c b/src/api/ssh_keys_delete.c new file mode 100644 index 0000000000000000000000000000000000000000..27874e7583443d8159f03ac09987cadbc232ec1b --- /dev/null +++ b/src/api/ssh_keys_delete.c @@ -0,0 +1,54 @@ +/** + * Callback function that deletes an SSH key from the list of keys authorised + * for access to the initrd. The line will be left empty, because that keeps + * the ids if SSH keys in tact for ssh_keys_get + * + * Example output: + * {"ssh-keys":{"1":"ssh-rsa AAAAB3... example@example.com", + * "2":"ssh-rsa AAAAB3...","5":"command=\"/usr/bin/cryptops-client\" ssh-rsa + * AAAAB3... cryptops-test@greenhost"}} + * + * The indices correspond to line numbers of the authorized_keys file. + * Missing indices (like 3 and 4 in the example) arise from empty lines in the + * file; those are creted when keys are deleted. + * + * @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_ssh_keys_delete(const struct _u_request * request, + struct _u_response * response, void * user_data) +{ + // Read ssh key id from request URI. + const char * id_string = u_map_get(request->map_url, "id"); + if (id_string == NULL) + { + return send_simple_response(response, 400, "error", + "missing url parameter `id`"); + } + + int id; + int r = parse_int(id_string, &id); + if (r != 0) + { + printf("invalid url parameter `id`: %s\n", id_string); + return send_simple_response(response, 400, "error", + "invalid url parameter `id`"); + } + + // Replace the key at ID with "" + r = replace_ssh_key(id, ""); + if(r < 0) + { + if(r == -1) + return send_simple_response(response, 500, "error", + "error opening authorized_keys"); + if(r == -2) + return send_simple_response(response, 500, "error", + "error opening authorized_keys tmp file"); + return send_simple_response(response, 500, "error", + "Unknown error while processing ssh keys"); + } + return send_simple_response(response, 200, "status", "ok"); +} diff --git a/src/api/ssh_keys_put.c b/src/api/ssh_keys_put.c new file mode 100644 index 0000000000000000000000000000000000000000..4503e5dfd6b9a77e9d727052209d9d05204261d8 --- /dev/null +++ b/src/api/ssh_keys_put.c @@ -0,0 +1,66 @@ + + +/** + * Callback function that deletes an SSH key from the list of keys authorised + * for access to the initrd. The line will be left empty, because that keeps + * the ids if SSH keys in tact for ssh_keys_get + * + * Example output: + * {"ssh-keys":{"1":"ssh-rsa AAAAB3... example@example.com", + * "2":"ssh-rsa AAAAB3...","5":"command=\"/usr/bin/cryptops-client\" ssh-rsa + * AAAAB3... cryptops-test@greenhost"}} + * + * The indices correspond to line numbers of the authorized_keys file. + * Missing indices (like 3 and 4 in the example) arise from empty lines in the + * file; those are creted when keys are deleted. + * + * @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_ssh_keys_put(const struct _u_request * request, + struct _u_response * response, void * user_data) +{ + // Read ssh key id from request URI. + const char * id_string = u_map_get(request->map_url, "id"); + if (id_string == NULL) + { + return send_simple_response(response, 400, "error", + "missing url parameter `id`"); + } + + int id; + int r = parse_int(id_string, &id); + if (r != 0) + { + printf("invalid url parameter `id`: %s\n", id_string); + return send_simple_response(response, 400, "error", + "invalid url parameter `id`"); + } + + // Read in json request body. + json_t * json_input = ulfius_get_json_body_request(request, NULL); + + // Read SSH key from request. + const char * ssh_key; + ssh_key = json_string_value(json_object_get(json_input, "ssh-key")); + if (ssh_key == NULL) + { + return send_simple_response(response, 400, "error", "missing ssh-key"); + } + + r = replace_ssh_key(id, ssh_key); + if(r < 0) + { + if(r == -1) + return send_simple_response(response, 500, "error", + "error opening authorized_keys"); + if(r == -2) + return send_simple_response(response, 500, "error", + "error opening authorized_keys tmp file"); + return send_simple_response(response, 500, "error", + "Unknown error while processing ssh keys"); + } + return send_simple_response(response, 200, "status", "ok"); +} diff --git a/src/auxiliary.c b/src/auxiliary.c index c05da344af7476fe4e9bc36b8dcef94d062585ca..b5fcb2efab437781ef01b0456ecff4ed46649a38 100644 --- a/src/auxiliary.c +++ b/src/auxiliary.c @@ -191,3 +191,90 @@ int parse_int(const char * input, int * result) *result = l; return 0; } + +/** + * Write an ssh_key to a specific rule in the authorized_keys file. + * + * @param[in] id line number/ssh_key id + * @param[in] ssh_key ssh key contents. If this string is empty, this is seen + * as deleting the key. Newline characters are ignored + */ +int replace_ssh_key(int id, const char * ssh_key) +{ + // Open file. + FILE * authorized_keys_in = fopen(AUTHORIZED_KEYS_PATH, "r"); + // Check if that succeeded. + if (authorized_keys_in == NULL) + { + printf("Could not open authorized_keys file for reading\n"); + return -1; + } + + // Make tmp outfile for authorized keys + char * authorized_keys_out_name; + asprintf(&authorized_keys_out_name, "%s%s", AUTHORIZED_KEYS_PATH, ".tmp"); + + // Open file. + FILE * authorized_keys_out = fopen(authorized_keys_out_name, "w"); + // Check if that succeeded. + if (authorized_keys_out == NULL) + { + printf("Could not open authorized_keys tmp file for writing\n"); + return -2; + } + + int line_number = 1; + char ch; + + // Loop through all the characters in the input file + do { + ch = getc(authorized_keys_in); + + if (ch == '\n') + line_number++; + + if (line_number != id) + { + // Copy all lines that don't have id as line number + putc(ch, authorized_keys_out); + } + else + { + // Insert the new line first + putc(ch); + + // Copy ssh_key to the line that has id as line number + // Some magic happens here, where *ssh_key++ returns the current + // index and moves the pointer to the next index of ssh_key + // TODO: prepend ssh_key with command= line + while(*ssh_key) + { + printf("inserting %c\n", *ssh_key); + if(*ssh_key != '\n' && *ssh_key != '\r') + { + putc(*ssh_key++, authorized_keys_out); + } + } + printf("inserting new line %c\n", *ssh_key); + putc('\n', authorized_keys_out); + // Read characters until the next newline + do { + printf("not inserting %c\n", ch); + ch = getc(authorized_keys_in); + } while (ch != EOF && ch != '\n'); + printf("increasing line number to %d\n", line_number + 1); + // Increment line_number because the next loop will read a new + // character + line_number++; + } + } while (ch != EOF); + + + fclose(authorized_keys_in); + fclose(authorized_keys_out); + + // Remove old authorized_keys file and replace it with the new one + remove(AUTHORIZED_KEYS_PATH); + rename(authorized_keys_out_name, AUTHORIZED_KEYS_PATH); + return 0; +} diff --git a/src/cryptops-api.c b/src/cryptops-api.c index faaf253e4f8d3df0db6f20b0a6eaba4e17726663..de28f6c4c48129e98d58bc8cadd8c99fe331c038 100644 --- a/src/cryptops-api.c +++ b/src/cryptops-api.c @@ -10,7 +10,9 @@ #include <api/encryption_unlock.c> #include <api/encryption_keys_put.c> #include <api/ssh_keys_get.c> +#include <api/ssh_keys_put.c> #include <api/ssh_keys_post.c> +#include <api/ssh_keys_delete.c> int main(int argc, char ** argv) { @@ -50,9 +52,15 @@ int main(int argc, char ** argv) ulfius_add_endpoint_by_val(&instance, "GET" , PREFIX, "/ssh/keys", 0, &callback_ssh_keys_get, NULL); + ulfius_add_endpoint_by_val(&instance, "PUT" , PREFIX, + "/ssh/keys/:id", + 0, &callback_ssh_keys_put, NULL); ulfius_add_endpoint_by_val(&instance, "POST" , PREFIX, "/ssh/keys", 0, &callback_ssh_keys_post, NULL); + ulfius_add_endpoint_by_val(&instance, "DELETE" , PREFIX, + "/ssh/keys/:id", + 0, &callback_ssh_keys_delete, NULL); // Add default endpoint. ulfius_set_default_endpoint(&instance, &callback_default, NULL);