2 * Copyright (c) 2016 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
18 #include <linux/dm-ioctl.h>
19 #include <sys/mount.h>
23 #include <klay/audit/logger.h>
25 #include <klay/error.h>
26 #include <klay/exception.h>
27 #include <klay/filesystem.h>
29 #include "../../file-footer.h"
30 #include "../../ext4-tool.h"
32 #include "dmcrypt-engine.h"
34 #define DM_MAX_BUFFER_SIZE 4096
35 #define DM_KEY_MIN_LEN_BYTE 32
36 #define DM_DEFAULT_LABEL_NAME "userdata"
37 #define DM_DEFAULT_CRYPTO_NAME "aes-cbc-essiv:sha256"
39 #define OPTION_INCLUDE_UNUSED_REGION (1 << 0)
41 using namespace std::placeholders;
47 size_t getFileSystemSize(const std::string &src)
49 int fd = open(src.c_str(), O_RDONLY);
54 if ((ioctl(fd, BLKGETSIZE, &size)) == -1)
61 const std::string convertToHex(const DMCryptEngine::data &binary)
63 std::stringstream hex;
65 hex << std::hex << std::setfill('0');
66 for (unsigned int byte : binary) {
67 hex << std::setw(2) << byte;
72 void initDMIoctl(char *buf, size_t size, const std::string &name, unsigned flags)
74 struct dm_ioctl *io = (struct dm_ioctl *)buf;
76 ::memset(io, 0, size);
78 io->data_start = sizeof(struct dm_ioctl);
83 ::memset(io->name, 0, sizeof(io->name));
84 ::strncpy(io->name, name.c_str(), sizeof(io->name) - 1);
87 const std::string createCryptoBlkDev(const std::string &realBlkDev, const std::string &mountName, const DMCryptEngine::data &key, std::string cryptoTypeName)
89 size_t fileSystemSize = getFileSystemSize(realBlkDev);
90 std::string cryptoBlkDev;
94 * dmBuf |-------------------------------------------------| DM_MAX_BUFFER_SIZE(4096)
96 char dmBuf[DM_MAX_BUFFER_SIZE]; // first: for dm_io, dm_ts
98 // Open dm control IOCTL
99 if ((fd = open("/dev/mapper/control", O_RDWR)) < 0) {
100 throw runtime::Exception("Cannot open device-mapper");
104 * dmBuf |-------------------------------------------------| DM_MAX_BUFFER_SIZE(4096)
105 * dmIo |----------| size of dm_ioctl
107 auto dmIo = (struct dm_ioctl *)dmBuf;
109 // Create Device (mount_name)
110 initDMIoctl(dmBuf, DM_MAX_BUFFER_SIZE, mountName, 0);
111 if (ioctl(fd, DM_DEV_CREATE, dmBuf)) {
113 throw runtime::Exception("Cannot create dm-crypt device");
116 // Get the device status, in particular, the mount_name of it's device file
117 initDMIoctl(dmBuf, DM_MAX_BUFFER_SIZE, mountName, 0);
118 if (ioctl(fd, DM_DEV_STATUS, dmBuf)) {
120 throw runtime::Exception("Cannot retrieve dm-crypt device status");
123 // Store created device into crypto_blkdev
124 unsigned int dmMinor = (dmIo->dev & 0xff) | ((dmIo->dev >> 12) & 0xfff00);
125 cryptoBlkDev = "/dev/dm-" + std::to_string(dmMinor);
128 * dmBuf |-------------------------------------------------| DM_MAX_BUFFER_SIZE(4096)
129 * dmIo |----------| size of dm_ioctl
130 * dmTs |--------------| size of dm_Target_spec
132 auto dmTs = (struct dm_target_spec *)(dmBuf + sizeof(struct dm_ioctl));
134 // Load the mapping table for this device
136 // Force clean-up whole dm_buffer
137 initDMIoctl(dmBuf, DM_MAX_BUFFER_SIZE, mountName, 0);
138 dmIo->target_count = 1;
140 dmTs->sector_start = 0;
141 dmTs->length = fileSystemSize;
142 ::memset(dmTs->target_type, 0, sizeof(dmTs->target_type));
143 ::strncpy(dmTs->target_type, "crypt", sizeof(dmTs->target_type) - 1);
146 * dmBuf |-------------------------------------------------| DM_MAX_BUFFER_SIZE(4096)
147 * dmIo |----------| size of dm_ioctl
148 * dmTs |--------------| size of dm_Target_spec
149 * cryptParams |---------------------|
151 char *cryptParams = dmBuf + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
154 snprintf(cryptParams, DM_MAX_BUFFER_SIZE - (cryptParams - dmBuf), "%s %s 0 %s 0", cryptoTypeName.c_str(), convertToHex(key).c_str(), realBlkDev.c_str());
156 cryptParams += strlen(cryptParams) + 1;
157 // Align to an 8 byte boundary
158 cryptParams = (char *)(((unsigned long)cryptParams + 7) & ~8);
159 dmTs->next = cryptParams - dmBuf;
162 if (ioctl(fd, DM_TABLE_LOAD, dmBuf) < 0) {
164 throw runtime::Exception("Cannot load dm-crypt mapping table.");
167 // Resume this device to activate it
168 initDMIoctl(dmBuf, DM_MAX_BUFFER_SIZE, mountName, 0);
169 if (ioctl(fd, DM_DEV_SUSPEND, dmBuf)) {
171 throw runtime::Exception("Cannot resume the dm-crypt device");
179 void destroyCryptoBlkDev(const std::string &cryptoBlkDev)
181 char buf[DM_MAX_BUFFER_SIZE];
184 if ((fd = open("/dev/mapper/control", O_RDWR)) < 0)
185 throw runtime::Exception("Cannot open device-mapper");
187 initDMIoctl(buf, sizeof(buf), cryptoBlkDev, 0);
188 ret = ioctl(fd, DM_DEV_REMOVE, buf);
192 throw runtime::Exception("Cannot remove dm-crypt device");
196 ode::DMCryptEngine::data sanitizeKey(const ode::DMCryptEngine::data &key)
198 ode::DMCryptEngine::data sanitized(key);
199 sanitized.resize(DM_KEY_MIN_LEN_BYTE);
203 void copyInPlace(const std::string &source, const std::string &destination,
204 const std::function<bool(unsigned int)> &isTarget,
205 const std::function<void(int, int)> &addProgress)
207 Ext4Tool ext4tool(source);
208 const size_t srcBlockSize = ext4tool.getBlockSize();
209 const size_t srcTotalBlockCount = ext4tool.getTotalBlockCount();
210 char buf[srcBlockSize];
212 runtime::File src(source, O_RDONLY);
213 runtime::File dst(destination, O_WRONLY);
215 for (size_t n = 0, pos = 0; n < srcTotalBlockCount;
216 n++, pos += srcBlockSize) {
221 src.lseek(pos, SEEK_SET);
222 dst.lseek(pos, SEEK_SET);
224 src.read(buf, srcBlockSize);
225 dst.write(buf, srcBlockSize);
227 addProgress(n, srcTotalBlockCount);
233 DMCryptEngine::DMCryptEngine(const std::string &src, const std::string &dest, const ProgressBar &prgsBar) :
234 source(src), destination(dest), progress(prgsBar)
238 DMCryptEngine::~DMCryptEngine()
242 void DMCryptEngine::mount(const DMCryptEngine::data &key, unsigned int options)
244 // create crypto type device mapping layer to mount the encrypted partition.
245 auto cryptoBlkDev = createCryptoBlkDev(source, DM_DEFAULT_LABEL_NAME, sanitizeKey(key), DM_DEFAULT_CRYPTO_NAME);
247 if (::mount(cryptoBlkDev.c_str(), destination.c_str(), "ext4", 0, 0) < 0)
248 throw runtime::Exception(runtime::GetSystemErrorMessage());
253 void DMCryptEngine::umount()
255 if (::umount(destination.c_str()))
256 throw runtime::Exception(runtime::GetSystemErrorMessage());
258 destroyCryptoBlkDev(DM_DEFAULT_LABEL_NAME);
263 bool DMCryptEngine::isMounted()
268 void DMCryptEngine::encrypt(const DMCryptEngine::data &key, unsigned int options)
270 // Force filesystem check via fcsf might be able to avoid fail situation during encryption.
271 Ext4Tool ext4Source(source);
272 ext4Source.forceCleanUp();
274 // create crypto type device mapping layer to mount the plain partition
275 // should be encrypted here.
276 auto cryptoBlkDev = createCryptoBlkDev(source, DM_DEFAULT_LABEL_NAME, sanitizeKey(key), DM_DEFAULT_CRYPTO_NAME);
278 std::function<bool(unsigned int)> isTarget;
279 if (!(options & OPTION_INCLUDE_UNUSED_REGION)) {
280 INFO("FastEncryption: Disabled");
281 isTarget = std::bind(&Ext4Tool::isUsedBlock, &ext4Source, _1);
283 INFO("FastEncryption: Enabled");
284 isTarget = [](unsigned int n) {
289 // We always do In-place encryption
290 copyInPlace(source, cryptoBlkDev, isTarget,
291 std::bind((void(ProgressBar::*)(int, int, int))&ProgressBar::update,
292 &progress, _1, _2, 1));
294 // remove crypto type device mapper
295 destroyCryptoBlkDev(DM_DEFAULT_LABEL_NAME);
301 void DMCryptEngine::decrypt(const DMCryptEngine::data &key, unsigned int options)
303 // create crypto type device mapping layer to mount the plain partition
304 // should be encrypted here.
305 auto cryptoBlkDev = createCryptoBlkDev(source, DM_DEFAULT_LABEL_NAME, sanitizeKey(key), DM_DEFAULT_CRYPTO_NAME);
307 // Force filesystem check via fcsf might be able to avoid fail situation during decryption.
308 Ext4Tool ext4CryptoBlkDev(cryptoBlkDev);
309 ext4CryptoBlkDev.forceCleanUp();
311 std::function<bool(unsigned int)> isTarget;
312 if (!(options & OPTION_INCLUDE_UNUSED_REGION)) {
313 INFO("FastEncryption: Disabled");
314 isTarget = std::bind(&Ext4Tool::isUsedBlock, &ext4CryptoBlkDev, _1);
316 INFO("FastEncryption: Enabled");
317 isTarget = [](unsigned int n) {
322 // We always do In-place decryption
323 copyInPlace(cryptoBlkDev, source, isTarget,
324 std::bind((void(ProgressBar::*)(int, int, int))&ProgressBar::update,
325 &progress, _1, _2, 1));
327 // remove crypto type device mapper
328 destroyCryptoBlkDev(DM_DEFAULT_LABEL_NAME);
334 bool DMCryptEngine::isKeyMetaSet()
336 return FileFooter::exist(source);
339 const DMCryptEngine::data DMCryptEngine::getKeyMeta()
341 return FileFooter::read(source);
344 void DMCryptEngine::setKeyMeta(const data &meta)
346 FileFooter::write(source, meta);
349 void DMCryptEngine::clearKeyMeta()
351 FileFooter::clear(source);
354 unsigned int DMCryptEngine::getSupportedOptions()
356 return OPTION_INCLUDE_UNUSED_REGION;