Add Ext4 encryption engine 63/99663/7
authoryeji01kim <yeji01.kim@samsung.com>
Wed, 23 Nov 2016 13:26:24 +0000 (22:26 +0900)
committeryeji01kim <yeji01.kim@samsung.com>
Tue, 6 Dec 2016 07:37:01 +0000 (16:37 +0900)
For working ext4 engine, needs to use above kernel 4.1 and kernel config.
The current implementation only applies to "/opt/usr"

Change-Id: I6044380f464f6ff69e688427bf29a484d40f63d4
Signed-off-by: yeji01kim <yeji01.kim@samsung.com>
server/engine/ext4-engine.cpp [changed mode: 0644->0755]
server/engine/ext4-engine.h [changed mode: 0644->0755]
server/key-manager/key-generator.cpp
server/key-manager/key-generator.h
tests/CMakeLists.txt
tests/ext4-engine.cpp [changed mode: 0644->0755]

old mode 100644 (file)
new mode 100755 (executable)
index ce17296..59769d3
  */
 #include <klay/filesystem.h>
 #include <klay/audit/logger.h>
+#include <klay/error.h>
+#include <klay/exception.h>
 
 #include "ext4-engine.h"
+#include "../key-manager/key-generator.h"
+#include <iostream>
+#include <string>
+#include <string.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <linux/keyctl.h>
+#include <sys/mount.h>
+#include <sys/xattr.h>
+#include <sys/syscall.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/sendfile.h>
 
 namespace ode {
 
+#define EXT4_MAX_KEY_SIZE 64
+#define EXT4_KEY_DESCRIPTOR_SIZE 8
+#define EXT4_KEY_DESC_PREFIX_SIZE 5
+#define EXT4_KEY_REF_STR_BUF_SIZE ((EXT4_KEY_DESCRIPTOR_SIZE * 2) + 1)
+#define EXT4_IOC_SET_ENCRYPTION_POLICY  _IOR('f', 19, struct ext4_encryption_policy)
+#define EXT4_IOC_GET_ENCRYPTION_POLICY  _IOW('f', 21, struct ext4_encryption_policy)
+
+/* special process keyring shortcut IDs */
+#define KEY_SPEC_THREAD_KEYRING                -1
+#define KEY_SPEC_PROCESS_KEYRING       -2
+#define KEY_SPEC_SESSION_KEYRING       -3
+#define KEY_SPEC_USER_KEYRING          -4
+#define KEY_SPEC_USER_SESSION_KEYRING  -5
+#define KEY_SPEC_GROUP_KEYRING         -6
+
+#define KEYCTL_GET_KEYRING_ID          0
+#define KEYCTL_SEARCH 10
+
+ /* Encryption algorithms */
+#define EXT4_ENCRYPTION_MODE_INVALID            0
+#define EXT4_ENCRYPTION_MODE_AES_256_XTS        1
+#define EXT4_ENCRYPTION_MODE_AES_256_GCM        2
+#define EXT4_ENCRYPTION_MODE_AES_256_CBC        3
+#define EXT4_ENCRYPTION_MODE_AES_256_CTS        4
+
+#define SMACK_LABEL_LEN_MAX 255
+struct ext4_encryption_policy {
+       char version;
+       char contents_encryption_mode;
+       char filenames_encryption_mode;
+       char flags;
+       char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
+}__attribute__((__packed__));
+
+struct ext4_encryption_key {
+       unsigned int mode;
+       char raw[EXT4_MAX_KEY_SIZE];
+       unsigned int size;
+} __attribute__((__packed__));
+
+// for now, It only suits for "/opt/usr"
+namespace {
+       std::string secondMountPoint("/opt/usr_encrypt");
+       std::string bindMountPoint("/opt/usr_encrypt/secure");
+       std::string ext4KeyringType("logon");
+       std::string smackAccessLabel("security.SMACK64");
+}
+
+const Ext4Engine::data generateKeyDesc(const ode::Ext4Engine::data& key)
+{
+       ode::KeyGenerator KeyGen(EXT4_MAX_KEY_SIZE);
+       Ext4Engine::data keyRef1 = KeyGen.SHA512(key);
+       Ext4Engine::data keyRef2 = KeyGen.SHA512(keyRef1);
+       Ext4Engine::data ret(keyRef2.begin(), keyRef2.begin()+EXT4_KEY_DESCRIPTOR_SIZE);
+
+       return ret;
+}
+
+const Ext4Engine::data generateKeyRefStr(const Ext4Engine::data& key)
+{
+       Ext4Engine::data ret(EXT4_KEY_REF_STR_BUF_SIZE);
+       Ext4Engine::data keyDesc = generateKeyDesc(key);
+
+       int i, a;
+       unsigned char nibble;
+       for (i=0, a=0; i < EXT4_KEY_DESCRIPTOR_SIZE; i++, a+=2) {
+               nibble = (keyDesc[i] >> 4) & 0xf;
+               if (nibble <= 9) ret[a] = '0' + nibble;
+               else                     ret[a] = 'a' + nibble - 10;
+               nibble = keyDesc[i] & 0xf;
+               if (nibble <= 9) ret[a+1] = '0' + nibble;
+               else                     ret[a+1] = 'a' + nibble - 10;
+       }
+
+       ret[a] = '\0';
+
+       return ret;
+}
+
+void addKeyToKeyring(const Ext4Engine::data& key)
+{
+       struct ext4_encryption_key ext4Key;
+       Ext4Engine::data keyRefStr(EXT4_KEY_REF_STR_BUF_SIZE);
+       keyRefStr = generateKeyRefStr(key);
+       std::string keyRef(keyRefStr.begin(), keyRefStr.end()), keyRefFull;
+       int keyringId, ret;
+
+       keyringId = ::syscall(__NR_keyctl, KEYCTL_GET_KEYRING_ID, KEY_SPEC_SESSION_KEYRING, 0);
+       if (keyringId == -1)
+               throw runtime::Exception(runtime::GetSystemErrorMessage());
+
+       keyRefFull = "ext4:" + keyRef;
+       ret = ::syscall(__NR_keyctl, KEYCTL_SEARCH, keyringId, ext4KeyringType.c_str(), keyRefFull.c_str(), 0);
+       if (ret != -1) {
+               INFO("Key with descriptor already exist");
+               return;
+       }
+       ext4Key.mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
+       ::memcpy(ext4Key.raw, key.data(), EXT4_MAX_KEY_SIZE);
+       std::string ext4KeyRaw = ext4Key.raw;
+       ext4Key.size = EXT4_MAX_KEY_SIZE;
+
+       ret = ::syscall(__NR_add_key, ext4KeyringType.c_str(), keyRefFull.c_str(),
+                            (void *)&ext4Key, sizeof(ext4Key), keyringId);
+
+       if (ret == -1) {
+               throw runtime::Exception(runtime::GetSystemErrorMessage());
+       }
+}
+
+static void copySmackLabel(std::string& srcPath, std::string& destPath)
+{
+       Ext4Engine::data smackLabel(SMACK_LABEL_LEN_MAX + 1);
+
+       if (::getxattr(srcPath.c_str(), smackAccessLabel.c_str(), (unsigned char*)smackLabel.data(), SMACK_LABEL_LEN_MAX + 1) == -1)
+               throw runtime::Exception(runtime::GetSystemErrorMessage());
+
+       if (::setxattr(destPath.c_str(), smackAccessLabel.c_str(), smackLabel.data(), smackLabel.size(), 0) == -1)
+               throw runtime::Exception(runtime::GetSystemErrorMessage());
+}
+
+static int copy(std::string& src, std::string& dest)
+{
+       int readFd, writeFd, ret;
+       struct stat st;
+
+       ret = ::stat(src.c_str(), &st);
+       if (ret != 0) {
+               throw runtime::Exception(src + runtime::GetSystemErrorMessage());
+       }
+
+       readFd = ::open(src.c_str(), O_RDONLY);
+       if (readFd < 0)
+               throw runtime::Exception(src + runtime::GetSystemErrorMessage());
+
+       writeFd = ::open(dest.c_str(), O_WRONLY | O_CREAT, st.st_mode);
+       if (writeFd < 0)
+               throw runtime::Exception(dest + runtime::GetSystemErrorMessage());
+       copySmackLabel(src, dest);
+       if (::chown(dest.c_str(), st.st_uid, st.st_gid) == -1)
+               throw runtime::Exception(dest + runtime::GetSystemErrorMessage());
+       if (::sendfile(writeFd, readFd, 0, st.st_size))
+               return 1;
+
+       if (::fsync(writeFd) != 0)
+               throw runtime::Exception(dest + runtime::GetSystemErrorMessage());
+
+       if (::posix_fadvise(writeFd, 0, st.st_size, POSIX_FADV_DONTNEED) < 0)
+               throw runtime::Exception(dest + runtime::GetSystemErrorMessage());
+
+       ::close(readFd);
+       ::close(writeFd);
+       return 0;
+}
+
+static void listDir(std::string& source, std::string& dest, bool excludeFlag)
+{
+       DIR* d;
+
+       if (excludeFlag && source.compare(0, bindMountPoint.size(), bindMountPoint) == 0)
+               return;
+
+       d = ::opendir(source.c_str());
+
+       if (!d)
+               throw runtime::Exception(source + runtime::GetSystemErrorMessage());
+
+       while(1) {
+               struct dirent* entry;
+               std::string dName;
+
+               entry = ::readdir(d);
+               if (!entry) {
+                       break;
+               }
+               dName = entry->d_name;
+               if (!(entry->d_type & DT_DIR)) {
+                       std::string srcFile = source + "/" + dName;
+                       std::string destFile = dest + "/" +dName;
+                       copy(srcFile, destFile);
+
+                       if (::remove(srcFile.c_str()) != 0)
+                               ERROR("file remove failed");
+               }
+
+               if (entry->d_type & DT_DIR) {
+                       if (dName.compare(".") != 0 && dName.compare("..") != 0) {
+                               std::string pathS, pathD;
+
+                               pathS = source + "/" + dName;
+                               pathD = dest + "/" +dName;
+
+                               // make new directory
+                               int mkdirRet, statRet;
+                               struct stat st;
+                               statRet = ::stat(pathS.c_str(), &st);
+                               if (statRet != 0)
+                                       throw runtime::Exception(pathS + runtime::GetSystemErrorMessage());
+
+                               if (excludeFlag) {
+                                       if (pathS.compare(bindMountPoint) != 0) {
+                                               mkdirRet = ::mkdir(pathD.c_str(), st.st_mode);
+                                               if (mkdirRet != 0)
+                                                       throw runtime::Exception(pathS + runtime::GetSystemErrorMessage());
+                                               copySmackLabel(pathS, pathD);
+                                               if (::chown(pathD.c_str(), st.st_uid, st.st_gid) == -1)
+                                                       throw runtime::Exception(runtime::GetSystemErrorMessage());
+                                       }
+                               } else {
+                                       mkdirRet = ::mkdir(pathD.c_str(), st.st_mode);
+                                       if (mkdirRet != 0)
+                                               throw runtime::Exception(pathD + runtime::GetSystemErrorMessage());
+                                       copySmackLabel(pathS, pathD);
+                                               if (::chown(pathD.c_str(), st.st_uid, st.st_gid) == -1)
+                                                       throw runtime::Exception(pathD + runtime::GetSystemErrorMessage());
+                               }
+                               if (pathS.size() >= PATH_MAX)
+                                       throw runtime::Exception(pathS + " :path length has got too long");
+
+                               listDir(pathS, pathD, excludeFlag);
+                       }
+               }
+       }
+
+       if (::closedir(d))
+               throw runtime::Exception(runtime::GetSystemErrorMessage());
+
+       if (source.compare(secondMountPoint) != 0) {
+               if (::remove(source.c_str()) != 0)
+                       throw runtime::Exception(source + runtime::GetSystemErrorMessage());
+       }
+}
+
+static int intLog2(int arg)
+{
+       int l = 0;
+
+       arg >>= 1;
+       while(arg) {
+               l++;
+               arg >>= 1;
+       }
+
+       return l;
+}
+
+static void setPolicy(const std::string& source, const Ext4Engine::data& key)
+{
+       struct ext4_encryption_policy policy;
+       int pad = 4;
+       int fd, rc;
+       Ext4Engine::data descriptor(EXT4_KEY_DESCRIPTOR_SIZE);
+
+       descriptor = generateKeyDesc(key);
+       std::string descStr(descriptor.begin(), descriptor.end());
+
+       fd = ::open(source.c_str(), O_DIRECTORY);
+       if (fd == -1)
+               throw runtime::Exception("invalid path");
+
+       policy.version = 0;
+       policy.contents_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
+       policy.filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
+       policy.flags = intLog2(pad >> 2);
+       ::memcpy(policy.master_key_descriptor, descriptor.data(), EXT4_KEY_DESCRIPTOR_SIZE);
+
+       rc = ::ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &policy);
+       ::close(fd);
+
+       if (rc)
+               throw runtime::Exception("set policy failed :" + runtime::GetSystemErrorMessage());
+}
+
+static bool prepareEncryptDir(std::string& sourceName, std::string& destName)
+{
+       struct stat dirStat;
+       ::stat(destName.c_str(), &dirStat);
+
+       if (::mkdir(secondMountPoint.c_str(), dirStat.st_mode) != 0)
+               throw runtime::Exception(runtime::GetSystemErrorMessage());
+       copySmackLabel(destName, secondMountPoint);
+       if (::chown(secondMountPoint.c_str(), dirStat.st_uid, dirStat.st_gid) == -1)
+               throw runtime::Exception(runtime::GetSystemErrorMessage());
+       if (::mount(sourceName.c_str(), secondMountPoint.c_str(), "ext4", 0, 0) < 0) {
+               ::remove(secondMountPoint.c_str());
+               throw runtime::Exception(runtime::GetSystemErrorMessage());
+       }
+
+       if (::mkdir(bindMountPoint.c_str(), dirStat.st_mode) != 0)
+               throw runtime::Exception(runtime::GetSystemErrorMessage());
+       copySmackLabel(secondMountPoint, bindMountPoint);
+       if (::chown(bindMountPoint.c_str(), dirStat.st_uid, dirStat.st_gid) == -1)
+               throw runtime::Exception(runtime::GetSystemErrorMessage());
+       return true;
+}
+
+static bool getPolicy(const std::string& dirName)
+{
+       struct ext4_encryption_policy policy;
+       int fd, rc;
+
+       fd = ::open(dirName.c_str(), O_DIRECTORY);
+       if (fd == -1)
+               return false;
+
+       rc = ::ioctl(fd, EXT4_IOC_GET_ENCRYPTION_POLICY, &policy);
+       close(fd);
+       if (rc) {
+               ERROR("ioctl error");
+               return false;
+       }
+       return true;
+}
+
 Ext4Engine::Ext4Engine(const std::string& src, const std::string& dest) :
        source(src), destination(dest)
 {
@@ -31,26 +362,70 @@ Ext4Engine::~Ext4Engine()
 
 void Ext4Engine::mount(const Ext4Engine::data& key)
 {
-       //TODO
+       addKey(key);
+       // mount : /dev/mmcblk0p21 /opt/usr_encrypt
+       if (::mount(source.c_str(), secondMountPoint.c_str(), "ext4", 0, 0) < 0)
+               throw runtime::Exception(runtime::GetSystemErrorMessage());
+       // bind mount :/opt/usr_encrypt/secure /opt/usr
+       if (::mount(bindMountPoint.c_str(), destination.c_str(), "ext4", MS_BIND, 0) < 0)
+               throw runtime::Exception(runtime::GetSystemErrorMessage());
 }
 
 void Ext4Engine::umount()
 {
-       //TODO
+       // for decrypt, umount /opt/usr
+       if (::umount(destination.c_str()))
+               throw runtime::Exception(runtime::GetSystemErrorMessage());
 }
 
-void Ext4Engine::encrypt(const Ext4Engine::data& key)
+void Ext4Engine::addKey(const Ext4Engine::data& key)
 {
-       //TODO
+       addKeyToKeyring(key);
 }
 
+void Ext4Engine::encrypt(const Ext4Engine::data& key)
+{
+       std::string sourceDir = getSource();
+       std::string destDir = getDestination();
+       bool copyFlag = false;
+
+       if (!(copyFlag = prepareEncryptDir(sourceDir, destDir)))
+               throw runtime::Exception("prepareEncryptDir failed");
 
+       // key add to keyring
+       addKeyToKeyring(key);
+       // set policy
+       setPolicy(bindMountPoint, key);
+
+       if (copyFlag)
+               listDir(secondMountPoint, bindMountPoint, true);
+       INFO("[ext4 encrypt] copy done");
+       if (::mount(bindMountPoint.c_str(), destDir.c_str(), "ext4", MS_BIND, 0) < 0)
+               throw runtime::Exception(runtime::GetSystemErrorMessage());
+}
 
 void Ext4Engine::decrypt(const Ext4Engine::data& key)
 {
-       //TODO
-}
+       std::string destDir = getDestination();
+
+       if (!getPolicy(bindMountPoint))
+               throw runtime::Exception("directory isn't encrypted");
+       addKeyToKeyring(key);
 
+       listDir(bindMountPoint, secondMountPoint, false);
+       INFO("[ext4 decrypt] copy done");
+       if (::open(bindMountPoint.c_str(), O_RDONLY) != -1)
+               ::remove(bindMountPoint.c_str());
 
+       // umount /opt/usr_encrypt
+       if (::umount(secondMountPoint.c_str()))
+               throw runtime::Exception(runtime::GetSystemErrorMessage());
+       // mount /dev/mmcblk0p21 /opt/usr
+       if (::mount(source.c_str(), destination.c_str(), "ext4", 0, 0) < 0)
+               throw runtime::Exception(runtime::GetSystemErrorMessage());
+
+       if (::open(secondMountPoint.c_str(), O_RDONLY) != -1)
+               ::remove(secondMountPoint.c_str());
+}
 
 } // namespace ode
