*/
#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)
{
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