This PR
- adds
-stdinwalletpassphrase
for use withwalletpasshprase(change)
- adds no-echo for passwords (
-stdinrpcpass
and above)
It may not be ideal, but it’s better than having to clear the screen whenever you unlock the wallet.
This PR
-stdinwalletpassphrase
for use with walletpasshprase(change)
-stdinrpcpass
and above)It may not be ideal, but it’s better than having to clear the screen whenever you unlock the wallet.
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
No conflicts as of last run.
77@@ -78,6 +78,14 @@
78 #include <openssl/conf.h>
79 #include <thread>
80
81+#ifdef WIN32
util.cpp
(which is shared between the server and client), would be better in a client-specific utility, e.g. compat/noecho.{cpp,h}
or something like that, that is only linked into -cli
.
77@@ -78,6 +78,14 @@
78 #include <openssl/conf.h>
79 #include <thread>
80
81+#ifdef WIN32
82+#include <windows.h> // for SetStdinEcho()
83+#else
84+#include <termios.h> // for SetStdinEcho()
415@@ -414,14 +416,33 @@ static int CommandLineRPC(int argc, char *argv[])
416 argc--;
417 argv++;
418 }
419+ SetStdinEcho(false);
-stdin
processing-stdinrpcpass
or -stdinwalletpassphrase
is called to avoid interfering with scripts that pipe in data, not attached to a TTYChecked that this compiles and works as-is on OpenBSD 6.3 and FreeBSD 11.2-RELEASE-p2.
Would be good to have functional tests for this, in as far as that is possible—seems tricky to simulate a terminal
Is it even possible to test stdin echo?
Yes, it’s certainly possible. However I’m afraid that it’d involve allocating a pty and emulating an actual terminal, all from python. There is code for that, but it’s probably a bit too much to pull in a dependency for. It’s a good indication that we should stop here at accommodating terminals, though, at the minimum possible code dedicated to this (which is easy to manually test.)
429 gArgs.ForceSetArg("-rpcpassword", rpcPass);
430+ SetStdinEcho(true);
431 }
432 std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]);
433+ if (gArgs.GetBoolArg("-stdinwalletpassphrase", false)) {
434+ SetStdinEcho(false);
will this leave the user’s terminal in a bad state in case of an exception?
if so, might want to use a RAII class
From the control flow, it seems a bit odd that one gets asked for the password first via std-in, but then get an error like RPC password> error: too few parameters (need at least command)
.
IMO a) we should newline after a password read and b) eventually check for missing command (and other static checks) before asking for the password.
21+{
22+#ifdef WIN32
23+ HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
24+ DWORD mode;
25+ GetConsoleMode(hStdin, &mode);
26+ if (!enable) mode &= ~ENABLE_ECHO_INPUT; else mode |= ENABLE_ECHO_INPUT;
02018-09-22 21:56:54 cpplint(pr=13716): src/compat/stdin.cpp:26: Else clause should never be on same line as else (use 2 lines) [whitespace/newline] [4]
12018-09-22 21:56:54 cpplint(pr=13716): src/compat/stdin.cpp:26: If/else bodies with multiple statements require braces [readability/braces] [4]
26+ if (!enable) mode &= ~ENABLE_ECHO_INPUT; else mode |= ENABLE_ECHO_INPUT;
27+ SetConsoleMode(hStdin, mode);
28+#else
29+ struct termios tty;
30+ tcgetattr(STDIN_FILENO, &tty);
31+ if (!enable) tty.c_lflag &= ~ECHO; else tty.c_lflag |= ECHO;
02018-09-22 21:56:54 cpplint(pr=13716): src/compat/stdin.cpp:31: Else clause should never be on same line as else (use 2 lines) [whitespace/newline] [4]
12018-09-22 21:56:54 cpplint(pr=13716): src/compat/stdin.cpp:31: If/else bodies with multiple statements require braces [readability/braces] [4]
430 }
431 std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]);
432+ if (gArgs.GetBoolArg("-stdinwalletpassphrase", false)) {
433+ NO_STDIN_ECHO();
434+ std::string walletPass;
435+ if (args.size() < 1 || args[0].substr(0, strlen("walletpassphrase")) != "walletpassphrase") {
strlen
? :-)
tACK b197e57 for macOS 10.14.3
Nit: return \n
after password entry.
Needs Windows testing too, @ken2812221?
For a followup it would be great if all wallet RPC’s that need a private key worked directly with -stdinwalletpassphrase
and just lock when they’re done (though then the question is what default timeout you need).
@kallewoof it didn’t return a new line for me (macOS 10.14.3):
re-tACK 110e821
-stdinrpcpass
as well. Fixed in d1688c9.
412@@ -411,18 +413,41 @@ static int CommandLineRPC(int argc, char *argv[])
413 }
414 std::string rpcPass;
415 if (gArgs.GetBoolArg("-stdinrpcpass", false)) {
416+ NO_STDIN_ECHO();
417+ if (!StdinReady()) {
418+ fprintf(stderr, "RPC password> ");
tfm::format(std::cerr, ...
should work as well
fputs
.
419+ fflush(stderr);
420+ }
421 if (!std::getline(std::cin, rpcPass)) {
422 throw std::runtime_error("-stdinrpcpass specified but failed to read from standard input");
423 }
424+ fputc('\n', stdout);
Understood. I think the latest version achieves this. (See StdinTerminal()
.)
0$ ./bitcoin-cli -datadir=d -regtest encryptwallet foobar38
1wallet encrypted; The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup.
2$ ./bitcoin-cli -datadir=d -regtest -stdinwalletpassphrase walletpassphrase 10
3Wallet passphrase>
4$ echo foobar38 | ./bitcoin-cli -datadir=d -regtest -stdinwalletpassphrase walletpassphrase 10
5$ echo xfoobar38 | ./bitcoin-cli -datadir=d -regtest -stdinwalletpassphrase walletpassphrase 10
6error code: -14
7error message:
8Error: The wallet passphrase entered was incorrect.