2 * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
17 #include <linux/dm-ioctl.h>
18 #include <sys/mount.h>
19 #include <sys/types.h>
33 #include <klay/error.h>
34 #include <klay/exception.h>
35 #include <klay/audit/dlog-sink.h>
37 #include <upgrade-support.h>
38 #include "progress-bar.h"
40 #define DM_MAX_BUFFER_SIZE 4096
41 #define DM_KEY_MIN_LEN_BYTE 32
42 #define DM_DEFAULT_LABEL_NAME "userdata"
43 #define DM_DEFAULT_CRYPTO_NAME "aes-cbc-essiv:sha256"
45 audit::LogSink *SINK = nullptr;
49 const char *INTERNAL_PATH = "/opt/usr";
51 struct AbstractDevice {
53 virtual ~AbstractDevice() {}
54 virtual std::string findNode(const std::string &name) = 0;
57 struct MmcDevice : public AbstractDevice {
59 MmcDevice(int id) : device("/dev/mmcblk" + std::to_string(id)), deviceHandle(-1)
61 deviceHandle = ::open(device.c_str(), O_RDONLY);
62 if (deviceHandle == -1)
63 throw std::runtime_error("Invalid device: " + device);
68 if (deviceHandle != -1)
69 ::close(deviceHandle);
72 virtual std::string findNode(const std::string &name)
74 blkid_partlist partlist;
78 probe = ::blkid_new_probe();
80 throw std::runtime_error("Failed to call blkid_new_probe");
83 if (::blkid_probe_set_device(probe, deviceHandle, 0, 0) < 0) {
84 ::blkid_free_probe(probe);
85 throw std::runtime_error("Failed to set prove device: " + device);
88 partlist = ::blkid_probe_get_partitions(probe);
90 ::blkid_free_probe(probe);
91 throw std::runtime_error("Failed to get partition list in device: " + device);
94 int num = ::blkid_partlist_numof_partitions(partlist);
95 for (int i = 1; i <= num; i++) {
96 blkid_partition par = ::blkid_partlist_get_partition(partlist, i);
98 const char *n = ::blkid_partition_get_name(par);
102 if (::strcasecmp(n, name.c_str()) == 0) {
103 partno = ::blkid_partition_get_partno(par);
108 ::blkid_free_probe(probe);
111 throw std::runtime_error("Failed to get partition number with " + name);
114 return device + "p" + std::to_string(partno);
122 // dummy implementation
123 ProgressBar::ProgressBar(UpdateFunc const&) : updateValue(0) {}
124 ProgressBar::~ProgressBar() {}
126 void ProgressBar::update(unsigned) {}
127 void ProgressBar::done(void) {}
129 //Hot fix for urgent issue - start, TODO : It should be removed.
130 unsigned long getBlockCount(const std::string &src)
132 int fd = open(src.c_str(), O_RDONLY);
136 unsigned long size = 0;
137 if ((ioctl(fd, BLKGETSIZE, &size)) == -1)
144 const std::string convertToHex(const BinaryData &binary)
146 std::stringstream hex;
148 hex << std::hex << std::setfill('0');
149 for (unsigned int byte : binary) {
150 hex << std::setw(2) << byte;
155 void initDMIoctl(char *buf, size_t size, const std::string &name, unsigned flags)
157 struct dm_ioctl *io = (struct dm_ioctl *)buf;
159 ::memset(io, 0, size);
160 io->data_size = size;
161 io->data_start = sizeof(struct dm_ioctl);
166 ::memset(io->name, 0, sizeof(io->name));
167 ::strncpy(io->name, name.c_str(), sizeof(io->name) - 1);
170 BinaryData sanitizeKey(const BinaryData &key)
172 if (key.size() < DM_KEY_MIN_LEN_BYTE)
173 throw runtime::Exception("Size of key smaller than minimum 32B");
174 BinaryData sanitized(key);
175 sanitized.resize(DM_KEY_MIN_LEN_BYTE);
179 const std::string createCryptoBlkDev(const std::string &realBlkDev,
180 const std::string &mountName,
181 const BinaryData &key,
182 std::string cryptoTypeName)
184 auto blockCount = getBlockCount(realBlkDev);
185 std::string cryptoBlkDev;
189 * dmBuf |-------------------------------------------------| DM_MAX_BUFFER_SIZE(4096)
191 char dmBuf[DM_MAX_BUFFER_SIZE]; // first: for dm_io, dm_ts
193 // Open dm control IOCTL
194 if ((fd = open("/dev/mapper/control", O_RDWR)) < 0) {
195 throw runtime::Exception("Cannot open device-mapper");
198 std::unique_ptr<int, void(*)(int*)> fdPtr(&fd, [](int* fd){ if (fd) ::close(*fd); });
201 * dmBuf |-------------------------------------------------| DM_MAX_BUFFER_SIZE(4096)
202 * dmIo |----------| size of dm_ioctl
204 auto dmIo = (struct dm_ioctl *)dmBuf;
206 // Create Device (mount_name)
207 initDMIoctl(dmBuf, DM_MAX_BUFFER_SIZE, mountName, 0);
208 if (ioctl(fd, DM_DEV_CREATE, dmBuf) && errno != EBUSY) {
209 throw runtime::Exception("Cannot create dm-crypt device");
212 // Get the device status, in particular, the mount_name of it's device file
213 initDMIoctl(dmBuf, DM_MAX_BUFFER_SIZE, mountName, 0);
214 if (ioctl(fd, DM_DEV_STATUS, dmBuf)) {
215 throw runtime::Exception("Cannot retrieve dm-crypt device status");
218 // Store created device into crypto_blkdev
219 unsigned int dmMinor = (dmIo->dev & 0xff) | ((dmIo->dev >> 12) & 0xfff00);
220 cryptoBlkDev = "/dev/dm-" + std::to_string(dmMinor);
223 * dmBuf |-------------------------------------------------| DM_MAX_BUFFER_SIZE(4096)
224 * dmIo |----------| size of dm_ioctl
225 * dmTs |--------------| size of dm_target_spec
227 auto dmTs = (struct dm_target_spec *)(dmBuf + sizeof(struct dm_ioctl));
229 // Load the mapping table for this device
231 // Force clean-up whole dm_buffer
232 initDMIoctl(dmBuf, DM_MAX_BUFFER_SIZE, mountName, 0);
233 dmIo->target_count = 1;
235 dmTs->sector_start = 0;
236 dmTs->length = blockCount;
237 ::memset(dmTs->target_type, 0, sizeof(dmTs->target_type));
238 ::strncpy(dmTs->target_type, "crypt", sizeof(dmTs->target_type) - 1);
241 * dmBuf |-------------------------------------------------| DM_MAX_BUFFER_SIZE(4096)
242 * dmIo |----------| size of dm_ioctl
243 * dmTs |--------------| size of dm_target_spec
244 * cryptParams |---------------------|
246 char *cryptParams = dmBuf + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
249 size_t cryptParamsSize = DM_MAX_BUFFER_SIZE - (cryptParams - dmBuf);
250 std::string keyHex = convertToHex(key);
251 int ret = snprintf(cryptParams,
254 cryptoTypeName.c_str(),
258 throw runtime::Exception("snprintf() failed");
260 if (static_cast<size_t>(ret) >= cryptParamsSize) {
261 throw runtime::Exception("Crypto params didn't fit the device mapper buffer");
264 cryptParams += strlen(cryptParams) + 1;
265 // Align to an 8 byte boundary
266 cryptParams = (char *)(((unsigned long)cryptParams + 7) & ~8);
267 dmTs->next = cryptParams - dmBuf;
270 if (ioctl(fd, DM_TABLE_LOAD, dmBuf) < 0) {
271 throw runtime::Exception("Cannot load dm-crypt mapping table.");
274 // Resume this device to activate it
275 initDMIoctl(dmBuf, DM_MAX_BUFFER_SIZE, mountName, 0);
276 if (ioctl(fd, DM_DEV_SUSPEND, dmBuf)) {
277 throw runtime::Exception("Cannot resume the dm-crypt device");
283 void destroyCryptoBlkDev(const std::string &cryptoBlkDev)
285 char buf[DM_MAX_BUFFER_SIZE];
288 if ((fd = open("/dev/mapper/control", O_RDWR)) < 0)
289 throw runtime::Exception("Cannot open device-mapper");
291 initDMIoctl(buf, sizeof(buf), cryptoBlkDev, 0);
292 ret = ioctl(fd, DM_DEV_REMOVE, buf);
296 if (ret != 0 && err != ENXIO) {
297 throw runtime::Exception("Cannot remove dm-crypt device");
301 void DMCryptMount(const std::string &source, const std::string &destination, const BinaryData &key, unsigned int options)
303 // create crypto type device mapping layer to mount the encrypted partition.
304 auto cryptoBlkDev = createCryptoBlkDev(source, DM_DEFAULT_LABEL_NAME, sanitizeKey(key), DM_DEFAULT_CRYPTO_NAME);
306 if (::mount(cryptoBlkDev.c_str(), destination.c_str(), "ext4", 0, 0) < 0)
307 throw runtime::Exception(runtime::GetSystemErrorMessage());
310 void DMCryptUmount(const std::string &destination)
312 if (::umount(destination.c_str()) && errno != EINVAL && errno != ENOENT)
313 throw runtime::Exception(runtime::GetSystemErrorMessage());
315 destroyCryptoBlkDev(DM_DEFAULT_LABEL_NAME);
323 const std::string MOUNT = "mount";
324 const std::string UMOUNT = "umount";
325 const std::string REMOVE = "remove";
330 "Usage: ode-fota [Operation]" << std::endl <<
332 "Operations :" << std::endl <<
333 " mount [path] Mount internal memory using stored master key" << std::endl <<
334 " umount [path] Unmount internal memory" << std::endl <<
335 " remove Remove stored internal memory master key" << std::endl;
338 } // anonymous namespace
340 int main(int argc, char* argv[])
345 if (argc < 2 || argc > 3) {
351 std::string devpath = dev.findNode("user");
353 if (MOUNT == argv[1]) {
354 auto masterKey = UpgradeSupport::loadMasterKey(devpath);
355 std::string path = INTERNAL_PATH;
359 // mount options are ignored by mount()
360 DMCryptMount(devpath, path, masterKey, 0);
361 UpgradeSupport::createUpgradeFlag();
362 } else if (UMOUNT == argv[1]) {
363 std::string path = INTERNAL_PATH;
368 } else if (REMOVE == argv[1]) {
369 UpgradeSupport::removeMasterKey(devpath);
375 } catch (const std::exception& e) {
376 std::cerr << e.what() << std::endl;
378 std::cerr << "Unknown exception occured" << std::endl;