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, &parameters);
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, &parameters);
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