Add APIs to managing password
[platform/core/security/ode.git] / server / engine / 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
18 #include <klay/error.h>
19 #include <klay/exception.h>
20 #include <klay/filesystem.h>
21 #include <klay/audit/logger.h>
22
23 #include "../kernel-keyring.h"
24 #include "../file-footer.h"
25
26 #include "ecryptfs-engine.h"
27
28 #define OPTION_ONLY_NEW_FILE                    (1 << 0)
29 #define OPTION_EXCEPT_FOR_MEDIA_FILE    (1 << 1)
30
31 #define SUPPORTED_OPTIONS OPTION_ONLY_NEW_FILE
32
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"
34
35 #define CIPHER_MODE "aes"
36 #define ENCRYPTION_CHECKER_NAME ".ecryptfs_encrypted"
37
38
39 #define ECRYPTFS_VERSION_MAJOR 0x00
40 #define ECRYPTFS_VERSION_MINOR 0x04
41 #define ECRYPTFS_VERSION ((ECRYPTFS_VERSION_MAJOR << 8) | ECRYPTFS_VERSION_MINOR)
42
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
50
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
56     int32_t flags;
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];
61 };
62
63 struct ecryptfs_password {
64     int32_t password_bytes;
65     int32_t hash_algo;
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
70     uint32_t flags;
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];
74 };
75
76 struct ecryptfs_private_key {
77     uint32_t key_size;
78     uint32_t data_len;
79     uint8_t signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
80     char key_mod_alias[ECRYPTFS_MAX_KEY_MOD_NAME_BYTES + 1];
81     uint8_t data[];
82 };
83
84 enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY};
85
86 struct ecryptfs_auth_tok {
87     uint16_t version;
88     uint16_t token_type;
89 #define ECRYPTFS_ENCRYPT_ONLY 0x00000001
90     uint32_t flags;
91     struct ecryptfs_session_key session_key;
92     uint8_t reserved[32];
93     union {
94         struct ecryptfs_password password;
95         struct ecryptfs_private_key private_key;
96     } token;
97 } __attribute__((packed));
98
99 #if 0
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
103 #endif
104
105 #define ECRYPTFS_AUTH_TOKEN_TYPE "user"
106
107 namespace ode {
108
109 namespace {
110
111 unsigned long long getAvailableSpace(const std::string& mountPoint)
112 {
113         struct statfs statbuf;
114         if (::statfs(mountPoint.c_str(), &statbuf)) {
115                 throw runtime::Exception("Failed to access " + mountPoint);
116         }
117
118         return (unsigned long long)statbuf.f_bfree * statbuf.f_bsize;
119 }
120
121 bool wasEncrypted(const std::string &path)
122 {
123 #ifdef ECRYPTFS_IOCTL_GET_ATTRIBUTES
124         unsigned int attrs = 0;
125         bool ret = false;
126         int fd = 0;
127
128         fd = ::open(path.c_str(), O_RDWR);
129         if (fd < 0) {
130                 throw runtime::Exception("Failed to open " + path);
131         }
132
133         if (::ioctl(fd, ECRYPTFS_IOCTL_GET_ATTRIBUTES, &attrs) == 0 &&
134                 (attrs & ECRYPTFS_WAS_ENCRYPTED)) {
135                 ret = true;
136         }
137         ::close(fd);
138
139         return ret;
140 #else
141         return true;
142 #endif
143 }
144
145 unsigned long long getEncryptedSize(const runtime::File &file) {
146         unsigned long long originalSize = file.size();
147         unsigned long long result = 0;
148
149         if (originalSize % 4096) {
150                 originalSize = (1 + originalSize / 4096) * 4096;
151         }
152
153 #ifdef ECRYPTFS_IOCTL_GET_ATTRIBUTES
154         if (wasEncrypted(file.getPath())) {
155                 result = originalSize;
156         } else {
157 #endif
158                 result = originalSize + 2 * 4096;
159 #ifdef ECRYPTFS_IOCTL_GET_ATTRIBUTES
160         }
161 #endif
162
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;
168         }
169
170         return result;
171 }
172
173 unsigned long long getDecryptedSize(const runtime::File &file) {
174         unsigned long long originalSize = file.size();
175         unsigned long long result = originalSize;
176
177         if (wasEncrypted(file.getPath())) {
178                 if (originalSize > 2 * 4096) {
179                         result = originalSize - 2 * 4096;
180                 }
181         }
182
183         result = originalSize;
184
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;
190         }
191
192         return result;
193 }
194
195 bool isEnoughToCopyInPlace(const std::string& path,
196         const std::function<unsigned long long(const runtime::File&)> getSizeFunc)
197 {
198         unsigned long long availableSpace = getAvailableSpace(path);
199
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())) {
206                                         return false;
207                                 }
208                         } else if (getSizeFunc(*iter) > availableSpace) {
209                                 //TODO : have to consider changing file size
210                                 //                      when encrypt/decrypt
211                                 return false;
212                         }
213                 }
214                 return true;
215         };
216
217         return check(path);
218 }
219
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)
224 {
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();
233
234                         iter->copyTo(tempFilePath);
235                         iter->remove();
236                         if (::rename(tempFilePath.c_str(), destFilePath.c_str()) != 0) {
237                                 throw runtime::Exception("Failed to rename from " + tempFilePath + " to " + destFilePath);
238                         }
239
240                         addProgress(iter->size());
241                 }
242         }
243 }
244
245 void ecryptfsMount(const std::string &source, const std::string &destination, const std::vector<unsigned char> &key, unsigned int options)
246 {
247         ecryptfs_auth_tok payload;
248         std::string mountOption;
249
250         ::memset(&(payload), 0, sizeof(ecryptfs_auth_tok));
251
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);
260
261     std::stringstream signature;
262     signature<< std::hex << std::setfill('0') << std::setw(2);
263     for (unsigned int byte : key) {
264         signature << byte;
265     }
266         for (int i = key.size(); i < ECRYPTFS_SIG_SIZE; i++) {
267                 signature << (unsigned int) 0;
268         }
269         ::memcpy((char *)payload.token.password.signature,
270                                 signature.str().c_str(), ECRYPTFS_PASSWORD_SIG_SIZE);
271
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.");
279                 }
280         }
281
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);
286
287         if (options & OPTION_EXCEPT_FOR_MEDIA_FILE) {
288                 mountOption += ",ecryptfs_enable_filtering=" MEDIA_EXCLUSION_LIST;
289         }
290
291         INFO("option = " + mountOption);
292         INFO("source = " + source);
293         INFO("dest = " + destination);
294
295         if (::mount(source.c_str(), destination.c_str(), "ecryptfs", MS_NODEV,
296                                 mountOption.c_str()) != 0) {
297                 throw runtime::Exception(runtime::GetSystemErrorMessage());
298         }
299 }
300
301 void ecryptfsUmount(const std::string &destination)
302 {
303         if (::umount(destination.c_str()) != 0) {
304                 throw runtime::Exception(runtime::GetSystemErrorMessage());
305         }
306
307         //TODO : remove key from keyring
308 }
309
310 } // namespace
311
312 EcryptfsEngine::EcryptfsEngine(const std::string &src, const std::string &dest, const ProgressBar &prg) :
313         source(src), destination(dest), progress(prg)
314 {
315 }
316
317 EcryptfsEngine::~EcryptfsEngine()
318 {
319 }
320
321 void EcryptfsEngine::mount(const data &key, unsigned int options)
322 {
323         ecryptfsMount(source, destination, key, options);
324 }
325
326 void EcryptfsEngine::umount()
327 {
328         ecryptfsUmount(destination);
329 }
330
331 void EcryptfsEngine::encrypt(const data &key, unsigned int options)
332 {
333         if (!isEnoughToCopyInPlace(source, getDecryptedSize)) {
334                 throw runtime::Exception("No space to encryption");
335         }
336
337         progress.update(0);
338
339         try {
340                 ecryptfsMount(source, destination, key, options);
341         } catch (runtime::Exception &e) {
342                 throw runtime::Exception("Failed to mount - " + std::string(e.what()));
343         }
344
345         try {
346                 unsigned long long totalSize = getAvailableSpace(source), current;
347             runtime::File tempDir(destination + "/" ENCRYPTION_CHECKER_NAME);
348
349                 tempDir.makeDirectory();
350                 if (!(options & OPTION_ONLY_NEW_FILE)) {
351                         copyInPlace(destination, destination, tempDir.getPath(),
352                                         [](const std::string &file) {
353                                                 return true;
354                                         },
355                                         [&current, &totalSize, this](unsigned long long size) {
356                                                 current += size;
357                                                 this->progress.update(current * 100 / totalSize);
358                                         });
359                 }
360         } catch (runtime::Exception &e) {
361                 try {
362                         ecryptfsUmount(destination);
363                 } catch (runtime::Exception &e) {}
364                 throw runtime::Exception("Failed to encrypt file - " + std::string(e.what()));
365         }
366
367         sync();
368
369         progress.done();
370 }
371
372 void EcryptfsEngine::decrypt(const data &key, unsigned int options)
373 {
374         if (!isEnoughToCopyInPlace(destination, getEncryptedSize)) {
375                 throw runtime::Exception("No space to encryption");
376         }
377
378         progress.update(0);
379
380         try {
381                 unsigned long long totalSize = getAvailableSpace(source), current;
382             runtime::File tempDir(source + "/" ENCRYPTION_CHECKER_NAME);
383                 runtime::File tempMountpoint(tempDir.getPath() + "/mount");
384
385                 tempMountpoint.makeDirectory();
386                 ecryptfsMount(source, tempMountpoint.getPath(), key, 0);
387
388                 copyInPlace(tempMountpoint.getPath(), source,
389                                         tempDir.getPath(), wasEncrypted,
390                                         [&current, &totalSize, this](unsigned long long size) {
391                                                 current += size;
392                                                 this->progress.update(current * 100 / totalSize);
393                                         });
394                 ecryptfsUmount(tempMountpoint.getPath());
395
396                 tempDir.remove(true);
397         } catch (runtime::Exception &e) {
398                 throw runtime::Exception("Failed to decrypt file - " + std::string(e.what()));
399         }
400
401         sync();
402
403         progress.done();
404 }
405
406 bool EcryptfsEngine::isKeyMetaCreated()
407 {
408     return FileFooter::exist(source);
409 }
410
411 const EcryptfsEngine::data EcryptfsEngine::getKeyMeta()
412 {
413     return FileFooter::read(source);
414 }
415
416 void EcryptfsEngine::setKeyMeta(const data &meta)
417 {
418         FileFooter::write(source, meta);
419 }
420
421 void EcryptfsEngine::clearKeyMeta()
422 {
423         FileFooter::clear(source);
424 }
425
426 unsigned int EcryptfsEngine::getSupportedOptions()
427 {
428         return SUPPORTED_OPTIONS;
429 }
430
431 } // namespace ode