From d8054d1a1afe7cc735073f0e80b7682bf8d73eac Mon Sep 17 00:00:00 2001 From: Ingo Franzki Date: Tue, 21 Jul 2020 10:39:10 +0200 Subject: [PATCH] zkey-ekmfweb: Add login support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To perform operations in EKMF Web, the user must log in with a EKMF Web user id and a time based one time passcode. The passcode can be obtained by the user from the EKMF Web user interface, after logging in into EKMF Web. That way a two factor authentication is performed. The plugin passes the passcode to EKMF Web retrieves a bearer token from EKMF Web which it then uses on subsequent requests to authenticate with EKMF Web. Such a bearer token is valid for several minutes, thus no re-login is required for zkey commands run during that time. Signed-off-by: Ingo Franzki Signed-off-by: Jan Höppner --- zkey/ekmfweb/zkey-ekmfweb.c | 159 +++++++++++++++++++++++++++++++++++- zkey/ekmfweb/zkey-ekmfweb.h | 5 ++ 2 files changed, 162 insertions(+), 2 deletions(-) diff --git a/zkey/ekmfweb/zkey-ekmfweb.c b/zkey/ekmfweb/zkey-ekmfweb.c index b5425932..259c1488 100644 --- a/zkey/ekmfweb/zkey-ekmfweb.c +++ b/zkey/ekmfweb/zkey-ekmfweb.c @@ -405,6 +405,9 @@ static int _get_ekmf_config(struct plugin_handle *ph) free(tmp); ph->ekmf_config.max_redirs = 0; + ph->ekmf_config.login_token = properties_get(ph->properties, + EKMFWEB_CONFIG_LOGIN_TOKEN); + return 0; } @@ -439,6 +442,29 @@ static void _free_ekmf_config(struct plugin_handle *ph) free((void *)ph->ekmf_config.ekmf_server_pubkey); } +/** + * Removes the login token file, if the error indicates an authorization or + * authentication error (-EACCES or -EPERM) + * + * @param ph the plugin handle + * @param error the negative errno value of the last error + */ +static void _remove_login_token_if_error(struct plugin_handle *ph, int error) +{ + switch (error) { + case -EACCES: + case -EPERM: + remove(ph->ekmf_config.login_token); + FREE_AND_SET_NULL(ph->ekmf_config.login_token); + break; + default: + break; + } + + return; + +} + /** * Initializes a KMS plugin for usage by zkey. When a repository is bound to a * KMS plugin, zkey calls this function when opening the repository. @@ -1805,6 +1831,41 @@ int kms_deconfigure(const kms_handle_t handle) return 0; } +/** + * Prompts the user for input on stdin, and returns the entered value. + * The returned string must be freed by the caller. + * + * @param ph the plugin handle + * @param msg the message to prompt for the input (can be NULL) + * + * @returns the entered value, or NULL in case of an error. + */ +static char *_prompt_for_input(struct plugin_handle *ph, const char *msg) +{ + size_t input_len = 0; + char *input = NULL; + int rc; + + while (input_len == 0 || input == NULL || strlen(input) < 1) { + if (msg != NULL) + printf("%s: %s: ", program_invocation_short_name, msg); + + rc = getline(&input, &input_len, stdin); + if (rc < 0) { + _set_error(ph, "Failed to read from stdin: %s", + strerror(errno)); + if (input != NULL) + free(input); + return NULL; + } + + if (input != NULL && input[strlen(input) - 1] == '\n') + input[strlen(input) - 1] = '\0'; + } + + return input; +} + /** * Allows the KMS plugin to perform a login to the KMS (if required). This * function is called at least once before any key operation function, typically @@ -1826,6 +1887,12 @@ int kms_deconfigure(const kms_handle_t handle) int kms_login(const kms_handle_t handle) { struct plugin_handle *ph = handle; + char *passcode_url = NULL; + char *error_msg = NULL; + char *passcode = NULL; + char *user_id = NULL; + bool valid = false; + int rc; util_assert(handle != NULL, "Internal error: handle is NULL"); @@ -1833,14 +1900,102 @@ int kms_login(const kms_handle_t handle) _clear_error(ph); - if (!ph->config_complete) { + if (!ph->connection_configured) { _set_error(ph, "The configuration is incomplete, run 'zkey " "kms configure [OPTIONS]' to complete the " "configuration."); return -EINVAL; } - return 0; + if (ph->ekmf_config.login_token != NULL) { + rc = ekmf_check_login_token(&ph->ekmf_config, &valid, NULL, + ph->verbose); + pr_verbose(ph, "Login token valid: %d", valid); + + if (rc == 0 && valid) + return 0; + + remove(ph->ekmf_config.login_token); + FREE_AND_SET_NULL(ph->ekmf_config.login_token); + + rc = _set_or_remove_property(ph, EKMFWEB_CONFIG_LOGIN_TOKEN, + NULL); + if (rc != 0) + goto out; + } + + passcode_url = properties_get(ph->properties, + EKMFWEB_CONFIG_PASSCODE_URL); + if (passcode_url == NULL) { + util_asprintf(&passcode_url, "%s%s", ph->ekmf_config.base_url, + EKMFWEB_PASSCODE_URL); + + rc = _set_or_remove_property(ph, EKMFWEB_CONFIG_PASSCODE_URL, + passcode_url); + if (rc != 0) + goto out; + } + + pr_verbose(ph, "passcode url: '%s'", passcode_url); + + user_id = _prompt_for_input(ph, "EKMF Web user ID"); + if (user_id == NULL) { + rc = -EIO; + goto out; + } + + pr_verbose(ph, "User-id: '%s'", user_id); + + util_print_indented("Go to the following web page in your web browser, " + "login with the same user ID as entered above and " + "your password, and obtain a one time passcode and " + "enter it here.", 0); + printf("%s\n", passcode_url); + + passcode = _prompt_for_input(ph, "Passcode"); + if (passcode == NULL) { + rc = -EIO; + goto out; + } + + pr_verbose(ph, "Passcode: '%s'", passcode); + + util_asprintf((char **)&ph->ekmf_config.login_token, "%s/%s", + ph->config_path, EKMFWEB_CONFIG_LOGIN_TOKEN_FILE); + + rc = ekmf_login(&ph->ekmf_config, &ph->curl_handle, user_id, passcode, + &error_msg, ph->verbose); + if (rc != 0) { + _set_error(ph, "Failed to login to EKMF Web server at '%s': " + "%s", ph->ekmf_config.base_url, + error_msg != NULL ? error_msg : strerror(-rc)); + goto out; + } + + rc = _set_file_permission(ph, ph->ekmf_config.login_token); + if (rc != 0) + goto out; + + rc = _set_or_remove_property(ph, EKMFWEB_CONFIG_LOGIN_TOKEN, + ph->ekmf_config.login_token); + if (rc != 0) + goto out; + + rc = _save_config(ph); + if (rc != 0) + goto out; + +out: + if (passcode_url != NULL) + free(passcode_url); + if (user_id != NULL) + free(user_id); + if (passcode != NULL) + free(passcode); + if (error_msg != NULL) + free(error_msg); + + return rc; } /** diff --git a/zkey/ekmfweb/zkey-ekmfweb.h b/zkey/ekmfweb/zkey-ekmfweb.h index 09c20d37..9f1d41e5 100644 --- a/zkey/ekmfweb/zkey-ekmfweb.h +++ b/zkey/ekmfweb/zkey-ekmfweb.h @@ -33,6 +33,7 @@ struct plugin_handle { #define EKMFWEB_CONFIG_FILE "ekmfweb.conf" #define EKMFWEB_CONFIG_SERVER_CERT_FILE "server-cert.pem" #define EKMFWEB_CONFIG_SERVER_PUBKEY_FILE "server-pubkey.pem" +#define EKMFWEB_CONFIG_LOGIN_TOKEN_FILE "login.token" #define EKMFWEB_CONFIG_APQNS "apqns" #define EKMFWEB_CONFIG_URL "url" @@ -44,5 +45,9 @@ struct plugin_handle { #define EKMFWEB_CONFIG_SERVER_PUBKEY "server-pubkey" #define EKMFWEB_CONFIG_VERIFY_SERVER_CERT "verify-server-cert" #define EKMFWEB_CONFIG_VERIFY_HOSTNAME "verify-hostname" +#define EKMFWEB_CONFIG_LOGIN_TOKEN "login-token" +#define EKMFWEB_CONFIG_PASSCODE_URL "passcode-url" + +#define EKMFWEB_PASSCODE_URL "/administration/passcode" #endif