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
25 #include <sys/mount.h>
26 #include <sys/ioctl.h>
27 #include <sys/types.h>
28 #include <sys/xattr.h>
30 #include <klay/error.h>
31 #include <klay/exception.h>
32 #include <klay/filesystem.h>
33 #include <klay/audit/logger.h>
35 #include "../../kernel-keyring.h"
36 #include "../../file-footer.h"
37 #include "../../key-manager/key-generator.h"
39 #include "ext4-engine.h"
41 #define ENCRYPTION_DIR ".encrypted"
43 #define SMACK_LABEL_LEN_MAX 255
45 #define EXT4_MAX_KEY_SIZE 64
46 #define EXT4_KEY_DESCRIPTOR_SIZE 8
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 #define EXT4_KEYRING_TYPE "logon"
52 /* Encryption algorithms */
53 #define EXT4_ENCRYPTION_MODE_INVALID 0
54 #define EXT4_ENCRYPTION_MODE_AES_256_XTS 1
55 #define EXT4_ENCRYPTION_MODE_AES_256_GCM 2
56 #define EXT4_ENCRYPTION_MODE_AES_256_CBC 3
57 #define EXT4_ENCRYPTION_MODE_AES_256_CTS 4
59 struct ext4_encryption_policy {
61 char contents_encryption_mode;
62 char filenames_encryption_mode;
64 char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
65 }__attribute__((__packed__));
67 struct ext4_encryption_key {
69 char raw[EXT4_MAX_KEY_SIZE];
71 } __attribute__((__packed__));
77 const Ext4Engine::data generateKeyDescriptor(const Ext4Engine::data& key)
79 auto hash = KeyGenerator::SHA512(KeyGenerator::SHA512(key));
80 hash.resize(EXT4_KEY_DESCRIPTOR_SIZE);
84 const std::string convertToHex(const Ext4Engine::data &binary)
86 std::stringstream hex;
88 hex << std::hex << std::setfill('0');
89 for (unsigned int byte : binary) {
90 hex << std::setw(2) << byte;
95 Ext4Engine::data sanitizeKey(const Ext4Engine::data &key)
97 Ext4Engine::data sanitized(key);
98 sanitized.resize(EXT4_MAX_KEY_SIZE);
102 void addKeyToKeyring(const Ext4Engine::data& key)
104 struct ext4_encryption_key payload;
105 std::string keyDescriptor;
108 keyringId = KernelKeyRing::getKeyringId(KEY_SPEC_SESSION_KEYRING, 0);
109 if (keyringId == -1) {
110 throw runtime::Exception("Unable to get keyring id");
113 keyDescriptor = "ext4:" + convertToHex(generateKeyDescriptor(key));
114 if (KernelKeyRing::search(keyringId, EXT4_KEYRING_TYPE,
115 keyDescriptor, 0) >= 0) {
116 INFO("Key with descriptor already exist");
120 payload.mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
121 payload.size = EXT4_MAX_KEY_SIZE;
122 ::memcpy(payload.raw, key.data(), key.size());
124 if (KernelKeyRing::add(EXT4_KEYRING_TYPE, keyDescriptor,
125 (void *)&payload, sizeof(payload),
126 KEY_SPEC_USER_KEYRING) < 0) {
127 throw runtime::Exception("Unable to add key to keyring");
144 void setPolicy(const std::string& path, const Ext4Engine::data& key)
146 struct ext4_encryption_policy policy;
150 fd = ::open(path.c_str(), O_DIRECTORY);
152 throw runtime::Exception("invalid path");
155 Ext4Engine::data descriptor = generateKeyDescriptor(key);
158 policy.contents_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
159 policy.filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
160 policy.flags = intLog2(pad >> 2);
161 ::memcpy(policy.master_key_descriptor, descriptor.data(), descriptor.size());
163 rc = ::ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &policy);
167 throw runtime::Exception("set policy failed :" + runtime::GetSystemErrorMessage());
171 bool hasPolicy(const std::string& dir)
173 struct ext4_encryption_policy policy;
176 fd = ::open(dir.c_str(), O_DIRECTORY);
181 rc = ::ioctl(fd, EXT4_IOC_GET_ENCRYPTION_POLICY, &policy);
191 off_t getUsedSpace(const std::string& mountPoint)
193 struct statfs statbuf;
194 if (::statfs(mountPoint.c_str(), &statbuf)) {
195 throw runtime::Exception("Failed to access " + mountPoint);
198 return (off_t)(statbuf.f_blocks - statbuf.f_bfree) * statbuf.f_bsize;
201 off_t getAvailableSpace(const std::string& mountPoint)
203 struct statfs statbuf;
204 if (::statfs(mountPoint.c_str(), &statbuf)) {
205 throw runtime::Exception("Failed to access " + mountPoint);
208 return (off_t)statbuf.f_bfree * statbuf.f_bsize;
211 static void copyDac(const std::string& srcPath, const std::string& destPath)
213 runtime::File src(srcPath), dest(destPath);
216 mode_t mode = src.getMode();
217 uid_t uid = src.getUid();
218 gid_t gid = src.getGid();
221 dest.chown(uid, gid);
222 } catch (runtime::Exception &e) {}
226 } catch (runtime::Exception &e) {}
227 } catch (runtime::Exception &e) {}
230 static void copyMac(const std::string& srcPath, const std::string& destPath)
232 char smackLabel[SMACK_LABEL_LEN_MAX + 1];
235 labelSize = ::getxattr(srcPath.c_str(), "security.SMACK64", smackLabel, SMACK_LABEL_LEN_MAX + 1);
237 if (labelSize == -1) {
241 ::setxattr(destPath.c_str(), "security.SMACK64", smackLabel, labelSize, 0);
244 bool isEnoughToCopyInPlace(const std::string& path)
246 off_t availableSpace = getAvailableSpace(path);
248 std::function<bool(const std::string &path)> check;
249 check = [&check, availableSpace](const std::string &path) {
250 for (runtime::DirectoryIterator iter(path), end;
251 iter != end; ++iter) {
252 if (iter->isDirectory()) {
253 if (!check(iter->getPath())) {
256 } else if (iter->size() > availableSpace) {
266 void copyInPlace(const std::string& source, const std::string& destination,
267 const std::function<void(off_t)> &addProgress)
269 for (runtime::DirectoryIterator iter(source), end; iter != end; ++iter) {
270 if (iter->getPath() == destination) {
274 runtime::File destFile(destination + "/" + iter->getName());
275 if (iter->isDirectory()) {
276 destFile.makeDirectory();
277 copyInPlace(iter->getPath(), destFile.getPath(), addProgress);
279 iter->copyTo(destFile.getPath());
280 addProgress(iter->size());
282 copyDac(iter->getPath(), destFile.getPath());
283 copyMac(iter->getPath(), destFile.getPath());
291 Ext4Engine::Ext4Engine(const std::string& src, const std::string& dest, const ProgressBar &prgsBar) :
292 source(src), destination(dest), progress(prgsBar), mounted(false)
296 Ext4Engine::~Ext4Engine()
300 void Ext4Engine::mount(const Ext4Engine::data& key, unsigned int options)
302 std::string encryptedPath(destination + "/" ENCRYPTION_DIR);
304 addKeyToKeyring(sanitizeKey(key));
306 if (::mount(source.c_str(), destination.c_str(), "ext4", 0, 0) < 0) {
307 throw runtime::Exception("Mount error - " + runtime::GetSystemErrorMessage());
310 if (::mount(encryptedPath.c_str(), destination.c_str(), NULL, MS_BIND, 0) < 0) {
311 throw runtime::Exception("Mount error - " + runtime::GetSystemErrorMessage());
317 void Ext4Engine::umount()
321 if (::umount(destination.c_str())) {
322 throw runtime::Exception(runtime::GetSystemErrorMessage());
328 bool Ext4Engine::isMounted()
333 void Ext4Engine::encrypt(const Ext4Engine::data& key, unsigned int options)
335 if (!isEnoughToCopyInPlace(destination)) {
336 throw runtime::Exception("No space to encryption");
339 if (::mount(source.c_str(), destination.c_str(), "ext4", 0, 0) < 0) {
340 throw runtime::Exception("Mount error - " + runtime::GetSystemErrorMessage());
343 Ext4Engine::data sanitizedKey = sanitizeKey(key);
344 addKeyToKeyring(sanitizedKey);
346 runtime::File encrypted(destination + "/" ENCRYPTION_DIR);
347 if (encrypted.exists()) {
348 encrypted.remove(true);
350 encrypted.makeDirectory();
351 setPolicy(encrypted.getPath(), sanitizedKey);
353 off_t totalSize = getUsedSpace(source), current = 0;
354 copyInPlace(destination, encrypted.getPath(),
355 [¤t, &totalSize, this](off_t size) {
357 this->progress.update(current, totalSize, 1);
360 if (::mount(encrypted.getPath().c_str(), destination.c_str(), NULL, MS_BIND, 0) < 0) {
361 throw runtime::Exception("Mount error - " + runtime::GetSystemErrorMessage());
369 void Ext4Engine::decrypt(const Ext4Engine::data& key, unsigned int options)
371 if (!isEnoughToCopyInPlace(destination)) {
372 throw runtime::Exception("No space to encryption");
375 if (!hasPolicy(destination)) {
376 throw runtime::Exception("failed get policy");
379 runtime::File encrypted(destination + "/" ENCRYPTION_DIR);
381 off_t totalSize = getUsedSpace(source), current = 0;
382 copyInPlace(encrypted.getPath(), destination,
383 [¤t, &totalSize, this](off_t size) {
385 this->progress.update(current, totalSize, 1);
388 encrypted.remove(true);
395 bool Ext4Engine::isKeyMetaSet()
397 return FileFooter::exist(source);
400 const Ext4Engine::data Ext4Engine::getKeyMeta()
402 return FileFooter::read(source);
405 void Ext4Engine::setKeyMeta(const data &data)
407 FileFooter::write(source, data);
410 void Ext4Engine::clearKeyMeta()
412 FileFooter::clear(source);
415 unsigned int Ext4Engine::getSupportedOptions()