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
18 #include <klay/error.h>
19 #include <klay/exception.h>
20 #include <klay/filesystem.h>
21 #include <klay/audit/logger.h>
23 #include "../kernel-keyring.h"
24 #include "../file-footer.h"
26 #include "ecryptfs-engine.h"
28 #define OPTION_ONLY_NEW_FILE (1 << 0)
29 #define OPTION_EXCEPT_FOR_MEDIA_FILE (1 << 1)
31 #define SUPPORTED_OPTIONS OPTION_ONLY_NEW_FILE
33 #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"
35 #define CIPHER_MODE "aes"
36 #define ENCRYPTION_CHECKER_NAME ".ecryptfs_encrypted"
39 #define ECRYPTFS_VERSION_MAJOR 0x00
40 #define ECRYPTFS_VERSION_MINOR 0x04
41 #define ECRYPTFS_VERSION ((ECRYPTFS_VERSION_MAJOR << 8) | ECRYPTFS_VERSION_MINOR)
43 #define ECRYPTFS_MAX_KEY_BYTES 64
44 #define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512
45 #define ECRYPTFS_SIG_SIZE 8
46 #define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2)
47 #define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX
48 #define ECRYPTFS_SALT_SIZE 8
49 #define ECRYPTFS_MAX_KEY_MOD_NAME_BYTES 16
51 struct ecryptfs_session_key {
52 #define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x00000001
53 #define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002
54 #define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004
55 #define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008
57 int32_t encrypted_key_size;
58 int32_t decrypted_key_size;
59 uint8_t encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
60 uint8_t decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
63 struct ecryptfs_password {
64 int32_t password_bytes;
66 int32_t hash_iterations;
67 int32_t session_key_encryption_key_bytes;
68 #define ECRYPTFS_PERSISTENT_PASSWORD 0x01
69 #define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02
71 uint8_t session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
72 uint8_t signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
73 uint8_t salt[ECRYPTFS_SALT_SIZE];
76 struct ecryptfs_private_key {
79 uint8_t signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
80 char key_mod_alias[ECRYPTFS_MAX_KEY_MOD_NAME_BYTES + 1];
84 enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY};
86 struct ecryptfs_auth_tok {
89 #define ECRYPTFS_ENCRYPT_ONLY 0x00000001
91 struct ecryptfs_session_key session_key;
94 struct ecryptfs_password password;
95 struct ecryptfs_private_key private_key;
97 } __attribute__((packed));
100 #define ECRYPTFS_IOCTL_GET_ATTRIBUTES _IOR('l', 0x10, unsigned int)
101 #define ECRYPTFS_WAS_ENCRYPTED 0x0080
102 #define ECRYPTFS_WAS_ENCRYPTED_OTHER_DEVICE 0x0100
105 #define ECRYPTFS_AUTH_TOKEN_TYPE "user"
111 unsigned long long getAvailableSpace(const std::string& mountPoint)
113 struct statfs statbuf;
114 if (::statfs(mountPoint.c_str(), &statbuf)) {
115 throw runtime::Exception("Failed to access " + mountPoint);
118 return (unsigned long long)statbuf.f_bfree * statbuf.f_bsize;
121 bool wasEncrypted(const std::string &path)
123 #ifdef ECRYPTFS_IOCTL_GET_ATTRIBUTES
124 unsigned int attrs = 0;
128 fd = ::open(path.c_str(), O_RDWR);
130 throw runtime::Exception("Failed to open " + path);
133 if (::ioctl(fd, ECRYPTFS_IOCTL_GET_ATTRIBUTES, &attrs) == 0 &&
134 (attrs & ECRYPTFS_WAS_ENCRYPTED)) {
145 unsigned long long getEncryptedSize(const runtime::File &file) {
146 unsigned long long originalSize = file.size();
147 unsigned long long result = 0;
149 if (originalSize % 4096) {
150 originalSize = (1 + originalSize / 4096) * 4096;
153 #ifdef ECRYPTFS_IOCTL_GET_ATTRIBUTES
154 if (wasEncrypted(file.getPath())) {
155 result = originalSize;
158 result = originalSize + 2 * 4096;
159 #ifdef ECRYPTFS_IOCTL_GET_ATTRIBUTES
163 //TODO : block size have to not hard-coded.
164 // If there is a better way, the followings have to be changed.
165 unsigned int blockSize = 4096;
166 if (result % blockSize) {
167 result = (1 + result / blockSize) * blockSize;
173 unsigned long long getDecryptedSize(const runtime::File &file) {
174 unsigned long long originalSize = file.size();
175 unsigned long long result = originalSize;
177 if (wasEncrypted(file.getPath())) {
178 if (originalSize > 2 * 4096) {
179 result = originalSize - 2 * 4096;
183 result = originalSize;
185 //TODO : block size have to not hard-coded.
186 // If there is a better way, the followings have to be changed.
187 unsigned int blockSize = 4096;
188 if (result % blockSize) {
189 result = (1 + result / blockSize) * blockSize;
195 bool isEnoughToCopyInPlace(const std::string& path,
196 const std::function<unsigned long long(const runtime::File&)> getSizeFunc)
198 unsigned long long availableSpace = getAvailableSpace(path);
200 std::function<bool(const std::string &path)> check;
201 check = [&check, &getSizeFunc, availableSpace](const std::string &path) {
202 for (runtime::DirectoryIterator iter(path), end;
203 iter != end; ++iter) {
204 if (iter->isDirectory()) {
205 if (!check(iter->getPath())) {
208 } else if (getSizeFunc(*iter) > availableSpace) {
209 //TODO : have to consider changing file size
210 // when encrypt/decrypt
220 void copyInPlace(const std::string& source, const std::string& destination,
221 const std::string& temp,
222 const std::function<bool(const std::string&)> &isTarget,
223 const std::function<void(unsigned long long)> &addProgress)
225 for (runtime::DirectoryIterator iter(source), end;
226 iter != end; ++iter) {
227 if (iter->isDirectory()) {
228 copyInPlace(iter->getPath(), destination + "/" + iter->getName(),
229 temp, isTarget, addProgress);
230 } else if (isTarget(iter->getPath())) {
231 std::string tempFilePath = temp + "/" + iter->getName();
232 std::string destFilePath = destination + "/" + iter->getName();
234 iter->copyTo(tempFilePath);
236 if (::rename(tempFilePath.c_str(), destFilePath.c_str()) != 0) {
237 throw runtime::Exception("Failed to rename from " + tempFilePath + " to " + destFilePath);
240 addProgress(iter->size());
245 void ecryptfsMount(const std::string &source, const std::string &destination, const std::vector<unsigned char> &key, unsigned int options)
247 ecryptfs_auth_tok payload;
248 std::string mountOption;
250 ::memset(&(payload), 0, sizeof(ecryptfs_auth_tok));
252 payload.version = ECRYPTFS_VERSION;
253 payload.token_type = ECRYPTFS_PASSWORD;
254 payload.token.password.flags = ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET;
255 payload.token.password.session_key_encryption_key_bytes =
256 (ECRYPTFS_MAX_KEY_BYTES > key.size())? key.size() :
257 ECRYPTFS_MAX_KEY_BYTES;
258 ::memcpy(payload.token.password.session_key_encryption_key, key.data(),
259 payload.token.password.session_key_encryption_key_bytes);
261 std::stringstream signature;
262 signature<< std::hex << std::setfill('0') << std::setw(2);
263 for (unsigned int byte : key) {
266 for (int i = key.size(); i < ECRYPTFS_SIG_SIZE; i++) {
267 signature << (unsigned int) 0;
269 ::memcpy((char *)payload.token.password.signature,
270 signature.str().c_str(), ECRYPTFS_PASSWORD_SIG_SIZE);
272 if (KernelKeyRing::search(KEY_SPEC_USER_KEYRING, ECRYPTFS_AUTH_TOKEN_TYPE,
273 (char *)payload.token.password.signature, 0) < 0) {
274 if (KernelKeyRing::add(ECRYPTFS_AUTH_TOKEN_TYPE,
275 (char *)payload.token.password.signature,
276 (void *)&payload, sizeof(payload),
277 KEY_SPEC_USER_KEYRING) < 0) {
278 throw runtime::Exception("Unable to add token to keyring.");
282 mountOption = "ecryptfs_passthrough"
283 ",ecryptfs_cipher=" CIPHER_MODE
284 ",ecryptfs_sig=" + std::string((char *)payload.token.password.signature) +
285 ",ecryptfs_key_bytes=" + std::to_string(payload.token.password.session_key_encryption_key_bytes);
287 if (options & OPTION_EXCEPT_FOR_MEDIA_FILE) {
288 mountOption += ",ecryptfs_enable_filtering=" MEDIA_EXCLUSION_LIST;
291 INFO("option = " + mountOption);
292 INFO("source = " + source);
293 INFO("dest = " + destination);
295 if (::mount(source.c_str(), destination.c_str(), "ecryptfs", MS_NODEV,
296 mountOption.c_str()) != 0) {
297 throw runtime::Exception(runtime::GetSystemErrorMessage());
301 void ecryptfsUmount(const std::string &destination)
303 if (::umount(destination.c_str()) != 0) {
304 throw runtime::Exception(runtime::GetSystemErrorMessage());
307 //TODO : remove key from keyring
312 EcryptfsEngine::EcryptfsEngine(const std::string &src, const std::string &dest, const ProgressBar &prg) :
313 source(src), destination(dest), progress(prg)
317 EcryptfsEngine::~EcryptfsEngine()
321 void EcryptfsEngine::mount(const data &key, unsigned int options)
323 ecryptfsMount(source, destination, key, options);
326 void EcryptfsEngine::umount()
328 ecryptfsUmount(destination);
331 void EcryptfsEngine::encrypt(const data &key, unsigned int options)
333 if (!isEnoughToCopyInPlace(source, getDecryptedSize)) {
334 throw runtime::Exception("No space to encryption");
340 ecryptfsMount(source, destination, key, options);
341 } catch (runtime::Exception &e) {
342 throw runtime::Exception("Failed to mount - " + std::string(e.what()));
346 unsigned long long totalSize = getAvailableSpace(source), current;
347 runtime::File tempDir(destination + "/" ENCRYPTION_CHECKER_NAME);
349 tempDir.makeDirectory();
350 if (!(options & OPTION_ONLY_NEW_FILE)) {
351 copyInPlace(destination, destination, tempDir.getPath(),
352 [](const std::string &file) {
355 [¤t, &totalSize, this](unsigned long long size) {
357 this->progress.update(current * 100 / totalSize);
360 } catch (runtime::Exception &e) {
362 ecryptfsUmount(destination);
363 } catch (runtime::Exception &e) {}
364 throw runtime::Exception("Failed to encrypt file - " + std::string(e.what()));
372 void EcryptfsEngine::decrypt(const data &key, unsigned int options)
374 if (!isEnoughToCopyInPlace(destination, getEncryptedSize)) {
375 throw runtime::Exception("No space to encryption");
381 unsigned long long totalSize = getAvailableSpace(source), current;
382 runtime::File tempDir(source + "/" ENCRYPTION_CHECKER_NAME);
383 runtime::File tempMountpoint(tempDir.getPath() + "/mount");
385 tempMountpoint.makeDirectory();
386 ecryptfsMount(source, tempMountpoint.getPath(), key, 0);
388 copyInPlace(tempMountpoint.getPath(), source,
389 tempDir.getPath(), wasEncrypted,
390 [¤t, &totalSize, this](unsigned long long size) {
392 this->progress.update(current * 100 / totalSize);
394 ecryptfsUmount(tempMountpoint.getPath());
396 tempDir.remove(true);
397 } catch (runtime::Exception &e) {
398 throw runtime::Exception("Failed to decrypt file - " + std::string(e.what()));
406 const EcryptfsEngine::data EcryptfsEngine::getKeyMeta()
408 return FileFooter::read(source);
411 void EcryptfsEngine::setKeyMeta(const data &meta)
413 FileFooter::write(source, meta);
416 unsigned int EcryptfsEngine::getSupportedOptions()
418 return SUPPORTED_OPTIONS;