diff options
author | Agata Olender <agata.olender@intel.com> | 2020-01-13 19:51:24 +0300 |
---|---|---|
committer | Olender, Agata <agata.olender@intel.com> | 2020-02-06 12:10:36 +0300 |
commit | c33ba00b914c267d14f395a1127aca5dda17fee2 (patch) | |
tree | b8e8778384cd0c69d9a04d43658662133831fabd /virtual-media/src/state_machine.hpp | |
parent | a8b6b77d79bac80543033cc3bd140ff6cf4ba2f5 (diff) | |
download | provingground-c33ba00b914c267d14f395a1127aca5dda17fee2.tar.xz |
Authentication support for Legacy mode
This change introduces new 'Mount' API argument - UNIX_FD for named pipe.
This named pipe is utilized to securely send secret data over D-Bus.
Currently data consists of null-terminated char buffers with username and
password.
Data on receiving side is encapsulated into classes whose role is to:
- keep secret as short-lived as possible
- erase secret from memory when it's not needed
- pass secrets (and format them) to another secure container with above
capabilities
New classes:
- Credentials: is a class encapsulating login and password. It zeroes them
at destruction.
- CredentialProvider: contains Credentials, specifies SecureBuffer, allows
to store credentials in SecureBuffer
- SecureBuffer: char vector which zeroes itself at destruction,
used to provision secret data
- VolatileFile: class creating temporary file with 'owner-only' permissions
in /tmp; at destruction overwrites it's contents with '*' and removes it
New behavior:
- when UNIX_FD is provided over D-Bus it's treated as open unix pipe. Data
is read from this pipe and stored securely into CredentialsProvider
- credentials are stored in applications inside CredentialsProvider object,
encapsulated by unique_ptr for as long as it's needed
- strings containing secrets are zeroed immediately after use
- VolatileFile is used to securely pass credentials to nbdkit curl plugin
instead of command line parameters.
Tested:
Manual and automated tests on WilsonCity platform:
- positive and negative tests for authentication on both CIFS and HTTPS
resources
- error injection (ill-formed data transfered over pipe, pipe broken etc.)
Change-Id: I608ae0380b8ad57110bc0939f71eb48604e7dc99
Signed-off-by: Adrian Ambrożewicz <adrian.ambrozewicz@linux.intel.com>
Signed-off-by: Agata Olender <agata.olender@intel.com>
Diffstat (limited to 'virtual-media/src/state_machine.hpp')
-rw-r--r-- | virtual-media/src/state_machine.hpp | 102 |
1 files changed, 90 insertions, 12 deletions
diff --git a/virtual-media/src/state_machine.hpp b/virtual-media/src/state_machine.hpp index 9cba565..a788043 100644 --- a/virtual-media/src/state_machine.hpp +++ b/virtual-media/src/state_machine.hpp @@ -4,6 +4,7 @@ #include "logger.hpp" #include "smb.hpp" #include "system.hpp" +#include "utils.hpp" #include <sys/mount.h> @@ -455,15 +456,68 @@ struct MountPointStateMachine // Mount specialization if (isLegacy) { + using sdbusplus::message::unix_fd; + using optional_fd = std::variant<int, unix_fd>; + iface->register_method( - "Mount", [& machine = state.machine, this, - handleMount](boost::asio::yield_context yield, - std::string imgUrl, bool rw) { + "Mount", [& machine = state.machine, this, handleMount]( + boost::asio::yield_context yield, + std::string imgUrl, bool rw, optional_fd fd) { LogMsg(Logger::Info, "[App]: Mount called on ", getObjectPath(machine), machine.name); machine.target = {imgUrl, rw}; - return handleMount(yield, machine); + + if (std::holds_alternative<unix_fd>(fd)) + { + LogMsg(Logger::Debug, "[App] Extra data available"); + + // Open pipe and prepare output buffer + boost::asio::posix::stream_descriptor secretPipe( + machine.ioc.get(), + dup(std::get<unix_fd>(fd).fd)); + std::array<char, utils::secretLimit> buf; + + // Read data + auto size = secretPipe.async_read_some( + boost::asio::buffer(buf), yield); + + // Validate number of NULL delimiters, ensures + // further operations are safe + auto nullCount = std::count( + buf.begin(), buf.begin() + size, '\0'); + if (nullCount != 2) + { + throw sdbusplus::exception::SdBusError( + EINVAL, "Malformed extra data"); + } + + // First 'part' of payload + std::string user(buf.begin()); + // Second 'part', after NULL delimiter + std::string pass(buf.begin() + user.length() + 1); + + // Encapsulate credentials into safe buffer + machine.target->credentials = + std::make_unique<utils::CredentialsProvider>( + std::move(user), std::move(pass)); + + // Cover the tracks + utils::secureCleanup(buf); + } + + try + { + auto ret = handleMount(yield, machine); + machine.target->credentials.reset(); + return ret; + } + catch (...) + { + machine.target->credentials.reset(); + throw; + return false; + } }); } else @@ -644,7 +698,8 @@ struct MountPointStateMachine "\n Remote parent: ", remoteParent, "\n Local file: ", localFile); - if (!smb.mount(remoteParent, state.machine.target->rw)) + if (!smb.mount(remoteParent, state.machine.target->rw, + state.machine.target->credentials)) { fs::remove_all(*mountDir); return ReadyState(state, std::errc::invalid_argument, @@ -684,6 +739,7 @@ struct MountPointStateMachine static std::shared_ptr<Process> spawnNbdKit(MountPointStateMachine& machine, + std::unique_ptr<utils::VolatileFile>&& secret, const std::vector<std::string>& params) { // Investigate @@ -740,7 +796,8 @@ struct MountPointStateMachine args.insert(args.end(), params.begin(), params.end()); if (!process->spawn( - args, [& machine = machine](int exitCode, bool isReady) { + args, [& machine = machine, secret = std::move(secret)]( + int exitCode, bool isReady) { LogMsg(Logger::Info, machine.name, " process ended."); machine.emitSubprocessStoppedEvent(); })) @@ -756,24 +813,43 @@ struct MountPointStateMachine static std::shared_ptr<Process> spawnNbdKit(MountPointStateMachine& machine, const fs::path& file) { - return spawnNbdKit(machine, {// Use file plugin ... - "file", - // ... to mount file at this location - "file=" + file.string()}); + return spawnNbdKit(machine, {}, + {// Use file plugin ... + "file", + // ... to mount file at this location + "file=" + file.string()}); } static std::shared_ptr<Process> spawnNbdKit(MountPointStateMachine& machine, const std::string& url) { + std::unique_ptr<utils::VolatileFile> secret; std::vector<std::string> params = { // Use curl plugin ... "curl", // ... to mount http resource at url "url=" + url}; - // TODO: Authorization support in future patch + // Authenticate if needed + if (machine.target->credentials) + { + // Pack password into buffer + utils::CredentialsProvider::SecureBuffer buff = + machine.target->credentials->pack( + [](const std::string& user, const std::string& pass, + std::vector<char>& buff) { + std::copy(pass.begin(), pass.end(), + std::back_inserter(buff)); + }); + + // Prepare file to provide the password with + secret = std::make_unique<utils::VolatileFile>(std::move(buff)); + + params.push_back("user=" + machine.target->credentials->user()); + params.push_back("password=+" + secret->path()); + } - return spawnNbdKit(machine, params); + return spawnNbdKit(machine, std::move(secret), params); } bool checkUrl(const std::string& urlScheme, const std::string& imageUrl) @@ -939,6 +1015,7 @@ struct MountPointStateMachine name = std::move(machine.name); ioc = machine.ioc; config = std::move(machine.config); + target = std::move(machine.target); } return *this; } @@ -999,6 +1076,7 @@ struct MountPointStateMachine std::string imgUrl; bool rw; std::optional<fs::path> mountDir; + std::unique_ptr<utils::CredentialsProvider> credentials; }; std::reference_wrapper<boost::asio::io_context> ioc; |