| // Copyright 2018 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 <brillo/blkdev_utils/device_mapper.h> |
| |
| #include <libdevmapper.h> |
| #include <algorithm> |
| #include <utility> |
| |
| #include <base/files/file_util.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/strings/string_tokenizer.h> |
| #include <base/strings/stringprintf.h> |
| #include <brillo/blkdev_utils/device_mapper_task.h> |
| #include <brillo/secure_blob.h> |
| |
| namespace brillo { |
| |
| // Use a tokenizer to parse string data stored in SecureBlob. |
| // The tokenizer does not store internal state so it should be |
| // okay to use with SecureBlobs. |
| // DO NOT USE .toker() as that leaks contents of the SecureBlob. |
| using SecureBlobTokenizer = |
| base::StringTokenizerT<std::string, SecureBlob::const_iterator>; |
| |
| DevmapperTable::DevmapperTable(uint64_t start, |
| uint64_t size, |
| const std::string& type, |
| const SecureBlob& parameters) |
| : start_(start), size_(size), type_(type), parameters_(parameters) {} |
| |
| SecureBlob DevmapperTable::ToSecureBlob() { |
| SecureBlob table_blob(base::StringPrintf("%" PRIu64 " %" PRIu64 " %s ", |
| start_, size_, type_.c_str())); |
| |
| return SecureBlob::Combine(table_blob, parameters_); |
| } |
| |
| DevmapperTable DevmapperTable::CreateTableFromSecureBlob( |
| const SecureBlob& table) { |
| uint64_t start, size; |
| std::string type; |
| DevmapperTable invalid_table(0, 0, "", SecureBlob()); |
| |
| SecureBlobTokenizer tokenizer(table.begin(), table.end(), " "); |
| |
| // First parameter is start. |
| if (!tokenizer.GetNext() || |
| !base::StringToUint64( |
| std::string(tokenizer.token_begin(), tokenizer.token_end()), &start)) |
| return invalid_table; |
| |
| // Second parameter is size of the dm device. |
| if (!tokenizer.GetNext() || |
| !base::StringToUint64( |
| std::string(tokenizer.token_begin(), tokenizer.token_end()), &size)) |
| return invalid_table; |
| |
| // Third parameter is type of dm device. |
| if (!tokenizer.GetNext()) |
| return invalid_table; |
| |
| type = std::string(tokenizer.token_begin(), tokenizer.token_end()); |
| |
| // The remaining string is the parameters. |
| if (!tokenizer.GetNext()) |
| return invalid_table; |
| |
| // The remaining part is the parameters passed to the device. |
| SecureBlob target = SecureBlob(tokenizer.token_begin(), table.end()); |
| |
| return DevmapperTable(start, size, type, target); |
| } |
| |
| SecureBlob DevmapperTable::CryptGetKey() { |
| SecureBlobTokenizer tokenizer(parameters_.begin(), parameters_.end(), " "); |
| |
| // First field is the cipher. |
| if (!tokenizer.GetNext()) |
| return SecureBlob(); |
| |
| // The key is stored in the second field. |
| if (!tokenizer.GetNext()) |
| return SecureBlob(); |
| |
| SecureBlob hex_key(tokenizer.token_begin(), tokenizer.token_end()); |
| |
| SecureBlob key = SecureHexToSecureBlob(hex_key); |
| |
| if (key.empty()) { |
| LOG(ERROR) << "CryptExtractKey: HexStringToBytes failed"; |
| return SecureBlob(); |
| } |
| |
| return key; |
| } |
| |
| // In order to not leak the encryption key to non-SecureBlob managed memory, |
| // create the parameter blobs in three parts and combine. |
| SecureBlob DevmapperTable::CryptCreateParameters( |
| const std::string& cipher, |
| const SecureBlob& encryption_key, |
| const int iv_offset, |
| const base::FilePath& device, |
| int device_offset, |
| bool allow_discard) { |
| // First field is the cipher. |
| SecureBlob parameter_parts[3]; |
| |
| parameter_parts[0] = SecureBlob(cipher + " "); |
| parameter_parts[1] = SecureBlobToSecureHex(encryption_key); |
| parameter_parts[2] = SecureBlob(base::StringPrintf( |
| " %d %s %d%s", iv_offset, device.value().c_str(), device_offset, |
| (allow_discard ? " 1 allow_discards" : ""))); |
| |
| SecureBlob parameters; |
| for (auto param_part : parameter_parts) |
| parameters = SecureBlob::Combine(parameters, param_part); |
| |
| return parameters; |
| } |
| |
| std::unique_ptr<DevmapperTask> CreateDevmapperTask(int type) { |
| return std::make_unique<DevmapperTaskImpl>(type); |
| } |
| |
| DeviceMapper::DeviceMapper() { |
| dm_task_factory_ = base::Bind(&CreateDevmapperTask); |
| } |
| |
| DeviceMapper::DeviceMapper(const DevmapperTaskFactory& factory) |
| : dm_task_factory_(factory) {} |
| |
| bool DeviceMapper::Setup(const std::string& name, const DevmapperTable& table) { |
| auto task = dm_task_factory_.Run(DM_DEVICE_CREATE); |
| |
| if (!task->SetName(name)) { |
| LOG(ERROR) << "Setup: SetName failed."; |
| return false; |
| } |
| |
| if (!task->AddTarget(table.GetStart(), table.GetSize(), table.GetType(), |
| table.GetParameters())) { |
| LOG(ERROR) << "Setup: AddTarget failed"; |
| return false; |
| } |
| |
| if (!task->Run(true /* udev sync */)) { |
| LOG(ERROR) << "Setup: Run failed."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool DeviceMapper::Remove(const std::string& name) { |
| auto task = dm_task_factory_.Run(DM_DEVICE_REMOVE); |
| |
| if (!task->SetName(name)) { |
| LOG(ERROR) << "Remove: SetName failed."; |
| return false; |
| } |
| |
| if (!task->Run(true /* udev_sync */)) { |
| LOG(ERROR) << "Remove: Teardown failed."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| DevmapperTable DeviceMapper::GetTable(const std::string& name) { |
| auto task = dm_task_factory_.Run(DM_DEVICE_TABLE); |
| uint64_t start, size; |
| std::string type; |
| SecureBlob parameters; |
| |
| if (!task->SetName(name)) { |
| LOG(ERROR) << "GetTable: SetName failed."; |
| return DevmapperTable(0, 0, "", SecureBlob()); |
| } |
| |
| if (!task->Run()) { |
| LOG(ERROR) << "GetTable: Run failed."; |
| return DevmapperTable(0, 0, "", SecureBlob()); |
| } |
| |
| task->GetNextTarget(&start, &size, &type, ¶meters); |
| |
| return DevmapperTable(start, size, type, parameters); |
| } |
| |
| bool DeviceMapper::WipeTable(const std::string& name) { |
| auto size_task = dm_task_factory_.Run(DM_DEVICE_TABLE); |
| |
| if (!size_task->SetName(name)) { |
| LOG(ERROR) << "WipeTable: SetName failed."; |
| return false; |
| } |
| |
| if (!size_task->Run()) { |
| LOG(ERROR) << "WipeTable: RunTask failed."; |
| return false; |
| } |
| |
| // Arguments for fetching dm target. |
| bool ret = false; |
| uint64_t start = 0, size = 0, total_size = 0; |
| std::string type; |
| SecureBlob parameters; |
| |
| // Get maximum size of the device to be wiped. |
| do { |
| ret = size_task->GetNextTarget(&start, &size, &type, ¶meters); |
| total_size = std::max(start + size, total_size); |
| } while (ret); |
| |
| // Setup wipe task. |
| auto wipe_task = dm_task_factory_.Run(DM_DEVICE_RELOAD); |
| |
| if (!wipe_task->SetName(name)) { |
| LOG(ERROR) << "WipeTable: SetName failed."; |
| return false; |
| } |
| |
| if (!wipe_task->AddTarget(0, total_size, "error", SecureBlob())) { |
| LOG(ERROR) << "WipeTable: AddTarget failed."; |
| return false; |
| } |
| |
| if (!wipe_task->Run()) { |
| LOG(ERROR) << "WipeTable: RunTask failed."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace brillo |