| // Copyright 2019 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "login_manager/secret_util.h" | |
| #include <unistd.h> | |
| #include <utility> | |
| #include <base/check_op.h> | |
| #include <base/file_descriptor_posix.h> | |
| #include <base/files/file_util.h> | |
| #include <base/files/scoped_file.h> | |
| #include <base/logging.h> | |
| #include <base/memory/read_only_shared_memory_region.h> | |
| #include <base/process/process_handle.h> | |
| #include <base/strings/string_number_conversions.h> | |
| #include <crypto/sha2.h> | |
| #include <gmock/gmock.h> | |
| #include <gtest/gtest.h> | |
| namespace login_manager { | |
| namespace secret_util { | |
| namespace { | |
| // Maximum amount of data that can be sent through the pipe using |secret_util| | |
| // methods. | |
| // 64k of data, minus 64 bits for a preceding size. This number was chosen to | |
| // fit all the data in a single pipe buffer and avoid blocking on write. | |
| // (http://man7.org/linux/man-pages/man7/pipe.7.html) | |
| const size_t kPipeSecretSizeLimit = 1024 * 64 - sizeof(size_t); | |
| // Reads |data_size| from the pipe. Returns 'false' if it couldn't read | |
| // |data_size| or if |data_size| was incorrect. | |
| bool GetSecretDataSizeFromPipe(int in_secret_fd, size_t* data_size_out) { | |
| if (!base::ReadFromFD(in_secret_fd, reinterpret_cast<char*>(data_size_out), | |
| sizeof(size_t))) { | |
| PLOG(ERROR) << "Could not read secret size from file."; | |
| return false; | |
| } | |
| if (*data_size_out == 0 || *data_size_out > kPipeSecretSizeLimit) { | |
| LOG(ERROR) << "Invalid data size read from file descriptor. Size read: " | |
| << *data_size_out; | |
| return false; | |
| } | |
| return true; | |
| } | |
| } // namespace | |
| // Limiting data size to 10MB here because it covers the current use cases, | |
| // it can be increased if needed up to an operating system limit (man 1 lsipc). | |
| const size_t kSharedMemorySecretSizeLimit = 10 * 1024 * 1024; | |
| SharedMemoryUtil::~SharedMemoryUtil() = default; | |
| base::ScopedFD SharedMemoryUtil::WriteDataToSharedMemory( | |
| const std::vector<uint8_t>& data) { | |
| size_t data_size = data.size(); | |
| CHECK_LE(data_size, kSharedMemorySecretSizeLimit); | |
| // Creates a new base::ReadOnlySharedMemoryRegion instance mapped to a | |
| // base::WritableSharedMemoryMapping, which is the only way to modify the | |
| // content of the newly created region. | |
| auto shmem = base::ReadOnlySharedMemoryRegion::Create(data.size()); | |
| if (!shmem.IsValid()) | |
| return base::ScopedFD(); | |
| memcpy(shmem.mapping.GetMemoryAs<uint8_t>(), data.data(), data.size()); | |
| base::subtle::PlatformSharedMemoryRegion platform_shm = | |
| base::ReadOnlySharedMemoryRegion::TakeHandleForSerialization( | |
| std::move(shmem.region)); | |
| if (!platform_shm.IsValid()) | |
| return base::ScopedFD(); | |
| return base::ScopedFD(std::move(platform_shm.PassPlatformHandle().fd)); | |
| } | |
| bool SharedMemoryUtil::ReadDataFromSharedMemory( | |
| const base::ScopedFD& in_data_fd, | |
| size_t data_size, | |
| std::vector<uint8_t>* out_data) { | |
| base::ScopedFD dup_in_data_fd(dup(in_data_fd.get())); | |
| base::ReadOnlySharedMemoryRegion shm_region = | |
| base::ReadOnlySharedMemoryRegion::Deserialize( | |
| base::subtle::PlatformSharedMemoryRegion::Take( | |
| std::move(dup_in_data_fd), | |
| base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly, | |
| data_size, base::UnguessableToken::Create())); | |
| if (!shm_region.IsValid()) | |
| return false; | |
| base::ReadOnlySharedMemoryMapping shm_mapping = shm_region.Map(); | |
| if (!shm_mapping.IsValid()) | |
| return false; | |
| out_data->assign(shm_mapping.GetMemoryAs<uint8_t>(), | |
| shm_mapping.GetMemoryAs<uint8_t>() + data_size); | |
| return true; | |
| } | |
| base::ScopedFD WriteSizeAndDataToPipe(const std::vector<uint8_t>& data) { | |
| size_t data_size = data.size(); | |
| CHECK_LE(data_size, kPipeSecretSizeLimit); | |
| int fds[2]; | |
| base::CreateLocalNonBlockingPipe(fds); | |
| base::ScopedFD read_dbus_fd(fds[0]); | |
| base::ScopedFD write_scoped_fd(fds[1]); | |
| base::WriteFileDescriptor(write_scoped_fd.get(), | |
| base::as_bytes(base::make_span(&data_size, 1u))); | |
| base::WriteFileDescriptor(write_scoped_fd.get(), data); | |
| return read_dbus_fd; | |
| } | |
| bool SaveSecretFromPipe(password_provider::PasswordProviderInterface* provider, | |
| const base::ScopedFD& in_secret_fd) { | |
| size_t data_size = 0; | |
| if (!GetSecretDataSizeFromPipe(in_secret_fd.get(), &data_size)) | |
| return false; | |
| auto secret = password_provider::Password::CreateFromFileDescriptor( | |
| in_secret_fd.get(), data_size); | |
| if (!secret) { | |
| LOG(ERROR) << "Could not create secret from file descriptor."; | |
| return false; | |
| } | |
| if (!provider->SavePassword(*secret)) { | |
| LOG(ERROR) << "Could not save secret."; | |
| return false; | |
| } | |
| return true; | |
| } | |
| base::FilePath StringToSafeFilename(std::string data) { | |
| std::string sha256hash = crypto::SHA256HashString(data); | |
| return base::FilePath(base::HexEncode(sha256hash.data(), sha256hash.size())); | |
| } | |
| } // namespace secret_util | |
| } // namespace login_manager |