Fix all mis-used types about large file support
[platform/core/security/ode.git] / server / engine / encryption / ecryptfs-engine.cpp
1 /*
2  *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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
15  */
16 #include <iomanip>
17 #include <fstream>
18
19 #include <unistd.h>
20 #include <sys/vfs.h>
21 #include <sys/mount.h>
22
23 #include <klay/error.h>
24 #include <klay/exception.h>
25 #include <klay/filesystem.h>
26 #include <klay/audit/logger.h>
27
28 #include "../../kernel-keyring.h"
29 #include "../../file-footer.h"
30
31 #include "ecryptfs-engine.h"
32
33 #define OPTION_ONLY_NEW_FILE                    (1 << 0)
34 #define OPTION_EXCEPT_FOR_MEDIA_FILE    (1 << 1)
35
36 #define SUPPORTED_OPTIONS OPTION_ONLY_NEW_FILE
37
38 #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
40 #define CIPHER_MODE "aes"
41 #define ENCRYPTION_TMP_DIR ".ecryptfs_temp"
42
43
44 #define ECRYPTFS_VERSION_MAJOR 0x00
45 #define ECRYPTFS_VERSION_MINOR 0x04
46 #define ECRYPTFS_VERSION ((ECRYPTFS_VERSION_MAJOR << 8) | ECRYPTFS_VERSION_MINOR)
47
48 #define ECRYPTFS_SALT_SIZE 8
49 #define ECRYPTFS_SIGNATURE_SIZE 16
50 #define ECRYPTFS_MAX_KEY_SIZE 64
51 #define ECRYPTFS_MAX_KEY_MOD_NAME_SIZE 16
52 #define ECRYPTFS_MAX_ENCRYPTED_KEY_SIZE 512
53
54 struct EcryptfsPassword {
55         enum Flag {
56                 PersistentPassword                      = 0x01,
57                 SessionKeyEncryptionKeySet      = 0x02
58         };
59
60     int32_t passwordSize;
61     int32_t hashAlgorithm;
62     int32_t hashIterations;
63     int32_t sessionKeyEncryptionKeySize;
64     uint32_t flags;
65     uint8_t sessionKeyEncryptionKey[ECRYPTFS_MAX_KEY_SIZE];
66     uint8_t signature[ECRYPTFS_SIGNATURE_SIZE + 1];
67     uint8_t salt[ECRYPTFS_SALT_SIZE];
68 };
69
70 struct EcryptfsPrivateKey {
71     uint32_t keySize;
72     uint32_t dataSize;
73     uint8_t signature[ECRYPTFS_SIGNATURE_SIZE + 1];
74     char keyModAlias[ECRYPTFS_MAX_KEY_MOD_NAME_SIZE + 1];
75     uint8_t data[];
76 };
77
78 struct EcryptfsSessionKey {
79         enum Flag {
80                 UserspaceShouldTryToDecrypt =   0x00000001,
81                 UserspaceShouldTryToEncrypt =   0x00000002,
82                 ContainsDecryptedKey =                  0x00000004,
83                 ContainsEncryptedKey =                  0x00000008
84         };
85
86     int32_t flags;
87     int32_t encryptedKeySize;
88     int32_t decryptedKeySize;
89     uint8_t encryptedKey[ECRYPTFS_MAX_ENCRYPTED_KEY_SIZE];
90     uint8_t decryptedKey[ECRYPTFS_MAX_KEY_SIZE];
91
92         EcryptfsSessionKey()
93                 : flags(0), encryptedKeySize(0), decryptedKeySize(0),
94                         encryptedKey{0, }, decryptedKey{0, }
95         {};
96 } __attribute__((packed));
97
98 struct EcryptfsPayload {
99         enum Type {
100                 PasswordToken,
101                 PrivateKeyToken
102         };
103         enum Flag {
104                 EncryptOnly
105         };
106
107     uint16_t version;
108     uint16_t type;
109     uint32_t flags;
110         EcryptfsSessionKey sessionKey;
111     uint8_t reserved[32];
112     union {
113         EcryptfsPassword password;
114         EcryptfsPrivateKey privateKey;
115     } token;
116
117         EcryptfsPayload(Type type)
118                 : version(ECRYPTFS_VERSION), type(type), flags(0), reserved{0, }
119         {
120                 ::memset(&token, 0, sizeof(token));
121         };
122 } __attribute__((packed));
123
124 #if 0
125 #define ECRYPTFS_IOCTL_GET_ATTRIBUTES  _IOR('l', 0x10, unsigned int)
126 #define ECRYPTFS_WAS_ENCRYPTED 0x0080
127 #define ECRYPTFS_WAS_ENCRYPTED_OTHER_DEVICE 0x0100
128 #endif
129
130 #define ECRYPTFS_AUTH_TOKEN_TYPE "user"
131
132 namespace ode {
133
134 namespace {
135
136 off_t getAvailableSpace(const std::string& mountPoint)
137 {
138         struct statfs statbuf;
139         if (::statfs(mountPoint.c_str(), &statbuf)) {
140                 throw runtime::Exception("Failed to access " + mountPoint);
141         }
142
143         return statbuf.f_bfree * statbuf.f_bsize;
144 }
145
146 bool wasEncrypted(const std::string &path)
147 {
148 #ifdef ECRYPTFS_IOCTL_GET_ATTRIBUTES
149         unsigned int attrs = 0;
150         bool ret = false;
151         int fd = 0;
152
153         fd = ::open(path.c_str(), O_RDWR);
154         if (fd < 0) {
155                 throw runtime::Exception("Failed to open " + path);
156         }
157
158         if (::ioctl(fd, ECRYPTFS_IOCTL_GET_ATTRIBUTES, &attrs) == 0 &&
159                 (attrs & ECRYPTFS_WAS_ENCRYPTED)) {
160                 ret = true;
161         }
162         ::close(fd);
163
164         return ret;
165 #else
166         return true;
167 #endif
168 }
169
170 off_t getEncryptedSize(const runtime::File &file) {
171         off_t originalSize = file.size();
172         off_t result = 0;
173
174         if (originalSize % 4096) {
175                 originalSize = (1 + originalSize / 4096) * 4096;
176         }
177
178 #ifdef ECRYPTFS_IOCTL_GET_ATTRIBUTES
179         if (wasEncrypted(file.getPath())) {
180                 result = originalSize;
181         } else {
182 #endif
183                 result = originalSize + 2 * 4096;
184 #ifdef ECRYPTFS_IOCTL_GET_ATTRIBUTES
185         }
186 #endif
187
188         //TODO : block size have to not hard-coded.
189         //       If there is a better way, the followings have to be changed.
190         blksize_t blockSize = 4096;
191         if (result % blockSize) {
192                 result = (1 + result / blockSize) * blockSize;
193         }
194
195         return result;
196 }
197
198 off_t getDecryptedSize(const runtime::File &file) {
199         off_t originalSize = file.size();
200         off_t result = originalSize;
201
202         if (wasEncrypted(file.getPath())) {
203                 if (originalSize > 2 * 4096) {
204                         result = originalSize - 2 * 4096;
205                 }
206         }
207
208         result = originalSize;
209
210         //TODO : block size have to not hard-coded.
211         //       If there is a better way, the followings have to be changed.
212         blksize_t blockSize = 4096;
213         if (result % blockSize) {
214                 result = (1 + result / blockSize) * blockSize;
215         }
216
217         return result;
218 }
219
220 bool isEnoughToCopyInPlace(const std::string& path,
221         const std::function<off_t(const runtime::File&)> getSizeFunc)
222 {
223         off_t availableSpace = getAvailableSpace(path);
224
225         std::function<bool(const std::string &path)> check;
226         check = [&check, &getSizeFunc, availableSpace](const std::string &path) {
227                 for (runtime::DirectoryIterator iter(path), end;
228                                 iter != end; ++iter) {
229                         if (iter->isDirectory()) {
230                                 if (!check(iter->getPath())) {
231                                         return false;
232                                 }
233                         } else if (getSizeFunc(*iter) > availableSpace) {
234                                 //TODO : have to consider changing file size
235                                 //                      when encrypt/decrypt
236                                 return false;
237                         }
238                 }
239                 return true;
240         };
241
242         return check(path);
243 }
244
245 void copyInPlace(const std::string& source, const std::string& destination,
246                                         const std::string& temp,
247                                         const std::function<bool(const std::string&)> &isTarget,
248                                         const std::function<void(off_t)> &addProgress)
249 {
250         for (runtime::DirectoryIterator iter(source), end;
251                         iter != end; ++iter) {
252                 if (iter->isDirectory()) {
253                         copyInPlace(iter->getPath(), destination + "/" + iter->getName(),
254                                                  temp, isTarget, addProgress);
255                 } else if (isTarget(iter->getPath())) {
256                         std::string tempFilePath = temp + "/" + iter->getName();
257                         std::string destFilePath = destination + "/" + iter->getName();
258
259                         iter->copyTo(tempFilePath);
260                         iter->remove();
261                         if (::rename(tempFilePath.c_str(), destFilePath.c_str()) != 0) {
262                                 throw runtime::Exception("Failed to rename from " + tempFilePath + " to " + destFilePath);
263                         }
264
265                         addProgress(iter->size());
266                 }
267         }
268 }
269
270 void ecryptfsMount(const std::string &source, const std::string &destination, const std::vector<unsigned char> &key, unsigned int options)
271 {
272         EcryptfsPayload payload(EcryptfsPayload::Type::PasswordToken);
273         std::string mountOption;
274
275         payload.token.password.flags = EcryptfsPassword::Flag::
276                                                                         SessionKeyEncryptionKeySet;
277         payload.token.password.sessionKeyEncryptionKeySize =
278                                 (ECRYPTFS_MAX_KEY_SIZE > key.size())? key.size() :
279                                                                                                                 ECRYPTFS_MAX_KEY_SIZE;
280         ::memcpy(payload.token.password.sessionKeyEncryptionKey, key.data(),
281                                 payload.token.password.sessionKeyEncryptionKeySize);
282
283     std::stringstream signature;
284     signature<< std::hex << std::setfill('0') << std::setw(2);
285     for (unsigned int byte : key) {
286         signature << byte;
287     }
288         for (int i = key.size(); i < ECRYPTFS_SIGNATURE_SIZE / 2; i++) {
289                 signature << (unsigned int) 0;
290         }
291         ::memcpy((char *)payload.token.password.signature,
292                                 signature.str().c_str(), ECRYPTFS_SIGNATURE_SIZE);
293
294         if (KernelKeyRing::search(KEY_SPEC_USER_KEYRING, ECRYPTFS_AUTH_TOKEN_TYPE,
295                                         (char *)payload.token.password.signature, 0) < 0) {
296                 if (KernelKeyRing::add(ECRYPTFS_AUTH_TOKEN_TYPE,
297                                         (char *)payload.token.password.signature,
298                                         (void *)&payload, sizeof(payload),
299                                         KEY_SPEC_USER_KEYRING) < 0) {
300                         throw runtime::Exception("Unable to add token to keyring.");
301                 }
302         }
303
304         mountOption = "ecryptfs_passthrough"
305                 ",ecryptfs_cipher=" CIPHER_MODE
306                 ",ecryptfs_sig=" + std::string((char *)payload.token.password.signature) +
307                 ",ecryptfs_key_bytes=" + std::to_string(payload.token.password.sessionKeyEncryptionKeySize);
308
309         if (options & OPTION_EXCEPT_FOR_MEDIA_FILE) {
310                 mountOption += ",ecryptfs_enable_filtering=" MEDIA_EXCLUSION_LIST;
311         }
312
313         INFO("option = " + mountOption);
314         INFO("source = " + source);
315         INFO("dest = " + destination);
316
317         if (::mount(source.c_str(), destination.c_str(), "ecryptfs", MS_NODEV,
318                                 mountOption.c_str()) != 0) {
319                 throw runtime::Exception(runtime::GetSystemErrorMessage());
320         }
321 }
322
323 void ecryptfsUmount(const std::string &destination)
324 {
325         if (::umount(destination.c_str()) != 0) {
326                 throw runtime::Exception(runtime::GetSystemErrorMessage());
327         }
328
329         //TODO : remove key from keyring
330 }
331
332 } // namespace
333
334 EcryptfsEngine::EcryptfsEngine(const std::string &src, const std::string &dest, const ProgressBar &prg) :
335         source(src), destination(dest), progress(prg)
336 {
337 }
338
339 EcryptfsEngine::~EcryptfsEngine()
340 {
341 }
342
343 void EcryptfsEngine::mount(const data &key, unsigned int options)
344 {
345         ecryptfsMount(source, destination, key, options);
346 }
347
348 void EcryptfsEngine::umount()
349 {
350         ecryptfsUmount(destination);
351 }
352
353 bool EcryptfsEngine::isMounted()
354 {
355         std::ifstream file("/proc/mounts");
356         std::string line;
357
358         while (std::getline(file, line)) {
359                 std::stringstream info(line);
360                 std::string src, dest, type;
361
362                 info >> src >> dest >> type;
363                 if (type == "ecryptfs" && src == source && dest == destination) {
364                         return true;
365                 }
366         }
367
368         return false;
369 }
370
371 void EcryptfsEngine::encrypt(const data &key, unsigned int options)
372 {
373         if (!isEnoughToCopyInPlace(source, getDecryptedSize)) {
374                 throw runtime::Exception("No space to encryption");
375         }
376
377         progress.update(0);
378
379         try {
380                 ecryptfsMount(source, destination, key, options);
381         } catch (runtime::Exception &e) {
382                 throw runtime::Exception("Failed to mount - " + std::string(e.what()));
383         }
384
385         try {
386                 off_t totalSize = getAvailableSpace(source), current;
387             runtime::File tempDir(destination + "/" ENCRYPTION_TMP_DIR);
388
389                 if (tempDir.exists()) {
390                         tempDir.remove(true);
391                 }
392                 tempDir.makeDirectory();
393
394                 if (!(options & OPTION_ONLY_NEW_FILE)) {
395                         copyInPlace(destination, destination, tempDir.getPath(),
396                                         [](const std::string &file) {
397                                                 return true;
398                                         },
399                                         [&current, &totalSize, this](off_t size) {
400                                                 current += size;
401                                                 this->progress.update(current * 100 / totalSize);
402                                         });
403                 }
404
405                 tempDir.remove(true);
406         } catch (runtime::Exception &e) {
407                 try {
408                         ecryptfsUmount(destination);
409                 } catch (runtime::Exception &e) {}
410                 throw runtime::Exception("Failed to encrypt file - " + std::string(e.what()));
411         }
412
413         sync();
414
415         progress.done();
416 }
417
418 void EcryptfsEngine::decrypt(const data &key, unsigned int options)
419 {
420         if (!isEnoughToCopyInPlace(destination, getEncryptedSize)) {
421                 throw runtime::Exception("No space to encryption");
422         }
423
424         progress.update(0);
425
426         try {
427                 off_t totalSize = getAvailableSpace(source), current;
428             runtime::File tempDir(source + "/" ENCRYPTION_TMP_DIR);
429                 runtime::File tempMountpoint(tempDir.getPath() + "/mount");
430
431                 if (tempDir.exists()) {
432                         tempDir.remove(true);
433                 }
434                 tempDir.makeDirectory();
435
436                 tempMountpoint.makeDirectory();
437                 ecryptfsMount(source, tempMountpoint.getPath(), key, 0);
438
439                 copyInPlace(tempMountpoint.getPath(), source,
440                                         tempDir.getPath(), wasEncrypted,
441                                         [&current, &totalSize, this](off_t size) {
442                                                 current += size;
443                                                 this->progress.update(current * 100 / totalSize);
444                                         });
445                 ecryptfsUmount(tempMountpoint.getPath());
446
447                 tempDir.remove(true);
448         } catch (runtime::Exception &e) {
449                 throw runtime::Exception("Failed to decrypt file - " + std::string(e.what()));
450         }
451
452         sync();
453
454         progress.done();
455 }
456
457 bool EcryptfsEngine::isKeyMetaSet()
458 {
459     return FileFooter::exist(source);
460 }
461
462 const EcryptfsEngine::data EcryptfsEngine::getKeyMeta()
463 {
464     return FileFooter::read(source);
465 }
466
467 void EcryptfsEngine::setKeyMeta(const data &meta)
468 {
469         FileFooter::write(source, meta);
470 }
471
472 void EcryptfsEngine::clearKeyMeta()
473 {
474         FileFooter::clear(source);
475 }
476
477 unsigned int EcryptfsEngine::getSupportedOptions()
478 {
479         return SUPPORTED_OPTIONS;
480 }
481
482 } // namespace ode