From a441485a358b0bdcb0b7a3e1c17e74078da1638c Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Sun, 3 Jan 2021 14:54:13 +0100 Subject: [PATCH] lightningd: regroup hsm_secret password input logic Signed-off-by: Antoine Poinsot --- common/hsm_encryption.c | 38 ++++++++++++++++++++ common/hsm_encryption.h | 7 ++++ lightningd/options.c | 19 ++++------ tools/hsmtool.c | 80 ++++++++++++++++++----------------------- 4 files changed, 87 insertions(+), 57 deletions(-) diff --git a/common/hsm_encryption.c b/common/hsm_encryption.c index d936fad2a..c9a85f773 100644 --- a/common/hsm_encryption.c +++ b/common/hsm_encryption.c @@ -1,6 +1,8 @@ +#include #include #include #include +#include char *hsm_secret_encryption_key(const char *pass, struct secret *key) @@ -36,3 +38,39 @@ void discard_key(struct secret *key TAKES) if (taken(key)) tal_free(key); } + +char *read_stdin_pass(char **reason) +{ + struct termios current_term, temp_term; + char *passwd = NULL; + size_t passwd_size = 0; + + /* Set a temporary term, same as current but with ECHO disabled. */ + if (tcgetattr(fileno(stdin), ¤t_term) != 0) { + *reason = "Could not get current terminal options."; + return NULL; + } + temp_term = current_term; + temp_term.c_lflag &= ~ECHO; + if (tcsetattr(fileno(stdin), TCSAFLUSH, &temp_term) != 0) { + *reason = "Could not disable pass echoing."; + return NULL; + } + + /* Read the password, do not take the newline character into account. */ + if (getline(&passwd, &passwd_size, stdin) < 0) { + *reason = "Could not read pass from stdin."; + return NULL; + } + if (passwd[strlen(passwd) - 1] == '\n') + passwd[strlen(passwd) - 1] = '\0'; + + /* Restore the original terminal */ + if (tcsetattr(fileno(stdin), TCSAFLUSH, ¤t_term) != 0) { + *reason = "Could not restore terminal options."; + free(passwd); + return NULL; + } + + return passwd; +} diff --git a/common/hsm_encryption.h b/common/hsm_encryption.h index 5239ba31e..ae358c000 100644 --- a/common/hsm_encryption.h +++ b/common/hsm_encryption.h @@ -19,4 +19,11 @@ char *hsm_secret_encryption_key(const char *pass, struct secret *encryption_key) */ void discard_key(struct secret *key TAKES); +/** Read hsm_secret encryption pass from stdin, disabling echoing. + * @reason: if NULL is returned, will point to the human-readable error. + * + * Caller must free the string as it does tal-reallocate getline's output. + */ +char *read_stdin_pass(char **reason); + #endif /* LIGHTNING_COMMON_HSM_ENCRYPTION_H */ diff --git a/lightningd/options.c b/lightningd/options.c index 63afe2e7a..e5b9297e3 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -389,8 +389,7 @@ static char *opt_important_plugin(const char *arg, struct lightningd *ld) static char *opt_set_hsm_password(struct lightningd *ld) { struct termios current_term, temp_term; - char *passwd = NULL, *passwd_confirmation = NULL, *err; - size_t passwd_size = 0; + char *passwd, *passwd_confirmation, *err; /* Get the password from stdin, but don't echo it. */ if (tcgetattr(fileno(stdin), ¤t_term) != 0) @@ -405,18 +404,14 @@ static char *opt_set_hsm_password(struct lightningd *ld) /* If we don't flush we might end up being buffered and we might seem * to hang while we wait for the password. */ fflush(stdout); - if (getline(&passwd, &passwd_size, stdin) < 0) - return "Could not read password from stdin."; + passwd = read_stdin_pass(&err); + if (!passwd) + return err; printf("Confirm hsm_secret password:\n"); fflush(stdout); - if (getline(&passwd_confirmation, &passwd_size, stdin) < 0) - return "Could not read password confirmation from stdin."; - if (!streq(passwd, passwd_confirmation)) - return "Password confirmation mismatch."; - if (passwd[strlen(passwd) - 1] == '\n') - passwd[strlen(passwd) - 1] = '\0'; - if (tcsetattr(fileno(stdin), TCSAFLUSH, ¤t_term) != 0) - return "Could not restore terminal options."; + passwd_confirmation = read_stdin_pass(&err); + if (!passwd_confirmation) + return err; printf("\n"); ld->config.keypass = tal(NULL, struct secret); diff --git a/tools/hsmtool.c b/tools/hsmtool.c index 6bbab5a30..14f3e4679 100644 --- a/tools/hsmtool.c +++ b/tools/hsmtool.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -168,46 +167,21 @@ static bool hsm_secret_is_encrypted(const char *hsm_secret_path) return st.st_size > 32; } -/* Read a pass from stdin, disabling echoing as done in lightning/options for the - * --encrypted-hsm startup option. - * Caller must free the returned string. */ -static char *read_stdin_pass(void) -{ - struct termios current_term, temp_term; - char *passwd = NULL; - size_t passwd_size = 0; - - if (tcgetattr(fileno(stdin), ¤t_term) != 0) - errx(ERROR_TERM, "Could not get current terminal options."); - temp_term = current_term; - temp_term.c_lflag &= ~ECHO; - if (tcsetattr(fileno(stdin), TCSAFLUSH, &temp_term) != 0) - errx(ERROR_TERM, "Could not disable pass echoing."); - /* If we don't flush we might end up being buffered and we might seem - * to hang while we wait for the password. */ - fflush(stdout); - if (getline(&passwd, &passwd_size, stdin) < 0) - errx(ERROR_TERM, "Could not read pass from stdin."); - if (passwd[strlen(passwd) - 1] == '\n') - passwd[strlen(passwd) - 1] = '\0'; - if (tcsetattr(fileno(stdin), TCSAFLUSH, ¤t_term) != 0) - errx(ERROR_TERM, "Could not restore terminal options."); - - return passwd; -} - static int decrypt_hsm(const char *hsm_secret_path) { int fd; struct secret hsm_secret; - char *passwd; + char *passwd, *err; const char *dir, *backup; /* This checks the file existence, too. */ if (!hsm_secret_is_encrypted(hsm_secret_path)) errx(ERROR_USAGE, "hsm_secret is not encrypted"); printf("Enter hsm_secret password:\n"); - passwd = read_stdin_pass(); + fflush(stdout); + passwd = read_stdin_pass(&err); + if (!passwd) + errx(ERROR_TERM, "%s", err); if (sodium_init() == -1) errx(ERROR_LIBSODIUM, @@ -265,9 +239,15 @@ static int encrypt_hsm(const char *hsm_secret_path) errx(ERROR_USAGE, "hsm_secret is already encrypted"); printf("Enter hsm_secret password:\n"); - passwd = read_stdin_pass(); + fflush(stdout); + passwd = read_stdin_pass(&err); + if (!passwd) + errx(ERROR_TERM, "%s", err); printf("Confirm hsm_secret password:\n"); - passwd_confirmation = read_stdin_pass(); + fflush(stdout); + passwd_confirmation = read_stdin_pass(&err); + if (!passwd_confirmation) + errx(ERROR_TERM, "%s", err); if (!streq(passwd, passwd_confirmation)) errx(ERROR_USAGE, "Passwords confirmation mismatch."); get_hsm_secret(&hsm_secret, hsm_secret_path); @@ -295,10 +275,8 @@ static int encrypt_hsm(const char *hsm_secret_path) errx(ERROR_LIBSODIUM, "Could not encrypt the seed."); /* Once the encryption key derived, we don't need it anymore. */ - if (passwd) - free(passwd); - if (passwd_confirmation) - free(passwd_confirmation); + free(passwd); + free(passwd_confirmation); /* Create a backup file, "just in case". */ rename(hsm_secret_path, backup); @@ -335,7 +313,7 @@ static int dump_commitments_infos(struct node_id *node_id, u64 channel_id, struct sha256 shaseed; struct secret hsm_secret, channel_seed, per_commitment_secret; struct pubkey per_commitment_point; - char *passwd; + char *passwd, *err; secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); @@ -343,7 +321,10 @@ static int dump_commitments_infos(struct node_id *node_id, u64 channel_id, /* This checks the file existence, too. */ if (hsm_secret_is_encrypted(hsm_secret_path)) { printf("Enter hsm_secret password:\n"); - passwd = read_stdin_pass(); + fflush(stdout); + passwd = read_stdin_pass(&err); + if (!passwd) + errx(ERROR_TERM, "%s", err); get_encrypted_hsm_secret(&hsm_secret, hsm_secret_path, passwd); free(passwd); } else @@ -388,7 +369,7 @@ static int guess_to_remote(const char *address, struct node_id *node_id, u64 tries, char *hsm_secret_path) { struct secret hsm_secret, channel_seed, basepoint_secret; - char *passwd; + char *passwd, *err; struct pubkey basepoint; struct ripemd160 pubkeyhash; /* We only support P2WPKH, hence 20. */ @@ -410,7 +391,10 @@ static int guess_to_remote(const char *address, struct node_id *node_id, /* This checks the file existence, too. */ if (hsm_secret_is_encrypted(hsm_secret_path)) { printf("Enter hsm_secret password:\n"); - passwd = read_stdin_pass(); + fflush(stdout); + passwd = read_stdin_pass(&err); + if (!passwd) + errx(ERROR_TERM, "%s", err); get_encrypted_hsm_secret(&hsm_secret, hsm_secret_path, passwd); free(passwd); } else @@ -512,14 +496,17 @@ static void read_mnemonic(char *mnemonic) { static int generate_hsm(const char *hsm_secret_path) { char mnemonic[BIP39_WORDLIST_LEN]; - char *passphrase; + char *passphrase, *err; read_mnemonic(mnemonic); printf("Warning: remember that different passphrases yield different " "bitcoin wallets.\n"); printf("If left empty, no password is used (echo is disabled).\n"); printf("Enter your passphrase: \n"); - passphrase = read_stdin_pass(); + fflush(stdout); + passphrase = read_stdin_pass(&err); + if (!passphrase) + errx(ERROR_TERM, "%s", err); if (strlen(passphrase) == 0) { free(passphrase); passphrase = NULL; @@ -558,7 +545,7 @@ static int dumponchaindescriptors(const char *hsm_secret_path, const char *old_p const bool is_testnet) { struct secret hsm_secret; - char *passwd; + char *passwd, *err; u8 bip32_seed[BIP32_ENTROPY_LEN_256]; u32 salt = 0; u32 version = is_testnet ? @@ -570,7 +557,10 @@ static int dumponchaindescriptors(const char *hsm_secret_path, const char *old_p /* This checks the file existence, too. */ if (hsm_secret_is_encrypted(hsm_secret_path)) { printf("Enter hsm_secret password:\n"); - passwd = read_stdin_pass(); + fflush(stdout); + passwd = read_stdin_pass(&err); + if (!passwd) + errx(ERROR_TERM, "%s", err); get_encrypted_hsm_secret(&hsm_secret, hsm_secret_path, passwd); free(passwd); } else