diff options
author | Jack Lloyd <[email protected]> | 2018-12-02 16:31:16 -0500 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2018-12-02 16:31:16 -0500 |
commit | 163e59f4af51f802c5e77fc0bb8c0901a7fa62ea (patch) | |
tree | d6d274f449efea663d236bd293f4e1bebf7fc7c4 /src | |
parent | 7bc0745c3ff2824f9a3607db19e7e1a3e563c5bc (diff) | |
parent | c3cbf959aa628ae541e4009894b040ab242dd45e (diff) |
Merge GH #1756 Support getting passphrase from the console
Diffstat (limited to 'src')
-rw-r--r-- | src/cli/cli.cpp | 45 | ||||
-rw-r--r-- | src/cli/cli.h | 7 | ||||
-rw-r--r-- | src/cli/psk.cpp | 6 | ||||
-rw-r--r-- | src/cli/pubkey.cpp | 19 | ||||
-rw-r--r-- | src/cli/tls_client.cpp | 2 | ||||
-rw-r--r-- | src/cli/tls_http_server.cpp | 2 | ||||
-rw-r--r-- | src/cli/tls_proxy.cpp | 2 | ||||
-rw-r--r-- | src/cli/utils.cpp | 7 | ||||
-rw-r--r-- | src/cli/x509.cpp | 16 | ||||
-rw-r--r-- | src/lib/utils/os_utils.cpp | 101 | ||||
-rw-r--r-- | src/lib/utils/os_utils.h | 27 |
11 files changed, 186 insertions, 48 deletions
diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index f8ec55e7b..1918a4fbd 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -8,13 +8,10 @@ #include "argparse.h" #include <botan/rng.h> #include <botan/parsing.h> +#include <botan/internal/os_utils.h> #include <iostream> #include <fstream> -#if defined(BOTAN_TARGET_OS_HAS_POSIX1) - #include <termios.h> -#endif - namespace Botan_CLI { Command::Command(const std::string& cmd_spec) : m_spec(cmd_spec) @@ -210,33 +207,35 @@ Botan::RandomNumberGenerator& Command::rng() return *m_rng.get(); } -std::string Command::get_passphrase(const std::string& prompt) +std::string Command::get_passphrase_arg(const std::string& prompt, const std::string& opt_name) { - error_output() << prompt << ": " << std::flush; - std::string pass; + const std::string s = get_arg(opt_name); + if(s != "-") + return s; + return get_passphrase(prompt); + } -#if defined(BOTAN_TARGET_OS_HAS_POSIX1) +namespace { - struct termios old_flags; - int stdin_fd = fileno(stdin); - ::tcgetattr(stdin_fd, &old_flags); - struct termios noecho_flags = old_flags; - noecho_flags.c_lflag &= ~ECHO; - noecho_flags.c_lflag |= ECHONL; +bool echo_suppression_supported() + { + auto echo = Botan::OS::suppress_echo_on_terminal(); + return (echo != nullptr); + } - if(::tcsetattr(stdin_fd, TCSANOW, &noecho_flags) != 0) - throw CLI_Error("Clearing terminal echo bit failed"); +} - std::getline(std::cin, pass); +std::string Command::get_passphrase(const std::string& prompt) + { + if(echo_suppression_supported() == false) + error_output() << "Warning: terminal echo suppression not enabled for this platform\n"; - if(::tcsetattr(stdin_fd, TCSANOW, &old_flags) != 0) - throw CLI_Error("Restoring terminal echo bit failed"); -#else + error_output() << prompt << ": " << std::flush; + std::string pass; - // TODO equivalent for Windows ... - std::getline(std::cin, pass); + auto echo_suppress = Botan::OS::suppress_echo_on_terminal(); -#endif + std::getline(std::cin, pass); return pass; } diff --git a/src/cli/cli.h b/src/cli/cli.h index fca8e5225..b6f6a3076 100644 --- a/src/cli/cli.h +++ b/src/cli/cli.h @@ -124,6 +124,13 @@ class Command std::string get_arg(const std::string& opt_name) const; + /** + * Like get_arg but if the value is '-' then reads a passphrase from + * the terminal with echo suppressed. + */ + std::string get_passphrase_arg(const std::string& prompt, + const std::string& opt_name); + /* * Like get_arg() but if the argument was not specified or is empty, returns otherwise */ diff --git a/src/cli/psk.cpp b/src/cli/psk.cpp index 45a5918d8..83b60225d 100644 --- a/src/cli/psk.cpp +++ b/src/cli/psk.cpp @@ -27,7 +27,7 @@ class PSK_Tool_Base : public Command void go() override { const std::string db_filename = get_arg("db"); - const Botan::secure_vector<uint8_t> db_key = Botan::hex_decode_locked(get_arg("db_key")); + const Botan::secure_vector<uint8_t> db_key = Botan::hex_decode_locked(get_passphrase_arg("Database key", "db_key")); std::shared_ptr<Botan::SQL_Database> db = std::make_shared<Botan::Sqlite3_Database>(db_filename); Botan::Encrypted_PSK_Database_SQL psk(db_key, db, "psk"); @@ -53,8 +53,8 @@ class PSK_Tool_Set final : public PSK_Tool_Base void psk_operation(Botan::PSK_Database& db) override { const std::string name = get_arg("name"); - Botan::secure_vector<uint8_t> key = Botan::hex_decode_locked(get_arg("psk")); - db.set_vec(name, key); + const Botan::secure_vector<uint8_t> psk = Botan::hex_decode_locked(get_passphrase_arg("PSK", "psk")); + db.set_vec(name, psk); } }; diff --git a/src/cli/pubkey.cpp b/src/cli/pubkey.cpp index 46655a6ae..6011c9701 100644 --- a/src/cli/pubkey.cpp +++ b/src/cli/pubkey.cpp @@ -60,7 +60,7 @@ class PK_Keygen final : public Command throw CLI_Error_Unsupported("keygen", algo); } - const std::string pass = get_arg("passphrase"); + const std::string pass = get_passphrase_arg("Key passphrase", "passphrase"); const bool der_out = flag_set("der-out"); const std::chrono::milliseconds pbe_millis(get_arg_sz("pbe-millis")); @@ -168,11 +168,11 @@ class PK_Sign final : public Command void go() override { - std::unique_ptr<Botan::Private_Key> key( - Botan::PKCS8::load_key( - get_arg("key"), - rng(), - get_arg("passphrase"))); + const std::string key_file = get_arg("key"); + const std::string passphrase = get_passphrase_arg("Passphrase for " + key_file, "passphrase"); + + Botan::DataSource_Stream input(key_file); + std::unique_ptr<Botan::Private_Key> key = Botan::PKCS8::load_key(input, passphrase);; if(!key) { @@ -265,9 +265,10 @@ class PKCS8_Tool final : public Command void go() override { - const std::string pass_in = get_arg("pass-in"); + const std::string key_file = get_arg("key"); + const std::string pass_in = get_passphrase_arg("Password for " + key_file, "pass-in"); - Botan::DataSource_Memory key_src(slurp_file(get_arg("key"))); + Botan::DataSource_Memory key_src(slurp_file(key_file)); std::unique_ptr<Botan::Private_Key> key; if(pass_in.empty()) @@ -296,7 +297,7 @@ class PKCS8_Tool final : public Command } else { - const std::string pass_out = get_arg("pass-out"); + const std::string pass_out = get_passphrase_arg("Passphrase to encrypt key", "pass-out"); if(der_out) { diff --git a/src/cli/tls_client.cpp b/src/cli/tls_client.cpp index dfe874313..32ae4cea5 100644 --- a/src/cli/tls_client.cpp +++ b/src/cli/tls_client.cpp @@ -101,7 +101,7 @@ class TLS_Client final : public Command, public Botan::TLS::Callbacks if(!sessions_db.empty()) { #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) - const std::string sessions_passphrase = get_arg("session-db-pass"); + const std::string sessions_passphrase = get_passphrase_arg("Session DB passphrase", "session-db-pass"); session_mgr.reset(new Botan::TLS::Session_Manager_SQLite(sessions_passphrase, rng(), sessions_db)); #else error_output() << "Ignoring session DB file, sqlite not enabled\n"; diff --git a/src/cli/tls_http_server.cpp b/src/cli/tls_http_server.cpp index 6b5cefab4..cc59a71c3 100644 --- a/src/cli/tls_http_server.cpp +++ b/src/cli/tls_http_server.cpp @@ -531,7 +531,7 @@ class TLS_HTTP_Server final : public Command if(!sessions_db.empty()) { #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) - const std::string sessions_passphrase = get_arg("session-db-pass"); + const std::string sessions_passphrase = get_passphrase_arg("Session DB passphrase", "session-db-pass"); session_mgr.reset(new Botan::TLS::Session_Manager_SQLite(sessions_passphrase, rng(), sessions_db)); #else throw CLI_Error_Unsupported("Sqlite3 support not available"); diff --git a/src/cli/tls_proxy.cpp b/src/cli/tls_proxy.cpp index 717bbc6c6..49ffbe376 100644 --- a/src/cli/tls_proxy.cpp +++ b/src/cli/tls_proxy.cpp @@ -453,7 +453,7 @@ class TLS_Proxy final : public Command std::unique_ptr<Botan::TLS::Session_Manager> session_mgr; #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) - const std::string sessions_passphrase = get_arg("session-db-pass"); + const std::string sessions_passphrase = get_passphrase_arg("Session DB passphrase", "session-db-pass"); const std::string sessions_db = get_arg("session-db"); if(!sessions_db.empty()) diff --git a/src/cli/utils.cpp b/src/cli/utils.cpp index 1a7639937..99471fba5 100644 --- a/src/cli/utils.cpp +++ b/src/cli/utils.cpp @@ -540,7 +540,7 @@ class Generate_Bcrypt final : public Command void go() override { - const std::string password = get_arg("password"); + const std::string password = get_passphrase_arg("Passphrase to hash", "password"); const size_t wf = get_arg_sz("work-factor"); if(wf < 4 || wf > 18) @@ -574,7 +574,7 @@ class Check_Bcrypt final : public Command void go() override { - const std::string password = get_arg("password"); + const std::string password = get_passphrase_arg("Password to check", "password"); const std::string hash = get_arg("hash"); if(hash.length() != 60) @@ -585,6 +585,9 @@ class Check_Bcrypt final : public Command const bool ok = Botan::check_bcrypt(password, hash); output() << "Password is " << (ok ? "valid" : "NOT valid") << std::endl; + + if(ok == false) + set_return_code(1); } }; diff --git a/src/cli/x509.cpp b/src/cli/x509.cpp index adae8285c..d894b99a4 100644 --- a/src/cli/x509.cpp +++ b/src/cli/x509.cpp @@ -45,23 +45,25 @@ class Sign_Cert final : public Command void go() override { Botan::X509_Certificate ca_cert(get_arg("ca_cert")); - std::unique_ptr<Botan::Private_Key> key; - const std::string pass = get_arg("ca-key-pass"); + + const std::string key_file = get_arg("ca_key"); + const std::string pass = get_passphrase_arg("Password for " + key_file, "ca-key-pass"); const std::string emsa = get_arg("emsa"); const std::string hash = get_arg("hash"); + std::unique_ptr<Botan::Private_Key> key; if(!pass.empty()) { - key.reset(Botan::PKCS8::load_key(get_arg("ca_key"), rng(), pass)); + key.reset(Botan::PKCS8::load_key(key_file, rng(), pass)); } else { - key.reset(Botan::PKCS8::load_key(get_arg("ca_key"), rng())); + key.reset(Botan::PKCS8::load_key(key_file, rng())); } if(!key) { - throw CLI_Error("Failed to load key from " + get_arg("ca_key")); + throw CLI_Error("Failed to load key from " + key_file); } std::map<std::string, std::string> options; @@ -251,7 +253,9 @@ class Gen_Self_Signed final : public Command void go() override { - std::unique_ptr<Botan::Private_Key> key(Botan::PKCS8::load_key(get_arg("key"), rng(), get_arg("key-pass"))); + const std::string key_file = get_arg("key"); + const std::string passphrase = get_passphrase_arg("Passphrase for " + key_file, "key-pass"); + std::unique_ptr<Botan::Private_Key> key(Botan::PKCS8::load_key(key_file, rng(), passphrase)); if(!key) { diff --git a/src/lib/utils/os_utils.cpp b/src/lib/utils/os_utils.cpp index ab9c73b0c..6bebbab58 100644 --- a/src/lib/utils/os_utils.cpp +++ b/src/lib/utils/os_utils.cpp @@ -1,6 +1,6 @@ /* * OS and machine specific utility functions -* (C) 2015,2016,2017 Jack Lloyd +* (C) 2015,2016,2017,2018 Jack Lloyd * (C) 2016 Daniel Neus * * Botan is released under the Simplified BSD License (see license.txt) @@ -26,6 +26,8 @@ #include <setjmp.h> #include <unistd.h> #include <errno.h> + #include <termios.h> + #undef B0 #endif #if defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN) @@ -470,4 +472,101 @@ int OS::run_cpu_instruction_probe(std::function<int ()> probe_fn) return probe_result; } +std::unique_ptr<OS::Echo_Suppression> OS::suppress_echo_on_terminal() + { +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) + class POSIX_Echo_Suppression : public Echo_Suppression + { + public: + POSIX_Echo_Suppression() + { + m_stdin_fd = fileno(stdin); + if(::tcgetattr(m_stdin_fd, &m_old_termios) != 0) + throw System_Error("Getting terminal status failed", errno); + + struct termios noecho_flags = m_old_termios; + noecho_flags.c_lflag &= ~ECHO; + noecho_flags.c_lflag |= ECHONL; + + if(::tcsetattr(m_stdin_fd, TCSANOW, &noecho_flags) != 0) + throw System_Error("Clearing terminal echo bit failed", errno); + } + + void reenable_echo() override + { + if(m_stdin_fd > 0) + { + if(::tcsetattr(m_stdin_fd, TCSANOW, &m_old_termios) != 0) + throw System_Error("Restoring terminal echo bit failed", errno); + m_stdin_fd = -1; + } + } + + ~POSIX_Echo_Suppression() + { + try + { + reenable_echo(); + } + catch(...) + { + } + } + + private: + int m_stdin_fd; + struct termios m_old_termios; + }; + + return std::unique_ptr<Echo_Suppression>(new POSIX_Echo_Suppression); + +#elif defined(BOTAN_TARGET_OS_HAS_WIN32) + + class Win32_Echo_Suppression : public Echo_Suppression + { + public: + Win32_Echo_Suppression() + { + m_input_handle = ::GetStdHandle(STD_INPUT_HANDLE); + if(::GetConsoleMode(m_input_handle, &m_console_state) == 0) + throw System_Error("Getting console mode failed", ::GetLastError()); + + DWORD new_mode = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; + if(::SetConsoleMode(m_input_handle, new_mode) == 0) + throw System_Error("Setting console mode failed", ::GetLastError()); + } + + void reenable_echo() override + { + if(m_input_handle != INVALID_HANDLE_VALUE) + { + if(::SetConsoleMode(m_input_handle, m_console_state) == 0) + throw System_Error("Setting console mode failed", ::GetLastError()); + m_input_handle = INVALID_HANDLE_VALUE; + } + } + + ~Win32_Echo_Suppression() + { + try + { + reenable_echo(); + } + catch(...) + { + } + } + + private: + HANDLE m_input_handle; + DWORD m_console_state; + }; + + return std::unique_ptr<Echo_Suppression>(new Win32_Echo_Suppression); +#endif + + // Not supported on this platform, return null + return std::unique_ptr<Echo_Suppression>(); + } + } diff --git a/src/lib/utils/os_utils.h b/src/lib/utils/os_utils.h index 778ace4e9..39e30fcb7 100644 --- a/src/lib/utils/os_utils.h +++ b/src/lib/utils/os_utils.h @@ -1,6 +1,6 @@ /* * OS specific utility functions -* (C) 2015,2016,2017 Jack Lloyd +* (C) 2015,2016,2017,2018 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -117,6 +117,31 @@ void free_locked_pages(void* ptr, size_t length); */ int BOTAN_TEST_API run_cpu_instruction_probe(std::function<int ()> probe_fn); +/** +* Represents a terminal state +*/ +class BOTAN_UNSTABLE_API Echo_Suppression + { + public: + /** + * Reenable echo on this terminal. Can be safely called + * multiple times. May throw if an error occurs. + */ + virtual void reenable_echo() = 0; + + /** + * Implicitly calls reenable_echo, but swallows/ignored all + * errors which would leave the terminal in an invalid state. + */ + virtual ~Echo_Suppression() = default; + }; + +/** +* Suppress echo on the terminal +* Returns null if this operation is not supported on the current system. +*/ +std::unique_ptr<Echo_Suppression> BOTAN_UNSTABLE_API suppress_echo_on_terminal(); + } } |