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
20 #include <sys/mount.h>
22 #include <klay/error.h>
23 #include <klay/exception.h>
24 #include <klay/filesystem.h>
25 #include <klay/audit/logger.h>
27 #include "../../kernel-keyring.h"
28 #include "../../file-footer.h"
30 #include "ecryptfs-engine.h"
32 #define OPTION_ONLY_NEW_FILE (1 << 0)
33 #define OPTION_EXCEPT_FOR_MEDIA_FILE (1 << 1)
35 #define SUPPORTED_OPTIONS OPTION_ONLY_NEW_FILE
37 #define MEDIA_EXCLUSION_LIST "temp_video/Camera/DCIM:mp3|mpga|m4a|mp4|wav|amr|awb|wma|ogg|oga|aac|mka|flac|3gp|3ga|mid|midi|xmf|rtttl|rtx|ota|smf|spm|imy|mpeg|m4v|3gp|3gpp|3g2|3gpp2|wmv|asf|mkv|webm|ts|avi|jpg|jpeg|gif|png|bmp|wbmp|divx|flv|ac3|mov|tiff|f4v|mpeg3|voice"
39 #define CIPHER_MODE "aes"
40 #define ENCRYPTION_CHECKER_NAME ".ecryptfs_encrypted"
43 #define ECRYPTFS_VERSION_MAJOR 0x00
44 #define ECRYPTFS_VERSION_MINOR 0x04
45 #define ECRYPTFS_VERSION ((ECRYPTFS_VERSION_MAJOR << 8) | ECRYPTFS_VERSION_MINOR)
47 #define ECRYPTFS_MAX_KEY_BYTES 64
48 #define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512
49 #define ECRYPTFS_SIG_SIZE 8
50 #define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2)
51 #define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX
52 #define ECRYPTFS_SALT_SIZE 8
53 #define ECRYPTFS_MAX_KEY_MOD_NAME_BYTES 16
55 struct ecryptfs_session_key {
56 #define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x00000001
57 #define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002
58 #define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004
59 #define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008
61 int32_t encrypted_key_size;
62 int32_t decrypted_key_size;
63 uint8_t encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
64 uint8_t decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
67 struct ecryptfs_password {
68 int32_t password_bytes;
70 int32_t hash_iterations;
71 int32_t session_key_encryption_key_bytes;
72 #define ECRYPTFS_PERSISTENT_PASSWORD 0x01
73 #define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02
75 uint8_t session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
76 uint8_t signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
77 uint8_t salt[ECRYPTFS_SALT_SIZE];
80 struct ecryptfs_private_key {
83 uint8_t signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
84 char key_mod_alias[ECRYPTFS_MAX_KEY_MOD_NAME_BYTES + 1];
88 enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY};
90 struct ecryptfs_auth_tok {
93 #define ECRYPTFS_ENCRYPT_ONLY 0x00000001
95 struct ecryptfs_session_key session_key;
98 struct ecryptfs_password password;
99 struct ecryptfs_private_key private_key;
101 } __attribute__((packed));
104 #define ECRYPTFS_IOCTL_GET_ATTRIBUTES _IOR('l', 0x10, unsigned int)
105 #define ECRYPTFS_WAS_ENCRYPTED 0x0080
106 #define ECRYPTFS_WAS_ENCRYPTED_OTHER_DEVICE 0x0100
109 #define ECRYPTFS_AUTH_TOKEN_TYPE "user"
115 unsigned long long getAvailableSpace(const std::string& mountPoint)
117 struct statfs statbuf;
118 if (::statfs(mountPoint.c_str(), &statbuf)) {
119 throw runtime::Exception("Failed to access " + mountPoint);
122 return (unsigned long long)statbuf.f_bfree * statbuf.f_bsize;
125 bool wasEncrypted(const std::string &path)
127 #ifdef ECRYPTFS_IOCTL_GET_ATTRIBUTES
128 unsigned int attrs = 0;
132 fd = ::open(path.c_str(), O_RDWR);
134 throw runtime::Exception("Failed to open " + path);
137 if (::ioctl(fd, ECRYPTFS_IOCTL_GET_ATTRIBUTES, &attrs) == 0 &&
138 (attrs & ECRYPTFS_WAS_ENCRYPTED)) {
149 unsigned long long getEncryptedSize(const runtime::File &file) {
150 unsigned long long originalSize = file.size();
151 unsigned long long result = 0;
153 if (originalSize % 4096) {
154 originalSize = (1 + originalSize / 4096) * 4096;
157 #ifdef ECRYPTFS_IOCTL_GET_ATTRIBUTES
158 if (wasEncrypted(file.getPath())) {
159 result = originalSize;
162 result = originalSize + 2 * 4096;
163 #ifdef ECRYPTFS_IOCTL_GET_ATTRIBUTES
167 //TODO : block size have to not hard-coded.
168 // If there is a better way, the followings have to be changed.
169 unsigned int blockSize = 4096;
170 if (result % blockSize) {
171 result = (1 + result / blockSize) * blockSize;
177 unsigned long long getDecryptedSize(const runtime::File &file) {
178 unsigned long long originalSize = file.size();
179 unsigned long long result = originalSize;
181 if (wasEncrypted(file.getPath())) {
182 if (originalSize > 2 * 4096) {
183 result = originalSize - 2 * 4096;
187 result = originalSize;
189 //TODO : block size have to not hard-coded.
190 // If there is a better way, the followings have to be changed.
191 unsigned int blockSize = 4096;
192 if (result % blockSize) {
193 result = (1 + result / blockSize) * blockSize;
199 bool isEnoughToCopyInPlace(const std::string& path,
200 const std::function<unsigned long long(const runtime::File&)> getSizeFunc)
202 unsigned long long availableSpace = getAvailableSpace(path);
204 std::function<bool(const std::string &path)> check;
205 check = [&check, &getSizeFunc, availableSpace](const std::string &path) {
206 for (runtime::DirectoryIterator iter(path), end;
207 iter != end; ++iter) {
208 if (iter->isDirectory()) {
209 if (!check(iter->getPath())) {
212 } else if (getSizeFunc(*iter) > availableSpace) {
213 //TODO : have to consider changing file size
214 // when encrypt/decrypt
224 void copyInPlace(const std::string& source, const std::string& destination,
225 const std::string& temp,
226 const std::function<bool(const std::string&)> &isTarget,
227 const std::function<void(unsigned long long)> &addProgress)
229 for (runtime::DirectoryIterator iter(source), end;
230 iter != end; ++iter) {
231 if (iter->isDirectory()) {
232 copyInPlace(iter->getPath(), destination + "/" + iter->getName(),
233 temp, isTarget, addProgress);
234 } else if (isTarget(iter->getPath())) {
235 std::string tempFilePath = temp + "/" + iter->getName();
236 std::string destFilePath = destination + "/" + iter->getName();
238 iter->copyTo(tempFilePath);
240 if (::rename(tempFilePath.c_str(), destFilePath.c_str()) != 0) {
241 throw runtime::Exception("Failed to rename from " + tempFilePath + " to " + destFilePath);
244 addProgress(iter->size());
249 void ecryptfsMount(const std::string &source, const std::string &destination, const std::vector<unsigned char> &key, unsigned int options)
251 ecryptfs_auth_tok payload;
252 std::string mountOption;
254 ::memset(&(payload), 0, sizeof(ecryptfs_auth_tok));
256 payload.version = ECRYPTFS_VERSION;
257 payload.token_type = ECRYPTFS_PASSWORD;
258 payload.token.password.flags = ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET;
259 payload.token.password.session_key_encryption_key_bytes =
260 (ECRYPTFS_MAX_KEY_BYTES > key.size())? key.size() :
261 ECRYPTFS_MAX_KEY_BYTES;
262 ::memcpy(payload.token.password.session_key_encryption_key, key.data(),
263 payload.token.password.session_key_encryption_key_bytes);
265 std::stringstream signature;
266 signature<< std::hex << std::setfill('0') << std::setw(2);
267 for (unsigned int byte : key) {
270 for (int i = key.size(); i < ECRYPTFS_SIG_SIZE; i++) {
271 signature << (unsigned int) 0;
273 ::memcpy((char *)payload.token.password.signature,
274 signature.str().c_str(), ECRYPTFS_PASSWORD_SIG_SIZE);
276 if (KernelKeyRing::search(KEY_SPEC_USER_KEYRING, ECRYPTFS_AUTH_TOKEN_TYPE,
277 (char *)payload.token.password.signature, 0) < 0) {
278 if (KernelKeyRing::add(ECRYPTFS_AUTH_TOKEN_TYPE,
279 (char *)payload.token.password.signature,
280 (void *)&payload, sizeof(payload),
281 KEY_SPEC_USER_KEYRING) < 0) {
282 throw runtime::Exception("Unable to add token to keyring.");
286 mountOption = "ecryptfs_passthrough"
287 ",ecryptfs_cipher=" CIPHER_MODE
288 ",ecryptfs_sig=" + std::string((char *)payload.token.password.signature) +
289 ",ecryptfs_key_bytes=" + std::to_string(payload.token.password.session_key_encryption_key_bytes);
291 if (options & OPTION_EXCEPT_FOR_MEDIA_FILE) {
292 mountOption += ",ecryptfs_enable_filtering=" MEDIA_EXCLUSION_LIST;
295 INFO("option = " + mountOption);
296 INFO("source = " + source);
297 INFO("dest = " + destination);
299 if (::mount(source.c_str(), destination.c_str(), "ecryptfs", MS_NODEV,
300 mountOption.c_str()) != 0) {
301 throw runtime::Exception(runtime::GetSystemErrorMessage());
305 void ecryptfsUmount(const std::string &destination)
307 if (::umount(destination.c_str()) != 0) {
308 throw runtime::Exception(runtime::GetSystemErrorMessage());
311 //TODO : remove key from keyring
316 EcryptfsEngine::EcryptfsEngine(const std::string &src, const std::string &dest, const ProgressBar &prg) :
317 source(src), destination(dest), progress(prg)
321 EcryptfsEngine::~EcryptfsEngine()
325 void EcryptfsEngine::mount(const data &key, unsigned int options)
327 ecryptfsMount(source, destination, key, options);
330 void EcryptfsEngine::umount()
332 ecryptfsUmount(destination);
335 void EcryptfsEngine::encrypt(const data &key, unsigned int options)
337 if (!isEnoughToCopyInPlace(source, getDecryptedSize)) {
338 throw runtime::Exception("No space to encryption");
344 ecryptfsMount(source, destination, key, options);
345 } catch (runtime::Exception &e) {
346 throw runtime::Exception("Failed to mount - " + std::string(e.what()));
350 unsigned long long totalSize = getAvailableSpace(source), current;
351 runtime::File tempDir(destination + "/" ENCRYPTION_CHECKER_NAME);
353 tempDir.makeDirectory();
354 if (!(options & OPTION_ONLY_NEW_FILE)) {
355 copyInPlace(destination, destination, tempDir.getPath(),
356 [](const std::string &file) {
359 [¤t, &totalSize, this](unsigned long long size) {
361 this->progress.update(current * 100 / totalSize);
364 } catch (runtime::Exception &e) {
366 ecryptfsUmount(destination);
367 } catch (runtime::Exception &e) {}
368 throw runtime::Exception("Failed to encrypt file - " + std::string(e.what()));
376 void EcryptfsEngine::decrypt(const data &key, unsigned int options)
378 if (!isEnoughToCopyInPlace(destination, getEncryptedSize)) {
379 throw runtime::Exception("No space to encryption");
385 unsigned long long totalSize = getAvailableSpace(source), current;
386 runtime::File tempDir(source + "/" ENCRYPTION_CHECKER_NAME);
387 runtime::File tempMountpoint(tempDir.getPath() + "/mount");
389 tempMountpoint.makeDirectory();
390 ecryptfsMount(source, tempMountpoint.getPath(), key, 0);
392 copyInPlace(tempMountpoint.getPath(), source,
393 tempDir.getPath(), wasEncrypted,
394 [¤t, &totalSize, this](unsigned long long size) {
396 this->progress.update(current * 100 / totalSize);
398 ecryptfsUmount(tempMountpoint.getPath());
400 tempDir.remove(true);
401 } catch (runtime::Exception &e) {
402 throw runtime::Exception("Failed to decrypt file - " + std::string(e.what()));
410 bool EcryptfsEngine::isKeyMetaSet()
412 return FileFooter::exist(source);
415 const EcryptfsEngine::data EcryptfsEngine::getKeyMeta()
417 return FileFooter::read(source);
420 void EcryptfsEngine::setKeyMeta(const data &meta)
422 FileFooter::write(source, meta);
425 void EcryptfsEngine::clearKeyMeta()
427 FileFooter::clear(source);
430 unsigned int EcryptfsEngine::getSupportedOptions()
432 return SUPPORTED_OPTIONS;