diff options
Diffstat (limited to 'src/home/homectl-recovery-key.c')
-rw-r--r-- | src/home/homectl-recovery-key.c | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/src/home/homectl-recovery-key.c b/src/home/homectl-recovery-key.c new file mode 100644 index 0000000..ff1ab68 --- /dev/null +++ b/src/home/homectl-recovery-key.c @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "errno-util.h" +#include "glyph-util.h" +#include "homectl-recovery-key.h" +#include "libcrypt-util.h" +#include "memory-util.h" +#include "qrcode-util.h" +#include "random-util.h" +#include "recovery-key.h" +#include "strv.h" +#include "terminal-util.h" + +static int add_privileged(JsonVariant **v, const char *hashed) { + _cleanup_(json_variant_unrefp) JsonVariant *e = NULL, *w = NULL, *l = NULL; + int r; + + assert(v); + assert(hashed); + + r = json_build(&e, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("type", JSON_BUILD_CONST_STRING("modhex64")), + JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRING(hashed)))); + if (r < 0) + return log_error_errno(r, "Failed to build recover key JSON object: %m"); + + json_variant_sensitive(e); + + w = json_variant_ref(json_variant_by_key(*v, "privileged")); + l = json_variant_ref(json_variant_by_key(w, "recoveryKey")); + + r = json_variant_append_array(&l, e); + if (r < 0) + return log_error_errno(r, "Failed append recovery key: %m"); + + r = json_variant_set_field(&w, "recoveryKey", l); + if (r < 0) + return log_error_errno(r, "Failed to set recovery key array: %m"); + + r = json_variant_set_field(v, "privileged", w); + if (r < 0) + return log_error_errno(r, "Failed to update privileged field: %m"); + + return 0; +} + +static int add_public(JsonVariant **v) { + _cleanup_strv_free_ char **types = NULL; + int r; + + assert(v); + + r = json_variant_strv(json_variant_by_key(*v, "recoveryKeyType"), &types); + if (r < 0) + return log_error_errno(r, "Failed to parse recovery key type list: %m"); + + r = strv_extend(&types, "modhex64"); + if (r < 0) + return log_oom(); + + r = json_variant_set_field_strv(v, "recoveryKeyType", types); + if (r < 0) + return log_error_errno(r, "Failed to update recovery key types: %m"); + + return 0; +} + +static int add_secret(JsonVariant **v, const char *password) { + _cleanup_(json_variant_unrefp) JsonVariant *w = NULL, *l = NULL; + _cleanup_(strv_free_erasep) char **passwords = NULL; + int r; + + assert(v); + assert(password); + + w = json_variant_ref(json_variant_by_key(*v, "secret")); + l = json_variant_ref(json_variant_by_key(w, "password")); + + r = json_variant_strv(l, &passwords); + if (r < 0) + return log_error_errno(r, "Failed to convert password array: %m"); + + r = strv_extend(&passwords, password); + if (r < 0) + return log_oom(); + + r = json_variant_new_array_strv(&l, passwords); + if (r < 0) + return log_error_errno(r, "Failed to allocate new password array JSON: %m"); + + json_variant_sensitive(l); + + r = json_variant_set_field(&w, "password", l); + if (r < 0) + return log_error_errno(r, "Failed to update password field: %m"); + + r = json_variant_set_field(v, "secret", w); + if (r < 0) + return log_error_errno(r, "Failed to update secret object: %m"); + + return 0; +} + +int identity_add_recovery_key(JsonVariant **v) { + _cleanup_(erase_and_freep) char *password = NULL, *hashed = NULL; + int r; + + assert(v); + + /* First, let's generate a secret key */ + r = make_recovery_key(&password); + if (r < 0) + return log_error_errno(r, "Failed to generate recovery key: %m"); + + /* Let's UNIX hash it */ + r = hash_password(password, &hashed); + if (r < 0) + return log_error_errno(errno_or_else(EINVAL), "Failed to UNIX hash secret key: %m"); + + /* Let's now add the "privileged" version of the recovery key */ + r = add_privileged(v, hashed); + if (r < 0) + return r; + + /* Let's then add the public information about the recovery key */ + r = add_public(v); + if (r < 0) + return r; + + /* Finally, let's add the new key to the secret part, too */ + r = add_secret(v, password); + if (r < 0) + return r; + + /* We output the key itself with a trailing newline to stdout and the decoration around it to stderr + * instead. */ + + fflush(stdout); + fprintf(stderr, + "A secret recovery key has been generated for this account:\n\n" + " %s%s%s", + emoji_enabled() ? special_glyph(SPECIAL_GLYPH_LOCK_AND_KEY) : "", + emoji_enabled() ? " " : "", + ansi_highlight()); + fflush(stderr); + + fputs(password, stdout); + fflush(stdout); + + fputs(ansi_normal(), stderr); + fflush(stderr); + + fputc('\n', stdout); + fflush(stdout); + + fputs("\nPlease save this secret recovery key at a secure location. It may be used to\n" + "regain access to the account if the other configured access credentials have\n" + "been lost or forgotten. The recovery key may be entered in place of a password\n" + "whenever authentication is requested.\n", stderr); + fflush(stderr); + + (void) print_qrcode(stderr, "You may optionally scan the recovery key off screen", password); + + return 0; +} |