2 * Copyright (c) 2015 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
23 #include <linux/keyctl.h>
24 #include <sys/mount.h>
25 #include <sys/xattr.h>
26 #include <sys/syscall.h>
27 #include <sys/ioctl.h>
29 #include <sys/sendfile.h>
31 #include <klay/filesystem.h>
32 #include <klay/audit/logger.h>
33 #include <klay/error.h>
34 #include <klay/exception.h>
36 #include "../../file-footer.h"
37 #include "../../key-manager/key-generator.h"
39 #include "ext4-engine.h"
43 #define EXT4_MAX_KEY_SIZE 64
44 #define EXT4_KEY_DESCRIPTOR_SIZE 8
45 #define EXT4_KEY_DESC_PREFIX_SIZE 5
46 #define EXT4_KEY_REF_STR_BUF_SIZE ((EXT4_KEY_DESCRIPTOR_SIZE * 2) + 1)
47 #define EXT4_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct ext4_encryption_policy)
48 #define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy)
50 /* special process keyring shortcut IDs */
51 #define KEY_SPEC_THREAD_KEYRING -1
52 #define KEY_SPEC_PROCESS_KEYRING -2
53 #define KEY_SPEC_SESSION_KEYRING -3
54 #define KEY_SPEC_USER_KEYRING -4
55 #define KEY_SPEC_USER_SESSION_KEYRING -5
56 #define KEY_SPEC_GROUP_KEYRING -6
58 #define KEYCTL_GET_KEYRING_ID 0
59 #define KEYCTL_SEARCH 10
61 /* Encryption algorithms */
62 #define EXT4_ENCRYPTION_MODE_INVALID 0
63 #define EXT4_ENCRYPTION_MODE_AES_256_XTS 1
64 #define EXT4_ENCRYPTION_MODE_AES_256_GCM 2
65 #define EXT4_ENCRYPTION_MODE_AES_256_CBC 3
66 #define EXT4_ENCRYPTION_MODE_AES_256_CTS 4
68 #define SMACK_LABEL_LEN_MAX 255
69 struct ext4_encryption_policy {
71 char contents_encryption_mode;
72 char filenames_encryption_mode;
74 char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
75 }__attribute__((__packed__));
77 struct ext4_encryption_key {
79 char raw[EXT4_MAX_KEY_SIZE];
81 } __attribute__((__packed__));
83 /* for now, It only suits for "/opt/usr" */
85 std::string secondMountPoint("/opt/usr_encrypt");
86 std::string bindMountPoint("/opt/usr_encrypt/secure");
87 std::string ext4KeyringType("logon");
88 std::string smackAccessLabel("security.SMACK64");
89 long long totalSize = 0;
90 long long curSize = 0;
93 const Ext4Engine::data generateKeyDesc(const ode::Ext4Engine::data& key)
95 Ext4Engine::data keyRef1 = ode::KeyGenerator::SHA512(key);
96 Ext4Engine::data keyRef2 = ode::KeyGenerator::SHA512(keyRef1);
97 Ext4Engine::data ret(keyRef2.begin(), keyRef2.begin()+EXT4_KEY_DESCRIPTOR_SIZE);
102 const Ext4Engine::data generateKeyRefStr(const Ext4Engine::data& key)
104 Ext4Engine::data ret(EXT4_KEY_REF_STR_BUF_SIZE);
105 Ext4Engine::data keyDesc = generateKeyDesc(key);
108 unsigned char nibble;
109 for (i=0, a=0; i < EXT4_KEY_DESCRIPTOR_SIZE; i++, a+=2) {
110 nibble = (keyDesc[i] >> 4) & 0xf;
111 if (nibble <= 9) ret[a] = '0' + nibble;
112 else ret[a] = 'a' + nibble - 10;
113 nibble = keyDesc[i] & 0xf;
114 if (nibble <= 9) ret[a+1] = '0' + nibble;
115 else ret[a+1] = 'a' + nibble - 10;
123 void addKeyToKeyring(const Ext4Engine::data& key)
125 struct ext4_encryption_key ext4Key;
126 Ext4Engine::data keyRefStr(EXT4_KEY_REF_STR_BUF_SIZE);
127 keyRefStr = generateKeyRefStr(key);
128 std::string keyRef(keyRefStr.begin(), keyRefStr.end()), keyRefFull;
131 keyringId = ::syscall(__NR_keyctl, KEYCTL_GET_KEYRING_ID, KEY_SPEC_SESSION_KEYRING, 0);
133 throw runtime::Exception(runtime::GetSystemErrorMessage());
135 keyRefFull = "ext4:" + keyRef;
136 ret = ::syscall(__NR_keyctl, KEYCTL_SEARCH, keyringId, ext4KeyringType.c_str(), keyRefFull.c_str(), 0);
138 INFO("Key with descriptor already exist");
141 ext4Key.mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
142 ::memcpy(ext4Key.raw, key.data(), EXT4_MAX_KEY_SIZE);
143 std::string ext4KeyRaw = ext4Key.raw;
144 ext4Key.size = EXT4_MAX_KEY_SIZE;
146 ret = ::syscall(__NR_add_key, ext4KeyringType.c_str(), keyRefFull.c_str(),
147 (void *)&ext4Key, sizeof(ext4Key), keyringId);
150 throw runtime::Exception(runtime::GetSystemErrorMessage());
154 static void copySmackLabel(std::string& srcPath, std::string& destPath)
156 Ext4Engine::data smackLabel(SMACK_LABEL_LEN_MAX + 1);
158 if (::getxattr(srcPath.c_str(), smackAccessLabel.c_str(), (unsigned char*)smackLabel.data(), SMACK_LABEL_LEN_MAX + 1) == -1)
159 throw runtime::Exception(runtime::GetSystemErrorMessage());
161 if (::setxattr(destPath.c_str(), smackAccessLabel.c_str(), smackLabel.data(), smackLabel.size(), 0) == -1)
162 throw runtime::Exception(runtime::GetSystemErrorMessage());
165 int Ext4Engine::copy(std::string& src, std::string& dest)
167 int readFd, writeFd, ret;
170 ret = ::stat(src.c_str(), &st);
172 throw runtime::Exception(src + runtime::GetSystemErrorMessage());
175 readFd = ::open(src.c_str(), O_RDONLY);
177 throw runtime::Exception(src + runtime::GetSystemErrorMessage());
179 writeFd = ::open(dest.c_str(), O_WRONLY | O_CREAT, st.st_mode);
181 throw runtime::Exception(dest + runtime::GetSystemErrorMessage());
182 copySmackLabel(src, dest);
183 if (::chown(dest.c_str(), st.st_uid, st.st_gid) == -1)
184 throw runtime::Exception(dest + runtime::GetSystemErrorMessage());
185 if (::sendfile(writeFd, readFd, 0, st.st_size) == -1)
188 /* progress bar update */
189 curSize += st.st_size;
190 INFO("curSize is " + std::to_string(curSize));
191 progressBar.update(curSize, totalSize, 1);
193 if (::fsync(writeFd) != 0)
194 throw runtime::Exception(dest + runtime::GetSystemErrorMessage());
196 if (::posix_fadvise(writeFd, 0, st.st_size, POSIX_FADV_DONTNEED) < 0)
197 throw runtime::Exception(dest + runtime::GetSystemErrorMessage());
204 static void preScanDir(std::string& dir)
208 d = ::opendir(dir.c_str());
211 throw runtime::Exception(dir + runtime::GetSystemErrorMessage());
214 struct dirent* entry;
217 entry = ::readdir(d);
221 dName = entry->d_name;
222 if (!(entry->d_type & DT_DIR)) {
223 std::string file = dir + "/" + dName;
227 statRet = ::stat(file.c_str(), &st);
229 throw runtime::Exception(file + runtime::GetSystemErrorMessage());
230 INFO("[preListDir] " + file + " totalSize = " + std::to_string(totalSize) + " " +std::to_string(st.st_size));
231 totalSize += st.st_size;
234 if (entry->d_type & DT_DIR) {
235 if (dName.compare(".") != 0 && dName.compare("..") != 0) {
238 path = dir + "/" + dName;
241 if (path.size() >= PATH_MAX)
242 throw runtime::Exception(path + " :path length has got too long");
250 throw runtime::Exception(runtime::GetSystemErrorMessage());
254 void Ext4Engine::listDir(std::string& source, std::string& dest, bool excludeFlag)
258 if (excludeFlag && source.compare(0, bindMountPoint.size(), bindMountPoint) == 0)
261 d = ::opendir(source.c_str());
264 throw runtime::Exception(source + runtime::GetSystemErrorMessage());
267 struct dirent* entry;
270 entry = ::readdir(d);
274 dName = entry->d_name;
275 if (!(entry->d_type & DT_DIR)) {
276 std::string srcFile = source + "/" + dName;
277 std::string destFile = dest + "/" +dName;
278 copy(srcFile, destFile);
280 if (::remove(srcFile.c_str()) != 0)
281 ERROR("file remove failed");
284 if (entry->d_type & DT_DIR) {
285 if (dName.compare(".") != 0 && dName.compare("..") != 0) {
286 std::string pathS, pathD;
288 pathS = source + "/" + dName;
289 pathD = dest + "/" +dName;
291 /* make new directory */
292 int mkdirRet, statRet;
294 statRet = ::stat(pathS.c_str(), &st);
296 throw runtime::Exception(pathS + runtime::GetSystemErrorMessage());
299 if (pathS.compare(bindMountPoint) != 0) {
300 mkdirRet = ::mkdir(pathD.c_str(), st.st_mode);
302 throw runtime::Exception(pathS + runtime::GetSystemErrorMessage());
303 copySmackLabel(pathS, pathD);
304 if (::chown(pathD.c_str(), st.st_uid, st.st_gid) == -1)
305 throw runtime::Exception(runtime::GetSystemErrorMessage());
308 mkdirRet = ::mkdir(pathD.c_str(), st.st_mode);
310 throw runtime::Exception(pathD + runtime::GetSystemErrorMessage());
311 copySmackLabel(pathS, pathD);
312 if (::chown(pathD.c_str(), st.st_uid, st.st_gid) == -1)
313 throw runtime::Exception(pathD + runtime::GetSystemErrorMessage());
315 if (pathS.size() >= PATH_MAX)
316 throw runtime::Exception(pathS + " :path length has got too long");
318 listDir(pathS, pathD, excludeFlag);
324 throw runtime::Exception(runtime::GetSystemErrorMessage());
326 if (source.compare(secondMountPoint) != 0) {
327 if (::remove(source.c_str()) != 0)
328 throw runtime::Exception(source + runtime::GetSystemErrorMessage());
332 static int intLog2(int arg)
345 static void setPolicy(const std::string& source, const Ext4Engine::data& key)
347 struct ext4_encryption_policy policy;
350 Ext4Engine::data descriptor(EXT4_KEY_DESCRIPTOR_SIZE);
352 descriptor = generateKeyDesc(key);
353 std::string descStr(descriptor.begin(), descriptor.end());
355 fd = ::open(source.c_str(), O_DIRECTORY);
357 throw runtime::Exception("invalid path");
360 policy.contents_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
361 policy.filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
362 policy.flags = intLog2(pad >> 2);
363 ::memcpy(policy.master_key_descriptor, descriptor.data(), EXT4_KEY_DESCRIPTOR_SIZE);
365 rc = ::ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &policy);
369 throw runtime::Exception("set policy failed :" + runtime::GetSystemErrorMessage());
372 static bool prepareEncryptDir(std::string& sourceName, std::string& destName)
375 ::stat(destName.c_str(), &dirStat);
377 if (::mkdir(secondMountPoint.c_str(), dirStat.st_mode) != 0)
378 throw runtime::Exception(runtime::GetSystemErrorMessage());
379 copySmackLabel(destName, secondMountPoint);
380 if (::chown(secondMountPoint.c_str(), dirStat.st_uid, dirStat.st_gid) == -1)
381 throw runtime::Exception(runtime::GetSystemErrorMessage());
382 if (::mount(sourceName.c_str(), secondMountPoint.c_str(), "ext4", 0, 0) < 0) {
383 ::remove(secondMountPoint.c_str());
384 throw runtime::Exception(runtime::GetSystemErrorMessage());
387 if (::mkdir(bindMountPoint.c_str(), dirStat.st_mode) != 0)
388 throw runtime::Exception(runtime::GetSystemErrorMessage());
389 copySmackLabel(secondMountPoint, bindMountPoint);
390 if (::chown(bindMountPoint.c_str(), dirStat.st_uid, dirStat.st_gid) == -1)
391 throw runtime::Exception(runtime::GetSystemErrorMessage());
395 static bool getPolicy(const std::string& dirName)
397 struct ext4_encryption_policy policy;
400 fd = ::open(dirName.c_str(), O_DIRECTORY);
404 rc = ::ioctl(fd, EXT4_IOC_GET_ENCRYPTION_POLICY, &policy);
407 ERROR("ioctl error");
413 Ext4Engine::Ext4Engine(const std::string& src, const std::string& dest, const ProgressBar &prgsBar) :
414 source(src), destination(dest), progressBar(prgsBar)
418 Ext4Engine::~Ext4Engine()
422 void Ext4Engine::mount(const Ext4Engine::data& key, unsigned int options)
425 /* mount : /dev/mmcblk0p21 /opt/usr_encrypt */
426 if (::mount(source.c_str(), secondMountPoint.c_str(), "ext4", 0, 0) < 0)
427 throw runtime::Exception(runtime::GetSystemErrorMessage());
428 /* bind mount :/opt/usr_encrypt/secure /opt/usr */
429 if (::mount(bindMountPoint.c_str(), destination.c_str(), "ext4", MS_BIND, 0) < 0)
430 throw runtime::Exception(runtime::GetSystemErrorMessage());
433 void Ext4Engine::umount()
435 /* for decrypt, umount /opt/usr */
436 if (::umount(destination.c_str()))
437 throw runtime::Exception(runtime::GetSystemErrorMessage());
440 void Ext4Engine::addKey(const Ext4Engine::data& key)
442 addKeyToKeyring(key);
445 void Ext4Engine::encrypt(const Ext4Engine::data& key, unsigned int options)
447 std::string sourceDir = getSource();
448 std::string destDir = getDestination();
449 bool copyFlag = false;
451 if (!(copyFlag = prepareEncryptDir(sourceDir, destDir)))
452 throw runtime::Exception("prepareEncryptDir failed");
454 preScanDir(secondMountPoint);
455 /* key add to keyring */
456 addKeyToKeyring(key);
458 setPolicy(bindMountPoint, key);
461 listDir(secondMountPoint, bindMountPoint, true);
462 INFO("[ext4 encrypt] copy done");
465 if (::mount(bindMountPoint.c_str(), destDir.c_str(), "ext4", MS_BIND, 0) < 0)
466 throw runtime::Exception(runtime::GetSystemErrorMessage());
469 void Ext4Engine::decrypt(const Ext4Engine::data& key, unsigned int options)
471 std::string destDir = getDestination();
473 if (!getPolicy(bindMountPoint))
474 throw runtime::Exception("directory isn't encrypted");
475 addKeyToKeyring(key);
477 preScanDir(bindMountPoint);
479 listDir(bindMountPoint, secondMountPoint, false);
480 INFO("[ext4 decrypt] copy done");
483 if (::open(bindMountPoint.c_str(), O_RDONLY) != -1)
484 ::remove(bindMountPoint.c_str());
486 /* umount /opt/usr_encrypt */
487 if (::umount(secondMountPoint.c_str()))
488 throw runtime::Exception(runtime::GetSystemErrorMessage());
489 /* mount /dev/mmcblk0p21 /opt/usr */
490 if (::mount(source.c_str(), destination.c_str(), "ext4", 0, 0) < 0)
491 throw runtime::Exception(runtime::GetSystemErrorMessage());
493 if (::open(secondMountPoint.c_str(), O_RDONLY) != -1)
494 ::remove(secondMountPoint.c_str());
497 bool Ext4Engine::isKeyMetaSet()
499 return FileFooter::exist(source);
502 const Ext4Engine::data Ext4Engine::getKeyMeta()
504 return FileFooter::read(source);
507 void Ext4Engine::setKeyMeta(const data &data)
509 FileFooter::write(source, data);
512 void Ext4Engine::clearKeyMeta()
514 FileFooter::clear(source);
517 unsigned int Ext4Engine::getSupportedOptions()