Refactor secure erase and add MMC erase engine
[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 #include <iostream>
17 #include <string>
18 #include <string.h>
19 #include <dirent.h>
20 #include <fcntl.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <linux/keyctl.h>
24 #include <sys/mount.h>
25 #include <sys/xattr.h>
26 #include <sys/syscall.h>
27 #include <sys/ioctl.h>
28 #include <sys/stat.h>
29 #include <sys/sendfile.h>
30
31 #include <klay/filesystem.h>
32 #include <klay/audit/logger.h>
33 #include <klay/error.h>
34 #include <klay/exception.h>
35
36 #include "../../file-footer.h"
37 #include "../../key-manager/key-generator.h"
38
39 #include "ext4-engine.h"
40
41 namespace ode {
42
43 #define EXT4_MAX_KEY_SIZE 64
44 #define EXT4_KEY_DESCRIPTOR_SIZE 8
45 #define EXT4_KEY_DESC_PREFIX_SIZE 5
46 #define EXT4_KEY_REF_STR_BUF_SIZE ((EXT4_KEY_DESCRIPTOR_SIZE * 2) + 1)
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 /* special process keyring shortcut IDs */
51 #define KEY_SPEC_THREAD_KEYRING         -1
52 #define KEY_SPEC_PROCESS_KEYRING        -2
53 #define KEY_SPEC_SESSION_KEYRING        -3
54 #define KEY_SPEC_USER_KEYRING           -4
55 #define KEY_SPEC_USER_SESSION_KEYRING   -5
56 #define KEY_SPEC_GROUP_KEYRING          -6
57
58 #define KEYCTL_GET_KEYRING_ID           0
59 #define KEYCTL_SEARCH 10
60
61  /* Encryption algorithms */
62 #define EXT4_ENCRYPTION_MODE_INVALID            0
63 #define EXT4_ENCRYPTION_MODE_AES_256_XTS        1
64 #define EXT4_ENCRYPTION_MODE_AES_256_GCM        2
65 #define EXT4_ENCRYPTION_MODE_AES_256_CBC        3
66 #define EXT4_ENCRYPTION_MODE_AES_256_CTS        4
67
68 #define SMACK_LABEL_LEN_MAX 255
69 struct ext4_encryption_policy {
70         char version;
71         char contents_encryption_mode;
72         char filenames_encryption_mode;
73         char flags;
74         char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
75 }__attribute__((__packed__));
76
77 struct ext4_encryption_key {
78         unsigned int mode;
79         char raw[EXT4_MAX_KEY_SIZE];
80         unsigned int size;
81 } __attribute__((__packed__));
82
83 /* for now, It only suits for "/opt/usr" */
84 namespace {
85         std::string secondMountPoint("/opt/usr_encrypt");
86         std::string bindMountPoint("/opt/usr_encrypt/secure");
87         std::string ext4KeyringType("logon");
88         std::string smackAccessLabel("security.SMACK64");
89         long long totalSize = 0;
90         long long curSize = 0;
91 }
92
93 const Ext4Engine::data generateKeyDesc(const ode::Ext4Engine::data& key)
94 {
95         Ext4Engine::data keyRef1 = ode::KeyGenerator::SHA512(key);
96         Ext4Engine::data keyRef2 = ode::KeyGenerator::SHA512(keyRef1);
97         Ext4Engine::data ret(keyRef2.begin(), keyRef2.begin()+EXT4_KEY_DESCRIPTOR_SIZE);
98
99         return ret;
100 }
101
102 const Ext4Engine::data generateKeyRefStr(const Ext4Engine::data& key)
103 {
104         Ext4Engine::data ret(EXT4_KEY_REF_STR_BUF_SIZE);
105         Ext4Engine::data keyDesc = generateKeyDesc(key);
106
107         int i, a;
108         unsigned char nibble;
109         for (i=0, a=0; i < EXT4_KEY_DESCRIPTOR_SIZE; i++, a+=2) {
110                 nibble = (keyDesc[i] >> 4) & 0xf;
111                 if (nibble <= 9) ret[a] = '0' + nibble;
112                 else                     ret[a] = 'a' + nibble - 10;
113                 nibble = keyDesc[i] & 0xf;
114                 if (nibble <= 9) ret[a+1] = '0' + nibble;
115                 else                     ret[a+1] = 'a' + nibble - 10;
116         }
117
118         ret[a] = '\0';
119
120         return ret;
121 }
122
123 void addKeyToKeyring(const Ext4Engine::data& key)
124 {
125         struct ext4_encryption_key ext4Key;
126         Ext4Engine::data keyRefStr(EXT4_KEY_REF_STR_BUF_SIZE);
127         keyRefStr = generateKeyRefStr(key);
128         std::string keyRef(keyRefStr.begin(), keyRefStr.end()), keyRefFull;
129         int keyringId, ret;
130
131         keyringId = ::syscall(__NR_keyctl, KEYCTL_GET_KEYRING_ID, KEY_SPEC_SESSION_KEYRING, 0);
132         if (keyringId == -1)
133                 throw runtime::Exception(runtime::GetSystemErrorMessage());
134
135         keyRefFull = "ext4:" + keyRef;
136         ret = ::syscall(__NR_keyctl, KEYCTL_SEARCH, keyringId, ext4KeyringType.c_str(), keyRefFull.c_str(), 0);
137         if (ret != -1) {
138                 INFO("Key with descriptor already exist");
139                 return;
140         }
141         ext4Key.mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
142         ::memcpy(ext4Key.raw, key.data(), EXT4_MAX_KEY_SIZE);
143         std::string ext4KeyRaw = ext4Key.raw;
144         ext4Key.size = EXT4_MAX_KEY_SIZE;
145
146         ret = ::syscall(__NR_add_key, ext4KeyringType.c_str(), keyRefFull.c_str(),
147                              (void *)&ext4Key, sizeof(ext4Key), keyringId);
148
149         if (ret == -1) {
150                 throw runtime::Exception(runtime::GetSystemErrorMessage());
151         }
152 }
153
154 static void copySmackLabel(std::string& srcPath, std::string& destPath)
155 {
156         Ext4Engine::data smackLabel(SMACK_LABEL_LEN_MAX + 1);
157
158         if (::getxattr(srcPath.c_str(), smackAccessLabel.c_str(), (unsigned char*)smackLabel.data(), SMACK_LABEL_LEN_MAX + 1) == -1)
159                 throw runtime::Exception(runtime::GetSystemErrorMessage());
160
161         if (::setxattr(destPath.c_str(), smackAccessLabel.c_str(), smackLabel.data(), smackLabel.size(), 0) == -1)
162                 throw runtime::Exception(runtime::GetSystemErrorMessage());
163 }
164
165 int Ext4Engine::copy(std::string& src, std::string& dest)
166 {
167         int readFd, writeFd, ret;
168         struct stat st;
169
170         ret = ::stat(src.c_str(), &st);
171         if (ret != 0) {
172                 throw runtime::Exception(src + runtime::GetSystemErrorMessage());
173         }
174
175         readFd = ::open(src.c_str(), O_RDONLY);
176         if (readFd < 0)
177                 throw runtime::Exception(src + runtime::GetSystemErrorMessage());
178
179         writeFd = ::open(dest.c_str(), O_WRONLY | O_CREAT, st.st_mode);
180         if (writeFd < 0)
181                 throw runtime::Exception(dest + runtime::GetSystemErrorMessage());
182         copySmackLabel(src, dest);
183         if (::chown(dest.c_str(), st.st_uid, st.st_gid) == -1)
184                 throw runtime::Exception(dest + runtime::GetSystemErrorMessage());
185         if (::sendfile(writeFd, readFd, 0, st.st_size) == -1)
186                 return 1;
187
188         /* progress bar update */
189         curSize += st.st_size;
190         INFO("curSize is " + std::to_string(curSize));
191         progressBar.update(curSize, totalSize, 1);
192
193         if (::fsync(writeFd) != 0)
194                 throw runtime::Exception(dest + runtime::GetSystemErrorMessage());
195
196         if (::posix_fadvise(writeFd, 0, st.st_size, POSIX_FADV_DONTNEED) < 0)
197                 throw runtime::Exception(dest + runtime::GetSystemErrorMessage());
198
199         ::close(readFd);
200         ::close(writeFd);
201         return 0;
202 }
203
204 static void preScanDir(std::string& dir)
205 {
206         DIR* d;
207
208         d = ::opendir(dir.c_str());
209
210         if (!d)
211                 throw runtime::Exception(dir + runtime::GetSystemErrorMessage());
212
213         while(1) {
214                 struct dirent* entry;
215                 std::string dName;
216
217                 entry = ::readdir(d);
218                 if (!entry) {
219                         break;
220                 }
221                 dName = entry->d_name;
222                 if (!(entry->d_type & DT_DIR)) {
223                         std::string file = dir + "/" + dName;
224                         struct stat st;
225                         int statRet;
226
227                         statRet = ::stat(file.c_str(), &st);
228                         if (statRet != 0)
229                                 throw runtime::Exception(file + runtime::GetSystemErrorMessage());
230                         INFO("[preListDir] " + file + " totalSize = " + std::to_string(totalSize) + " " +std::to_string(st.st_size));
231                         totalSize += st.st_size;
232                 }
233
234                 if (entry->d_type & DT_DIR) {
235                         if (dName.compare(".") != 0 && dName.compare("..") != 0) {
236                                 std::string path;
237
238                                 path = dir + "/" + dName;
239
240
241                                 if (path.size() >= PATH_MAX)
242                                         throw runtime::Exception(path + " :path length has got too long");
243
244                                 preScanDir(path);
245                         }
246                 }
247         }
248
249         if (::closedir(d))
250                 throw runtime::Exception(runtime::GetSystemErrorMessage());
251
252 }
253
254 void Ext4Engine::listDir(std::string& source, std::string& dest, bool excludeFlag)
255 {
256         DIR* d;
257
258         if (excludeFlag && source.compare(0, bindMountPoint.size(), bindMountPoint) == 0)
259                 return;
260
261         d = ::opendir(source.c_str());
262
263         if (!d)
264                 throw runtime::Exception(source + runtime::GetSystemErrorMessage());
265
266         while(1) {
267                 struct dirent* entry;
268                 std::string dName;
269
270                 entry = ::readdir(d);
271                 if (!entry) {
272                         break;
273                 }
274                 dName = entry->d_name;
275                 if (!(entry->d_type & DT_DIR)) {
276                         std::string srcFile = source + "/" + dName;
277                         std::string destFile = dest + "/" +dName;
278                         copy(srcFile, destFile);
279
280                         if (::remove(srcFile.c_str()) != 0)
281                                 ERROR("file remove failed");
282                 }
283
284                 if (entry->d_type & DT_DIR) {
285                         if (dName.compare(".") != 0 && dName.compare("..") != 0) {
286                                 std::string pathS, pathD;
287
288                                 pathS = source + "/" + dName;
289                                 pathD = dest + "/" +dName;
290
291                                 /* make new directory */
292                                 int mkdirRet, statRet;
293                                 struct stat st;
294                                 statRet = ::stat(pathS.c_str(), &st);
295                                 if (statRet != 0)
296                                         throw runtime::Exception(pathS + runtime::GetSystemErrorMessage());
297
298                                 if (excludeFlag) {
299                                         if (pathS.compare(bindMountPoint) != 0) {
300                                                 mkdirRet = ::mkdir(pathD.c_str(), st.st_mode);
301                                                 if (mkdirRet != 0)
302                                                         throw runtime::Exception(pathS + runtime::GetSystemErrorMessage());
303                                                 copySmackLabel(pathS, pathD);
304                                                 if (::chown(pathD.c_str(), st.st_uid, st.st_gid) == -1)
305                                                         throw runtime::Exception(runtime::GetSystemErrorMessage());
306                                         }
307                                 } else {
308                                         mkdirRet = ::mkdir(pathD.c_str(), st.st_mode);
309                                         if (mkdirRet != 0)
310                                                 throw runtime::Exception(pathD + runtime::GetSystemErrorMessage());
311                                         copySmackLabel(pathS, pathD);
312                                                 if (::chown(pathD.c_str(), st.st_uid, st.st_gid) == -1)
313                                                         throw runtime::Exception(pathD + runtime::GetSystemErrorMessage());
314                                 }
315                                 if (pathS.size() >= PATH_MAX)
316                                         throw runtime::Exception(pathS + " :path length has got too long");
317
318                                 listDir(pathS, pathD, excludeFlag);
319                         }
320                 }
321         }
322
323         if (::closedir(d))
324                 throw runtime::Exception(runtime::GetSystemErrorMessage());
325
326         if (source.compare(secondMountPoint) != 0) {
327                 if (::remove(source.c_str()) != 0)
328                         throw runtime::Exception(source + runtime::GetSystemErrorMessage());
329         }
330 }
331
332 static int intLog2(int arg)
333 {
334         int l = 0;
335
336         arg >>= 1;
337         while(arg) {
338                 l++;
339                 arg >>= 1;
340         }
341
342         return l;
343 }
344
345 static void setPolicy(const std::string& source, const Ext4Engine::data& key)
346 {
347         struct ext4_encryption_policy policy;
348         int pad = 4;
349         int fd, rc;
350         Ext4Engine::data descriptor(EXT4_KEY_DESCRIPTOR_SIZE);
351
352         descriptor = generateKeyDesc(key);
353         std::string descStr(descriptor.begin(), descriptor.end());
354
355         fd = ::open(source.c_str(), O_DIRECTORY);
356         if (fd == -1)
357                 throw runtime::Exception("invalid path");
358
359         policy.version = 0;
360         policy.contents_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
361         policy.filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
362         policy.flags = intLog2(pad >> 2);
363         ::memcpy(policy.master_key_descriptor, descriptor.data(), EXT4_KEY_DESCRIPTOR_SIZE);
364
365         rc = ::ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &policy);
366         ::close(fd);
367
368         if (rc)
369                 throw runtime::Exception("set policy failed :" + runtime::GetSystemErrorMessage());
370 }
371
372 static bool prepareEncryptDir(std::string& sourceName, std::string& destName)
373 {
374         struct stat dirStat;
375         ::stat(destName.c_str(), &dirStat);
376
377         if (::mkdir(secondMountPoint.c_str(), dirStat.st_mode) != 0)
378                 throw runtime::Exception(runtime::GetSystemErrorMessage());
379         copySmackLabel(destName, secondMountPoint);
380         if (::chown(secondMountPoint.c_str(), dirStat.st_uid, dirStat.st_gid) == -1)
381                 throw runtime::Exception(runtime::GetSystemErrorMessage());
382         if (::mount(sourceName.c_str(), secondMountPoint.c_str(), "ext4", 0, 0) < 0) {
383                 ::remove(secondMountPoint.c_str());
384                 throw runtime::Exception(runtime::GetSystemErrorMessage());
385         }
386
387         if (::mkdir(bindMountPoint.c_str(), dirStat.st_mode) != 0)
388                 throw runtime::Exception(runtime::GetSystemErrorMessage());
389         copySmackLabel(secondMountPoint, bindMountPoint);
390         if (::chown(bindMountPoint.c_str(), dirStat.st_uid, dirStat.st_gid) == -1)
391                 throw runtime::Exception(runtime::GetSystemErrorMessage());
392         return true;
393 }
394
395 static bool getPolicy(const std::string& dirName)
396 {
397         struct ext4_encryption_policy policy;
398         int fd, rc;
399
400         fd = ::open(dirName.c_str(), O_DIRECTORY);
401         if (fd == -1)
402                 return false;
403
404         rc = ::ioctl(fd, EXT4_IOC_GET_ENCRYPTION_POLICY, &policy);
405         close(fd);
406         if (rc) {
407                 ERROR("ioctl error");
408                 return false;
409         }
410         return true;
411 }
412
413 Ext4Engine::Ext4Engine(const std::string& src, const std::string& dest, const ProgressBar &prgsBar) :
414         source(src), destination(dest), progressBar(prgsBar)
415 {
416 }
417
418 Ext4Engine::~Ext4Engine()
419 {
420 }
421
422 void Ext4Engine::mount(const Ext4Engine::data& key, unsigned int options)
423 {
424         addKey(key);
425         /* mount : /dev/mmcblk0p21 /opt/usr_encrypt */
426         if (::mount(source.c_str(), secondMountPoint.c_str(), "ext4", 0, 0) < 0)
427                 throw runtime::Exception(runtime::GetSystemErrorMessage());
428         /* bind mount :/opt/usr_encrypt/secure /opt/usr */
429         if (::mount(bindMountPoint.c_str(), destination.c_str(), "ext4", MS_BIND, 0) < 0)
430                 throw runtime::Exception(runtime::GetSystemErrorMessage());
431 }
432
433 void Ext4Engine::umount()
434 {
435         /* for decrypt, umount /opt/usr */
436         if (::umount(destination.c_str()))
437                 throw runtime::Exception(runtime::GetSystemErrorMessage());
438 }
439
440 void Ext4Engine::addKey(const Ext4Engine::data& key)
441 {
442         addKeyToKeyring(key);
443 }
444
445 void Ext4Engine::encrypt(const Ext4Engine::data& key, unsigned int options)
446 {
447         std::string sourceDir = getSource();
448         std::string destDir = getDestination();
449         bool copyFlag = false;
450
451         if (!(copyFlag = prepareEncryptDir(sourceDir, destDir)))
452                 throw runtime::Exception("prepareEncryptDir failed");
453
454         preScanDir(secondMountPoint);
455         /* key add to keyring */
456         addKeyToKeyring(key);
457         /* set policy */
458         setPolicy(bindMountPoint, key);
459
460         if (copyFlag)
461                 listDir(secondMountPoint, bindMountPoint, true);
462         INFO("[ext4 encrypt] copy done");
463
464         progressBar.done();
465         if (::mount(bindMountPoint.c_str(), destDir.c_str(), "ext4", MS_BIND, 0) < 0)
466                 throw runtime::Exception(runtime::GetSystemErrorMessage());
467 }
468
469 void Ext4Engine::decrypt(const Ext4Engine::data& key, unsigned int options)
470 {
471         std::string destDir = getDestination();
472
473         if (!getPolicy(bindMountPoint))
474                 throw runtime::Exception("directory isn't encrypted");
475         addKeyToKeyring(key);
476
477         preScanDir(bindMountPoint);
478
479         listDir(bindMountPoint, secondMountPoint, false);
480         INFO("[ext4 decrypt] copy done");
481         progressBar.done();
482
483         if (::open(bindMountPoint.c_str(), O_RDONLY) != -1)
484                 ::remove(bindMountPoint.c_str());
485
486         /* umount /opt/usr_encrypt */
487         if (::umount(secondMountPoint.c_str()))
488                 throw runtime::Exception(runtime::GetSystemErrorMessage());
489         /* mount /dev/mmcblk0p21 /opt/usr */
490         if (::mount(source.c_str(), destination.c_str(), "ext4", 0, 0) < 0)
491                 throw runtime::Exception(runtime::GetSystemErrorMessage());
492
493         if (::open(secondMountPoint.c_str(), O_RDONLY) != -1)
494                 ::remove(secondMountPoint.c_str());
495 }
496
497 bool Ext4Engine::isKeyMetaSet()
498 {
499         return FileFooter::exist(source);
500 }
501
502 const Ext4Engine::data Ext4Engine::getKeyMeta()
503 {
504         return FileFooter::read(source);
505 }
506
507 void Ext4Engine::setKeyMeta(const data &data)
508 {
509         FileFooter::write(source, data);
510 }
511
512 void Ext4Engine::clearKeyMeta()
513 {
514         FileFooter::clear(source);
515 }
516
517 unsigned int Ext4Engine::getSupportedOptions()
518 {
519         return 0;
520 }
521
522 } // namespace ode