diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index 796a4e06f9f..dc377553ced 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -105,7 +105,8 @@ enum { OPT_DEBUG_TLS, OPT_API_VERSION, OPT_MEMORY, - OPT_USE_FUTURE_PROTOCOL_VERSION + OPT_USE_FUTURE_PROTOCOL_VERSION, + OPT_ENCRYPT }; CSimpleOpt::SOption g_rgOptions[] = { { OPT_CONNFILE, "-C", SO_REQ_SEP }, @@ -132,6 +133,7 @@ CSimpleOpt::SOption g_rgOptions[] = { { OPT_CONNFILE, "-C", SO_REQ_SEP }, { OPT_API_VERSION, "--api-version", SO_REQ_SEP }, { OPT_MEMORY, "--memory", SO_REQ_SEP }, { OPT_USE_FUTURE_PROTOCOL_VERSION, "--use-future-protocol-version", SO_NONE }, + { OPT_ENCRYPT, "--encrypt", SO_REQ_SEP }, TLS_OPTION_FLAGS, SO_END_OF_OPTIONS }; @@ -506,6 +508,11 @@ static void printProgramUsage(const char* name) { " --use-future-protocol-version\n" " Use the simulated future protocol version to connect to the cluster.\n" " This option can be used testing purposes only!\n" + " --encrypt PASSWORD\n" + " Encrypts the specified password and prints the encrypted password\n" + " with the `encrypted:' prefix. The encrypted password can be used\n" + " with --tls-password option. This option causes fdbcli to encrypt\n" + " the password and exit.\n" " -v, --version Print FoundationDB CLI version information and exit.\n" " -h, --help Display this help and exit.\n"); } @@ -906,7 +913,6 @@ void LogCommand(std::string line, UID randomID, std::string errMsg) { printf("%s\n", errMsg.c_str()); TraceEvent(SevInfo, "CLICommandLog", randomID).detail("Command", line).detail("Error", errMsg); } - struct CLIOptions { std::string program_name; int exit_code = -1; @@ -932,6 +938,7 @@ struct CLIOptions { std::string tlsPassword; bool tlsDisablePlainTextConnection = false; uint64_t memLimit = 8uLL << 30; + Optional encrypt; std::vector> knobs; @@ -1077,6 +1084,9 @@ struct CLIOptions { knobs.emplace_back(knobName.get(), args.OptionArg()); break; } + case OPT_ENCRYPT: + encrypt = args.OptionArg(); + break; case OPT_DEBUG_TLS: debugTLS = true; break; @@ -2430,6 +2440,16 @@ int main(int argc, char** argv) { if (opt.exit_code != -1) return opt.exit_code; + if (opt.encrypt.present()) { + std::string encrypted; + if (!TLSConfig::encodePassword(opt.encrypt.get(), encrypted)) { + fprintf(stderr, "ERROR: Failed to encrypt password\n"); + return 1; + } + printf("%s\n", encrypted.c_str()); + return 0; + } + if (opt.trace) { if (opt.traceDir.empty()) setNetworkOption(FDBNetworkOptions::TRACE_ENABLE); diff --git a/flow/TLSConfig.actor.cpp b/flow/TLSConfig.actor.cpp index d7bd7fa0962..02afcc473e7 100644 --- a/flow/TLSConfig.actor.cpp +++ b/flow/TLSConfig.actor.cpp @@ -1037,3 +1037,78 @@ bool TLSPolicy::verify_peer(bool preverified, X509_STORE_CTX* store_ctx, const N return verifier.isOk(); } +struct CryptoLibHandle { + void* lib = nullptr; + void* func = nullptr; + + CryptoLibHandle(std::string_view funcName) { + const char* libName = "libnscipher-crypto.so"; + lib = loadLibrary(libName); + if (!lib) { + TraceEvent(SevError, "ExternalLibLoadError").detail("Library", libName); + return; + } + func = loadFunction(lib, funcName.data()); + if (!func) { + TraceEvent(SevError, "ExternalLibFunctionLoadError") + .detail("Function", funcName) + .detail("Library", libName); + fprintf(stderr, "ERROR: Failed to load '%s' function\n", funcName.data()); + closeLibrary(lib); + lib = nullptr; + } + } + explicit operator bool() const { return func != nullptr; } + ~CryptoLibHandle() { + if (lib) + closeLibrary(lib); + } +}; + +static bool processWithCrypto(std::string_view funcName, const std::string& input, std::string& output) { + constexpr int bufLen = 1024; // Assume max size of encrypted and decrypted password is 1024 + CryptoLibHandle cryptoHandle(funcName); + + if (!cryptoHandle) { + return false; + } + + int outputLen = bufLen; + + char buf[bufLen]{}; + + auto func = reinterpret_cast(cryptoHandle.func); + if (int rc = func(input.c_str(), buf, &outputLen); rc != 0) { + fprintf(stderr, "ERROR: Failed to exec function (rc=%d)\n", rc); + TraceEvent(SevError, "ErrorExecFunction").detail("ReturnCode", rc); + return false; + } + output.assign(buf, outputLen); + return true; +} + +constexpr std::string_view encryptedPrefix = "encrypted:"; + +bool TLSConfig::encodePassword(const std::string& plainPassword, std::string& encoded) { + if (processWithCrypto("crypt", plainPassword, encoded)) { + encoded.insert(0, encryptedPrefix); + return true; + } + return false; +} + +void TLSConfig::setPassword(const std::string& password) { + if (password.size() > encryptedPrefix.size() && password.starts_with(encryptedPrefix)) { + + std::string decoded; + + if (processWithCrypto("decrypt", password.substr(encryptedPrefix.size()), decoded)) { + tlsPassword = std::move(decoded); + } else { + tlsPassword.clear(); + TraceEvent(SevError, "FailedToDecryptPassword"); + } + } else { + tlsPassword = password; + } +} diff --git a/flow/include/flow/TLSConfig.actor.h b/flow/include/flow/TLSConfig.actor.h index f4b7aa70c9f..7e98484251a 100644 --- a/flow/include/flow/TLSConfig.actor.h +++ b/flow/include/flow/TLSConfig.actor.h @@ -184,7 +184,9 @@ class TLSConfig { void setDisablePlainTextConnection(const bool val) { tlsDisablePlainTextConnection = val; } - void setPassword(const std::string& password) { tlsPassword = password; } + void setPassword(const std::string& password); + + static bool encodePassword(const std::string& plainPassword, std::string& encoded); void clearVerifyPeers() { tlsVerifyPeers.clear(); }