1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <brillo/blkdev_utils/device_mapper.h>
6
7 #include <libdevmapper.h>
8 #include <algorithm>
9 #include <utility>
10
11 #include <base/files/file_util.h>
12 #include <base/strings/string_number_conversions.h>
13 #include <base/strings/string_tokenizer.h>
14 #include <base/strings/stringprintf.h>
15 #include <brillo/blkdev_utils/device_mapper_task.h>
16 #include <brillo/secure_blob.h>
17
18 namespace brillo {
19
20 // Use a tokenizer to parse string data stored in SecureBlob.
21 // The tokenizer does not store internal state so it should be
22 // okay to use with SecureBlobs.
23 // DO NOT USE .toker() as that leaks contents of the SecureBlob.
24 using SecureBlobTokenizer =
25 base::StringTokenizerT<std::string, SecureBlob::const_iterator>;
26
DevmapperTable(uint64_t start,uint64_t size,const std::string & type,const SecureBlob & parameters)27 DevmapperTable::DevmapperTable(uint64_t start,
28 uint64_t size,
29 const std::string& type,
30 const SecureBlob& parameters)
31 : start_(start), size_(size), type_(type), parameters_(parameters) {}
32
ToSecureBlob()33 SecureBlob DevmapperTable::ToSecureBlob() {
34 SecureBlob table_blob(base::StringPrintf("%" PRIu64 " %" PRIu64 " %s ",
35 start_, size_, type_.c_str()));
36
37 return SecureBlob::Combine(table_blob, parameters_);
38 }
39
CreateTableFromSecureBlob(const SecureBlob & table)40 DevmapperTable DevmapperTable::CreateTableFromSecureBlob(
41 const SecureBlob& table) {
42 uint64_t start, size;
43 std::string type;
44 DevmapperTable invalid_table(0, 0, "", SecureBlob());
45
46 SecureBlobTokenizer tokenizer(table.begin(), table.end(), " ");
47
48 // First parameter is start.
49 if (!tokenizer.GetNext() ||
50 !base::StringToUint64(
51 std::string(tokenizer.token_begin(), tokenizer.token_end()), &start))
52 return invalid_table;
53
54 // Second parameter is size of the dm device.
55 if (!tokenizer.GetNext() ||
56 !base::StringToUint64(
57 std::string(tokenizer.token_begin(), tokenizer.token_end()), &size))
58 return invalid_table;
59
60 // Third parameter is type of dm device.
61 if (!tokenizer.GetNext())
62 return invalid_table;
63
64 type = std::string(tokenizer.token_begin(), tokenizer.token_end());
65
66 // The remaining string is the parameters.
67 if (!tokenizer.GetNext())
68 return invalid_table;
69
70 // The remaining part is the parameters passed to the device.
71 SecureBlob target = SecureBlob(tokenizer.token_begin(), table.end());
72
73 return DevmapperTable(start, size, type, target);
74 }
75
CryptGetKey()76 SecureBlob DevmapperTable::CryptGetKey() {
77 SecureBlobTokenizer tokenizer(parameters_.begin(), parameters_.end(), " ");
78
79 // First field is the cipher.
80 if (!tokenizer.GetNext())
81 return SecureBlob();
82
83 // The key is stored in the second field.
84 if (!tokenizer.GetNext())
85 return SecureBlob();
86
87 SecureBlob hex_key(tokenizer.token_begin(), tokenizer.token_end());
88
89 SecureBlob key = SecureHexToSecureBlob(hex_key);
90
91 if (key.empty()) {
92 LOG(ERROR) << "CryptExtractKey: HexStringToBytes failed";
93 return SecureBlob();
94 }
95
96 return key;
97 }
98
99 // In order to not leak the encryption key to non-SecureBlob managed memory,
100 // create the parameter blobs in three parts and combine.
CryptCreateParameters(const std::string & cipher,const SecureBlob & encryption_key,const int iv_offset,const base::FilePath & device,int device_offset,bool allow_discard)101 SecureBlob DevmapperTable::CryptCreateParameters(
102 const std::string& cipher,
103 const SecureBlob& encryption_key,
104 const int iv_offset,
105 const base::FilePath& device,
106 int device_offset,
107 bool allow_discard) {
108 // First field is the cipher.
109 SecureBlob parameter_parts[3];
110
111 parameter_parts[0] = SecureBlob(cipher + " ");
112 parameter_parts[1] = SecureBlobToSecureHex(encryption_key);
113 parameter_parts[2] = SecureBlob(base::StringPrintf(
114 " %d %s %d%s", iv_offset, device.value().c_str(), device_offset,
115 (allow_discard ? " 1 allow_discards" : "")));
116
117 SecureBlob parameters;
118 for (auto param_part : parameter_parts)
119 parameters = SecureBlob::Combine(parameters, param_part);
120
121 return parameters;
122 }
123
CreateDevmapperTask(int type)124 std::unique_ptr<DevmapperTask> CreateDevmapperTask(int type) {
125 return std::make_unique<DevmapperTaskImpl>(type);
126 }
127
DeviceMapper()128 DeviceMapper::DeviceMapper() {
129 dm_task_factory_ = base::Bind(&CreateDevmapperTask);
130 }
131
DeviceMapper(const DevmapperTaskFactory & factory)132 DeviceMapper::DeviceMapper(const DevmapperTaskFactory& factory)
133 : dm_task_factory_(factory) {}
134
Setup(const std::string & name,const DevmapperTable & table)135 bool DeviceMapper::Setup(const std::string& name, const DevmapperTable& table) {
136 auto task = dm_task_factory_.Run(DM_DEVICE_CREATE);
137
138 if (!task->SetName(name)) {
139 LOG(ERROR) << "Setup: SetName failed.";
140 return false;
141 }
142
143 if (!task->AddTarget(table.GetStart(), table.GetSize(), table.GetType(),
144 table.GetParameters())) {
145 LOG(ERROR) << "Setup: AddTarget failed";
146 return false;
147 }
148
149 if (!task->Run(true /* udev sync */)) {
150 LOG(ERROR) << "Setup: Run failed.";
151 return false;
152 }
153
154 return true;
155 }
156
Remove(const std::string & name)157 bool DeviceMapper::Remove(const std::string& name) {
158 auto task = dm_task_factory_.Run(DM_DEVICE_REMOVE);
159
160 if (!task->SetName(name)) {
161 LOG(ERROR) << "Remove: SetName failed.";
162 return false;
163 }
164
165 if (!task->Run(true /* udev_sync */)) {
166 LOG(ERROR) << "Remove: Teardown failed.";
167 return false;
168 }
169
170 return true;
171 }
172
GetTable(const std::string & name)173 DevmapperTable DeviceMapper::GetTable(const std::string& name) {
174 auto task = dm_task_factory_.Run(DM_DEVICE_TABLE);
175 uint64_t start, size;
176 std::string type;
177 SecureBlob parameters;
178
179 if (!task->SetName(name)) {
180 LOG(ERROR) << "GetTable: SetName failed.";
181 return DevmapperTable(0, 0, "", SecureBlob());
182 }
183
184 if (!task->Run()) {
185 LOG(ERROR) << "GetTable: Run failed.";
186 return DevmapperTable(0, 0, "", SecureBlob());
187 }
188
189 task->GetNextTarget(&start, &size, &type, ¶meters);
190
191 return DevmapperTable(start, size, type, parameters);
192 }
193
WipeTable(const std::string & name)194 bool DeviceMapper::WipeTable(const std::string& name) {
195 auto size_task = dm_task_factory_.Run(DM_DEVICE_TABLE);
196
197 if (!size_task->SetName(name)) {
198 LOG(ERROR) << "WipeTable: SetName failed.";
199 return false;
200 }
201
202 if (!size_task->Run()) {
203 LOG(ERROR) << "WipeTable: RunTask failed.";
204 return false;
205 }
206
207 // Arguments for fetching dm target.
208 bool ret = false;
209 uint64_t start = 0, size = 0, total_size = 0;
210 std::string type;
211 SecureBlob parameters;
212
213 // Get maximum size of the device to be wiped.
214 do {
215 ret = size_task->GetNextTarget(&start, &size, &type, ¶meters);
216 total_size = std::max(start + size, total_size);
217 } while (ret);
218
219 // Setup wipe task.
220 auto wipe_task = dm_task_factory_.Run(DM_DEVICE_RELOAD);
221
222 if (!wipe_task->SetName(name)) {
223 LOG(ERROR) << "WipeTable: SetName failed.";
224 return false;
225 }
226
227 if (!wipe_task->AddTarget(0, total_size, "error", SecureBlob())) {
228 LOG(ERROR) << "WipeTable: AddTarget failed.";
229 return false;
230 }
231
232 if (!wipe_task->Run()) {
233 LOG(ERROR) << "WipeTable: RunTask failed.";
234 return false;
235 }
236
237 return true;
238 }
239
240 } // namespace brillo
241