Fix all mis-used types about large file support
[platform/core/security/ode.git] / server / engine / encryption / ext4-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
17 #include <string>
18 #include <iomanip>
19 #include <iostream>
20
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <sys/vfs.h>
24 #include <sys/stat.h>
25 #include <sys/mount.h>
26 #include <sys/ioctl.h>
27 #include <sys/types.h>
28 #include <sys/xattr.h>
29
30 #include <klay/error.h>
31 #include <klay/exception.h>
32 #include <klay/filesystem.h>
33 #include <klay/audit/logger.h>
34
35 #include "../../kernel-keyring.h"
36 #include "../../file-footer.h"
37 #include "../../key-manager/key-generator.h"
38
39 #include "ext4-engine.h"
40
41 #define ENCRYPTION_DIR ".encrypted"
42
43 #define SMACK_LABEL_LEN_MAX 255
44
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)
49
50 #define EXT4_KEYRING_TYPE       "logon"
51
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
58
59 struct ext4_encryption_policy {
60         char version;
61         char contents_encryption_mode;
62         char filenames_encryption_mode;
63         char flags;
64         char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
65 }__attribute__((__packed__));
66
67 struct ext4_encryption_key {
68         unsigned int mode;
69         char raw[EXT4_MAX_KEY_SIZE];
70         unsigned int size;
71 } __attribute__((__packed__));
72
73 namespace ode {
74
75 namespace {
76
77 const Ext4Engine::data generateKeyDescriptor(const Ext4Engine::data& key)
78 {
79         auto hash = KeyGenerator::SHA512(KeyGenerator::SHA512(key));
80         hash.resize(EXT4_KEY_DESCRIPTOR_SIZE);
81         return hash;
82 }
83
84 const std::string convertToHex(const Ext4Engine::data &binary)
85 {
86         std::stringstream hex;
87
88         hex << std::hex << std::setfill('0');
89         for (unsigned int byte : binary) {
90                 hex << std::setw(2) << byte;
91         }
92         return hex.str();
93 }
94
95 Ext4Engine::data sanitizeKey(const Ext4Engine::data &key)
96 {
97         Ext4Engine::data sanitized(key);
98         sanitized.resize(EXT4_MAX_KEY_SIZE);
99         return sanitized;
100 }
101
102 void addKeyToKeyring(const Ext4Engine::data& key)
103 {
104         struct ext4_encryption_key payload;
105         std::string keyDescriptor;
106         int keyringId;
107
108         keyringId = KernelKeyRing::getKeyringId(KEY_SPEC_SESSION_KEYRING, 0);
109         if (keyringId == -1) {
110                 throw runtime::Exception("Unable to get keyring id");
111         }
112
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");
117                 return;
118         }
119
120         payload.mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
121         payload.size = EXT4_MAX_KEY_SIZE;
122         ::memcpy(payload.raw, key.data(), key.size());
123
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");
128         }
129 }
130
131 int intLog2(int arg)
132 {
133         int l = 0;
134
135         arg >>= 1;
136         while(arg) {
137                 l++;
138                 arg >>= 1;
139         }
140
141         return l;
142 }
143
144 void setPolicy(const std::string& path, const Ext4Engine::data& key)
145 {
146         struct ext4_encryption_policy policy;
147         int pad = 4;
148         int fd, rc;
149
150         fd = ::open(path.c_str(), O_DIRECTORY);
151         if (fd == -1) {
152                 throw runtime::Exception("invalid path");
153         }
154
155         Ext4Engine::data descriptor = generateKeyDescriptor(key);
156
157         policy.version = 0;
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());
162
163         rc = ::ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &policy);
164         ::close(fd);
165
166         if (rc) {
167                 throw runtime::Exception("set policy failed :" + runtime::GetSystemErrorMessage());
168         }
169 }
170
171 bool hasPolicy(const std::string& dir)
172 {
173         struct ext4_encryption_policy policy;
174         int fd, rc;
175
176         fd = ::open(dir.c_str(), O_DIRECTORY);
177         if (fd == -1) {
178                 return false;
179         }
180
181         rc = ::ioctl(fd, EXT4_IOC_GET_ENCRYPTION_POLICY, &policy);
182         close(fd);
183
184         if (rc) {
185                 return false;
186         }
187
188         return true;
189 }
190
191 off_t getUsedSpace(const std::string& mountPoint)
192 {
193         struct statfs statbuf;
194         if (::statfs(mountPoint.c_str(), &statbuf)) {
195                 throw runtime::Exception("Failed to access " + mountPoint);
196         }
197
198         return (off_t)(statbuf.f_blocks - statbuf.f_bfree) * statbuf.f_bsize;
199 }
200
201 off_t getAvailableSpace(const std::string& mountPoint)
202 {
203         struct statfs statbuf;
204         if (::statfs(mountPoint.c_str(), &statbuf)) {
205                 throw runtime::Exception("Failed to access " + mountPoint);
206         }
207
208         return (off_t)statbuf.f_bfree * statbuf.f_bsize;
209 }
210
211 static void copyDac(const std::string& srcPath, const std::string& destPath)
212 {
213         runtime::File src(srcPath), dest(destPath);
214
215         try {
216                 mode_t mode = src.getMode();
217                 uid_t uid = src.getUid();
218                 gid_t gid = src.getGid();
219
220                 try {
221                         dest.chown(uid, gid);
222                 } catch (runtime::Exception &e) {}
223
224                 try {
225                         dest.chmod(mode);
226                 } catch (runtime::Exception &e) {}
227         } catch (runtime::Exception &e) {}
228 }
229
230 static void copyMac(const std::string& srcPath, const std::string& destPath)
231 {
232         char smackLabel[SMACK_LABEL_LEN_MAX + 1];
233         ssize_t labelSize;
234
235         labelSize = ::getxattr(srcPath.c_str(), "security.SMACK64", smackLabel, SMACK_LABEL_LEN_MAX + 1);
236
237         if (labelSize == -1) {
238                 return;
239         }
240
241         ::setxattr(destPath.c_str(), "security.SMACK64", smackLabel, labelSize, 0);
242 }
243
244 bool isEnoughToCopyInPlace(const std::string& path)
245 {
246         off_t availableSpace = getAvailableSpace(path);
247
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())) {
254                                         return false;
255                                 }
256                         } else if (iter->size() > availableSpace) {
257                                 return false;
258                         }
259                 }
260                 return true;
261         };
262
263         return check(path);
264 }
265
266 void copyInPlace(const std::string& source, const std::string& destination,
267                                         const std::function<void(off_t)> &addProgress)
268 {
269         for (runtime::DirectoryIterator iter(source), end; iter != end; ++iter) {
270                 if (iter->getPath() == destination) {
271                         continue;
272                 }
273
274                 runtime::File destFile(destination + "/" + iter->getName());
275                 if (iter->isDirectory()) {
276                         destFile.makeDirectory();
277                         copyInPlace(iter->getPath(), destFile.getPath(), addProgress);
278                 } else {
279                         iter->copyTo(destFile.getPath());
280                         addProgress(iter->size());
281                 }
282                 copyDac(iter->getPath(), destFile.getPath());
283                 copyMac(iter->getPath(), destFile.getPath());
284
285                 iter->remove();
286         }
287 }
288
289 } // namespace
290
291 Ext4Engine::Ext4Engine(const std::string& src, const std::string& dest, const ProgressBar &prgsBar) :
292         source(src), destination(dest), progress(prgsBar), mounted(false)
293 {
294 }
295
296 Ext4Engine::~Ext4Engine()
297 {
298 }
299
300 void Ext4Engine::mount(const Ext4Engine::data& key, unsigned int options)
301 {
302         std::string encryptedPath(destination + "/" ENCRYPTION_DIR);
303
304         addKeyToKeyring(sanitizeKey(key));
305
306         if (::mount(source.c_str(), destination.c_str(), "ext4", 0, 0) < 0) {
307                 throw runtime::Exception("Mount error - " + runtime::GetSystemErrorMessage());
308         }
309
310         if (::mount(encryptedPath.c_str(), destination.c_str(), NULL, MS_BIND, 0) < 0) {
311                 throw runtime::Exception("Mount error - " + runtime::GetSystemErrorMessage());
312         }
313
314         mounted = true;
315 }
316
317 void Ext4Engine::umount()
318 {
319         //TODO : remove Key
320
321         if (::umount(destination.c_str())) {
322                 throw runtime::Exception(runtime::GetSystemErrorMessage());
323         }
324
325         mounted = false;
326 }
327
328 bool Ext4Engine::isMounted()
329 {
330         return mounted;
331 }
332
333 void Ext4Engine::encrypt(const Ext4Engine::data& key, unsigned int options)
334 {
335         if (!isEnoughToCopyInPlace(destination)) {
336                 throw runtime::Exception("No space to encryption");
337         }
338
339         if (::mount(source.c_str(), destination.c_str(), "ext4", 0, 0) < 0) {
340                 throw runtime::Exception("Mount error - " + runtime::GetSystemErrorMessage());
341         }
342
343         Ext4Engine::data sanitizedKey = sanitizeKey(key);
344         addKeyToKeyring(sanitizedKey);
345
346         runtime::File encrypted(destination + "/" ENCRYPTION_DIR);
347         if (encrypted.exists()) {
348                 encrypted.remove(true);
349         }
350         encrypted.makeDirectory();
351         setPolicy(encrypted.getPath(), sanitizedKey);
352
353         off_t totalSize = getUsedSpace(source), current = 0;
354         copyInPlace(destination, encrypted.getPath(),
355                                 [&current, &totalSize, this](off_t size) {
356                                         current += size;
357                                         this->progress.update(current, totalSize, 1);
358                                 });
359
360         if (::mount(encrypted.getPath().c_str(), destination.c_str(), NULL, MS_BIND, 0) < 0) {
361                 throw runtime::Exception("Mount error - " + runtime::GetSystemErrorMessage());
362         }
363
364         sync();
365
366         progress.done();
367 }
368
369 void Ext4Engine::decrypt(const Ext4Engine::data& key, unsigned int options)
370 {
371         if (!isEnoughToCopyInPlace(destination)) {
372                 throw runtime::Exception("No space to encryption");
373         }
374
375         if (!hasPolicy(destination)) {
376                 throw runtime::Exception("failed get policy");
377         }
378
379         runtime::File encrypted(destination + "/" ENCRYPTION_DIR);
380
381         off_t totalSize = getUsedSpace(source), current = 0;
382         copyInPlace(encrypted.getPath(), destination,
383                                 [&current, &totalSize, this](off_t size) {
384                                         current += size;
385                                         this->progress.update(current, totalSize, 1);
386                                 });
387
388         encrypted.remove(true);
389
390         sync();
391
392         progress.done();
393 }
394
395 bool Ext4Engine::isKeyMetaSet()
396 {
397         return FileFooter::exist(source);
398 }
399
400 const Ext4Engine::data Ext4Engine::getKeyMeta()
401 {
402         return FileFooter::read(source);
403 }
404
405 void Ext4Engine::setKeyMeta(const data &data)
406 {
407         FileFooter::write(source, data);
408 }
409
410 void Ext4Engine::clearKeyMeta()
411 {
412         FileFooter::clear(source);
413 }
414
415 unsigned int Ext4Engine::getSupportedOptions()
416 {
417         return 0;
418 }
419
420 } // namespace ode