blob: 96b5c22db421232edbbb15d0e182655d49e3a18f [file] [log] [blame]
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// 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 <utility>
#include <base/file_descriptor_posix.h>
#include <base/files/file_util.h>
#include <base/files/scoped_file.h>
#include <base/memory/shared_memory_handle.h>
#include <base/memory/shared_memory.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);
base::SharedMemory shared_memory;
base::SharedMemoryCreateOptions options;
options.size = data.size();
options.share_read_only = true;
if (!shared_memory.Create(options) || !shared_memory.Map(data.size()))
return base::ScopedFD();
memcpy(shared_memory.memory(), data.data(), data.size());
base::SharedMemoryHandle read_only_handle = shared_memory.GetReadOnlyHandle();
if (!read_only_handle.IsValid())
return base::ScopedFD();
return base::ScopedFD(read_only_handle.Release());
}
bool SharedMemoryUtil::ReadDataFromSharedMemory(
const base::ScopedFD& in_data_fd,
size_t data_size,
std::vector<uint8_t>* out_data) {
// |shared_memory.TakeHandle()| must be called before leaving this function,
// otherwise the file descriptor will be closed twice (once here and once by
// session manager).
base::SharedMemory shared_memory(
base::SharedMemoryHandle::ImportHandle(in_data_fd.get(), data_size),
/*read_only=*/true);
if (!shared_memory.Map(data_size)) {
shared_memory.TakeHandle();
return false;
}
out_data->assign(
reinterpret_cast<uint8_t*>(shared_memory.memory()),
reinterpret_cast<uint8_t*>(shared_memory.memory()) + data_size);
shared_memory.Unmap();
shared_memory.TakeHandle();
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(),
reinterpret_cast<const char*>(&data_size),
sizeof(size_t));
base::WriteFileDescriptor(write_scoped_fd.get(),
reinterpret_cast<const char*>(data.data()),
data_size);
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