old mode 100644 (file)
new mode 100755 (executable)
index a90e260..661a248
@@ -47,6 +47,7 @@ public:
        void mount(const data& key);
        void umount();
 
+       void addKey(const data& key);
        void encrypt(const data& key);
        void decrypt(const data& key);
 
index dbd82d7..b772db4 100755 (executable)
@@ -143,4 +143,13 @@ const KeyGenerator::data KeyGenerator::MD5(const KeyGenerator::data& in)
        return ret;
 }
 
+const KeyGenerator::data KeyGenerator::SHA512(const KeyGenerator::data& in)
+{
+    data ret(SHA512_DIGEST_LENGTH);
+
+    ::SHA512((unsigned char*)in.data(), in.size(), (unsigned char*)ret.data());
+
+    return ret;
+}
+
 } // namespace ode
index 42d7eb2..1653d58 100755 (executable)
@@ -39,6 +39,7 @@ public:
        const data HMAC(const data& original, const data& key);
        const data RNG();
        const data MD5(const data& in);
+       const data SHA512(const data& in);
 
 private:
        int keySize;
index 958f61d..232d192 100755 (executable)
@@ -21,6 +21,7 @@ SET(TEST_SRC  main.cpp
                                ecryptfs-engine.cpp
                                ../server/engine/ext4-engine.cpp
                                ../server/engine/ecryptfs-engine.cpp
+                               ../server/key-manager/key-generator.cpp
 )
 
 ADD_EXECUTABLE(${PROJECT_NAME} ${TEST_SRC})
@@ -28,6 +29,7 @@ ADD_EXECUTABLE(${PROJECT_NAME} ${TEST_SRC})
 PKG_CHECK_MODULES(TEST_DEPS REQUIRED   klay
                                                                                glib-2.0
                                                                                gio-2.0
+                                                                               openssl
 )
 
 INCLUDE_DIRECTORIES(SYSTEM ${TEST_DEPS_INCLUDE_DIRS})
old mode 100644 (file)
new mode 100755 (executable)