From 9a4610af556c8d29412358ade3011f4070faccab Mon Sep 17 00:00:00 2001 From: yeji01kim Date: Wed, 23 Nov 2016 22:26:24 +0900 Subject: [PATCH] Add Ext4 encryption engine 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 --- server/engine/ext4-engine.cpp | 387 ++++++++++++++++++++++++++++++++++- server/engine/ext4-engine.h | 1 + server/key-manager/key-generator.cpp | 9 + server/key-manager/key-generator.h | 1 + tests/CMakeLists.txt | 2 + tests/ext4-engine.cpp | 0 6 files changed, 394 insertions(+), 6 deletions(-) mode change 100644 => 100755 server/engine/ext4-engine.cpp mode change 100644 => 100755 server/engine/ext4-engine.h mode change 100644 => 100755 tests/ext4-engine.cpp diff --git a/server/engine/ext4-engine.cpp b/server/engine/ext4-engine.cpp old mode 100644 new mode 100755 index ce17296..59769d3 --- a/server/engine/ext4-engine.cpp +++ b/server/engine/ext4-engine.cpp @@ -15,11 +15,342 @@ */ #include #include +#include +#include #include "ext4-engine.h" +#include "../key-manager/key-generator.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include 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 diff --git a/server/engine/ext4-engine.h b/server/engine/ext4-engine.h old mode 100644 new mode 100755 index a90e260..661a248 --- a/server/engine/ext4-engine.h +++ b/server/engine/ext4-engine.h @@ -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); diff --git a/server/key-manager/key-generator.cpp b/server/key-manager/key-generator.cpp index dbd82d7..b772db4 100755 --- a/server/key-manager/key-generator.cpp +++ b/server/key-manager/key-generator.cpp @@ -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 diff --git a/server/key-manager/key-generator.h b/server/key-manager/key-generator.h index 42d7eb2..1653d58 100755 --- a/server/key-manager/key-generator.h +++ b/server/key-manager/key-generator.h @@ -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; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 958f61d..232d192 100755 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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}) diff --git a/tests/ext4-engine.cpp b/tests/ext4-engine.cpp old mode 100644 new mode 100755 -- 2.7.4