// 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 #include #include #include #include #include #include #include #include #include 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; 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 CreateDevmapperTask(int type) { return std::make_unique(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