Fix static analysis issue
[platform/core/security/ode.git] / server / internal-encryption.cpp
index 36216cd..58edac5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *  Copyright (c) 2015-2023 Samsung Electronics Co., Ltd All Rights Reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
  */
 #include <set>
 #include <algorithm>
+#include <memory>
+#include <mutex>
+#include <condition_variable>
+#include <list>
 
+#include <fstream>
 #include <fcntl.h>
 #include <signal.h>
 #include <unistd.h>
 #include <sys/mount.h>
 #include <sys/reboot.h>
-#include <sys/inotify.h>
+#include <sys/wait.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
 
 #include <vconf.h>
 #include <tzplatform_config.h>
+#include <device/power.h>
 #include <klay/process.h>
 #include <klay/file-user.h>
 #include <klay/filesystem.h>
 #include <klay/dbus/connection.h>
+#include <klay/dbus/signal.h>
+#include <klay/error.h>
+#include <systemd/sd-bus.h>
 
-#include "vconf.h"
+#include "misc.h"
 #include "logger.h"
 #include "progress-bar.h"
-#include "engine/encryption/dmcrypt-engine.h"
-#include "key-manager/key-manager.h"
-
-#include "rmi/internal-encryption.h"
-
-#define INTERNAL_ENGINE        DMCryptEngine
-#define INTERNAL_DEV_PATH      "/dev/disk/by-partlabel"
-#define INTERNAL_DEV_NAME      "USER"
-#define INTERNAL_PATH          "/opt/usr"
-#define INTERNAL_STATE_VCONF_KEY                                       VCONFKEY_ODE_CRYPTO_STATE
-#define INTERNAL_OPTION_ONLY_USED_REGION_VCONF_KEY     VCONFKEY_ODE_FAST_ENCRYPTION
-
-#define PRIVILEGE_PLATFORM "http://tizen.org/privilege/internal/default/platform"
-
-const std::string PROG_FACTORY_RESET = "/usr/bin/dbus-send";
-const std::vector<std::string> wipeCommand = {
-    PROG_FACTORY_RESET,
-    "--system",
-    "--type=signal",
-    "--print-reply",
-    "--dest=com.samsung.factoryreset",
-    "/com/samsung/factoryreset",
-    "com.samsung.factoryreset.start.setting"
-};
+#include "rmi/common.h"
+
+#include "ext4-tool.h"
+#include "internal-encryption.h"
+#include "internal-encryption-common.h"
+#include "upgrade-support.h"
+#include "file-footer.h"
 
 namespace ode {
 
 namespace {
 
-std::unique_ptr<INTERNAL_ENGINE> engine;
-KeyManager::data mountKey;
+const char *PRIVILEGE_PLATFORM = "http://tizen.org/privilege/internal/default/platform";
+const mode_t MODE_0640 = S_IRUSR | S_IWUSR | S_IRGRP;
+const std::string ODE_OBJECT_PATH = "/Org/Tizen/OnDeviceEncryption";
+const std::string ODE_INTERFACE_EVENT = "org.tizen.OnDeviceEncryption.Event";
+const std::string ODE_SIGNAL_NAME = "unmount";
+const std::string manifest =
+       "<node>"
+       "       <interface name='" + ODE_INTERFACE_EVENT + "'>"
+       "       <signal name='" + ODE_SIGNAL_NAME + "'>"
+       "       </signal>"
+       "       </interface>"
+       "</node>";
+
+// watches systemd jobs
+class JobWatch {
+public:
+       explicit JobWatch(dbus::Connection& systemDBus);
+       ~JobWatch();
+
+       bool waitForJob(const std::string& job);
+
+private:
+       void jobRemoved(const dbus::Variant& parameters);
+
+       struct Job {
+               Job(uint32_t id,
+                       const std::string& job,
+                       const std::string& unit,
+                       const std::string& result) : id(id), job(job), unit(unit), result(result) {}
+               uint32_t id;
+               std::string job;
+               std::string unit;
+               std::string result;
+       };
 
-void stopDependedSystemdServices()
-{
-       runtime::File fileToTouch("/run/.ode-umount-internal");
-       try {
-               fileToTouch.remove();
-       } catch(runtime::Exception &e) {}
-       fileToTouch.create(O_WRONLY);
+       dbus::Connection::SubscriptionId id;
+       dbus::Connection& systemDBus;
+       std::list<Job> removedJobs;
+       std::mutex jobsMutex;
+       std::condition_variable jobsCv;
+};
 
-       sleep(1);
+JobWatch::JobWatch(dbus::Connection& systemDBus) : systemDBus(systemDBus) {
+       auto callback = [this](dbus::Variant parameters) {
+               this->jobRemoved(parameters);
+       };
+
+       id = systemDBus.subscribeSignal("",
+                                                                       "/org/freedesktop/systemd1",
+                                                                       "org.freedesktop.systemd1.Manager",
+                                                                       "JobRemoved",
+                                                                       callback);
 }
 
-void killDependedProcesses()
-{
-       INFO(SINK, "killDependedProcesses");
-    for (pid_t pid : runtime::FileUser::getList(INTERNAL_PATH, true)) {
-        INFO(SINK, "Close process - " + std::to_string(pid));
-               int ret = ::kill(pid, SIGKILL);
-               if (ret != 0) {
-                       ERROR(SINK, "Failed to kill process " + std::to_string(pid));
+JobWatch::~JobWatch() {
+       systemDBus.unsubscribeSignal(id);
+}
+
+bool JobWatch::waitForJob(const std::string& job) {
+       while(true) {
+               std::unique_lock<std::mutex> lock(jobsMutex);
+               bool timeout = true;
+               auto sec = std::chrono::seconds(1);
+               jobsCv.wait_for(lock, 5*sec, [this, &timeout]{
+                               if (!removedJobs.empty()) {
+                                       timeout = false;
+                                       return true;
+                               }
+                               return false;});
+
+               if (timeout) {
+                       ERROR(SINK, "job: " + job + ", result: time out");
+                       return false;
+               }
+
+               while(!removedJobs.empty()) {
+                       bool match = (removedJobs.front().job == job);
+                       bool done = (removedJobs.front().result == "done");
+                       removedJobs.pop_front();
+                       if (match)
+                               return done;
                }
-    }
+       };
+}
+
+void JobWatch::jobRemoved(const dbus::Variant& parameters)
+{
+       uint32_t id;
+       const char* job;
+       const char* unit;
+       const char* result;
+       parameters.get("(uoss)", &id, &job, &unit, &result);
+       WARN(SINK, "id:" + std::to_string(id) + " job:" + job + " unit:" + unit + " result:" + result);
+
+       {
+               std::lock_guard<std::mutex> guard(jobsMutex);
+               removedJobs.emplace_back(id, job, unit, result);
+       }
+       jobsCv.notify_one();
 }
 
-void showProgressUI(const std::string type) {
-       runtime::File fileToTouch("/run/.ode-progress-internal@" + type);
+void stopSystemdUnits()
+{
+       dbus::Connection& systemDBus = dbus::Connection::getSystem();
+       dbus::VariantIterator iter;
+       std::vector<std::string> preprocessUnits;
+       std::set<std::string> unitsToStop;
+
        try {
-               fileToTouch.remove();
-       } catch(runtime::Exception &e) {}
-       fileToTouch.create(O_WRONLY);
+               systemDBus.emitSignal("",
+                                               ODE_OBJECT_PATH,
+                                               ODE_INTERFACE_EVENT,
+                                               ODE_SIGNAL_NAME, "()", nullptr);
+       } catch (runtime::Exception &e) {
+               ERROR(SINK, "Failed to emit signal : " + std::string(e.what()));
+       }
+
+       auto stopUnit = [&systemDBus](const std::string &unit) {
+               JobWatch watch(systemDBus);
+               WARN(SINK, "Stopping unit: " + unit);
+               const char* job = NULL;
+               try {
+                       systemDBus.methodcall("org.freedesktop.systemd1",
+                                                                       "/org/freedesktop/systemd1",
+                                                                       "org.freedesktop.systemd1.Manager",
+                                                                       "StopUnit",
+                                                                       -1, "(o)", "(ss)", unit.c_str(), "flush").get("(o)", &job);
+                       WARN(SINK, "Waiting for job: " + std::string(job));
+                       if (!watch.waitForJob(job))
+                               ERROR(SINK, "Stopping unit: " + unit + " failed");
+               } catch (runtime::Exception &e) {
+                       ERROR(SINK, std::string(e.what()));
+               }
+       };
+
+       try {
+               systemDBus.methodcall("org.freedesktop.systemd1",
+                                                               "/org/freedesktop/systemd1",
+                                                               "org.freedesktop.systemd1.Manager",
+                                                               "ListUnits",
+                                                               -1, "(a(ssssssouso))", "").get("(a(ssssssouso))", &iter);
+       } catch (runtime::Exception &e) {
+               ERROR(SINK, "Get list of systemd unit : " + std::string(e.what()));
+       }
+
+       while (1) {
+               unsigned int dataUnit;
+               char *dataStr[9];
+               int ret;
+               ret = iter.get("(ssssssouso)", dataStr, dataStr + 1, dataStr + 2,
+                                               dataStr + 3, dataStr + 4, dataStr + 5,
+                                               dataStr + 6, &dataUnit, dataStr + 7, dataStr + 8);
+               if (!ret)
+                       break;
+
+               std::string unitName(dataStr[0]);
+               if (unitName == "security-manager.socket" ||
+                               unitName == "connman.socket" ||
+                               unitName == "msg-server.socket") {
+                       preprocessUnits.insert(preprocessUnits.begin(), unitName);
+               } else if (unitName.compare(0, 5, "user@") == 0 ||
+                               unitName == "tlm.service" ||
+                               unitName == "resourced.service" ||
+                               unitName == "security-manager.service") {
+                       preprocessUnits.push_back(unitName);
+               }
+       }
+
+       for (auto &unit : preprocessUnits) {
+               stopUnit(unit);
+       }
+
+       for (pid_t pid : runtime::FileUser::getList(INTERNAL_PATH, true)) {
+               auto pgid = ::getpgid(pid);
+               if (pgid > 0 && pid != pgid) {
+                       WARN(SINK, "PGID doesn't match - pgid : " + std::to_string(pgid) + ", pid : " + std::to_string(pid));
+                       continue;
+               }
+
+               try {
+                       char *unit = nullptr;
+                       systemDBus.methodcall("org.freedesktop.systemd1",
+                                                                       "/org/freedesktop/systemd1",
+                                                                       "org.freedesktop.systemd1.Manager",
+                                                                       "GetUnitByPID",
+                                                                       -1, "(o)", "(u)", (unsigned int)pid)
+                                                                               .get("(o)", &unit);
+
+                       char* tmp  = NULL;
+                       int err = sd_bus_path_decode(unit, "/org/freedesktop/systemd1/unit", &tmp);
+                       if (err <= 0 || tmp == NULL) {
+                               ERROR(SINK, "Failed to decode unit name: " + std::string(unit) + " error: " +
+                                           std::to_string(err));
+                               continue;
+                       }
+                       std::string unescapedName(tmp);
+                       free(tmp);
+
+                       unitsToStop.insert(unescapedName);
+               } catch (runtime::Exception &e) {
+                       ERROR(SINK, "Killing process: " + std::to_string(pid));
+                       ::kill(pid, SIGKILL);
+               }
+       }
+
+       for (auto unit : unitsToStop) {
+               stopUnit(unit);
+       }
+}
+
+void showProgressUI(const std::string type)
+{
+       dbus::Connection& systemDBus = dbus::Connection::getSystem();
+       std::string unit("ode-progress-ui@"+type+".service");
+
+       JobWatch watch(systemDBus);
+       INFO(SINK, "Start unit: " + unit);
+
+       const char* job = NULL;
+       systemDBus.methodcall("org.freedesktop.systemd1",
+                                                       "/org/freedesktop/systemd1",
+                                                       "org.freedesktop.systemd1.Manager",
+                                                       "StartUnit",
+                                                       -1, "(o)", "(ss)", unit.c_str(), "replace").get("(o)", &job);
+
+       INFO(SINK, "Waiting for job: " + std::string(job));
+       if (!watch.waitForJob(job))
+               ERROR(SINK, "Starting unit: " + unit + " failed");
 }
 
 unsigned int getOptions()
@@ -102,7 +293,7 @@ unsigned int getOptions()
        int value;
 
        value = 0;
-       ::vconf_get_bool(INTERNAL_OPTION_ONLY_USED_REGION_VCONF_KEY, &value);
+       ::vconf_get_bool(VCONFKEY_ODE_FAST_ENCRYPTION, &value);
        if (value) {
                result |= InternalEncryption::Option::IncludeUnusedRegion;
        }
@@ -119,322 +310,595 @@ void setOptions(unsigned int options)
        } else {
                value = false;
        }
-       ::vconf_set_bool(INTERNAL_OPTION_ONLY_USED_REGION_VCONF_KEY, value);
+       ::vconf_set_bool(VCONFKEY_ODE_FAST_ENCRYPTION, value);
 }
 
-}
-
-InternalEncryption::InternalEncryption(ODEControlContext& ctx) :
-       context(ctx)
+void killProcesses(std::vector<const char*> &args)
 {
-       context.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryption::setMountPassword)(std::string));
-       context.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryption::mount)());
-       context.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryption::umount)());
-       context.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryption::encrypt)(std::string, unsigned int));
-       context.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryption::decrypt)(std::string));
-       context.expose(this, "", (int)(InternalEncryption::isPasswordInitialized)());
-       context.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryption::initPassword)(std::string));
-       context.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryption::cleanPassword)(std::string));
-       context.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryption::changePassword)(std::string, std::string));
-       context.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryption::verifyPassword)(std::string));
-       context.expose(this, "", (int)(InternalEncryption::getState)());
-       context.expose(this, "", (unsigned int)(InternalEncryption::getSupportedOptions)());
-
-       context.createNotification("InternalEncryption::mount");
-
-       std::string source = INTERNAL_DEV_PATH "/" INTERNAL_DEV_NAME;
+       int pipeFd[2] = {0, };
+       pid_t pid = 0;
+
+       if (::pipe(pipeFd) < 0)
+               throw runtime::Exception("Failed to create pipe");
+
+       pid = ::fork();
+       if (pid < 0) {
+               ::close(pipeFd[0]);
+               ::close(pipeFd[1]);
+               throw runtime::Exception("Failed to fork");
+       }
+
+       if (pid == 0) {
+               ::close(pipeFd[0]);
+               if (::dup2(pipeFd[1], STDOUT_FILENO) < 0)
+                       ERROR(SINK, "Failed to copy STDOUT_FILENO");
+
+               if (::dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
+                       ERROR(SINK, "Failed to copy STDERR_FILENO");
+
+               ::close(pipeFd[1]);
+
+               if (::execv(args[0], const_cast<char* const*>(args.data())) < 0)
+                       ERROR(SINK, "Failed to execute : " + std::to_string(errno));
+
+               std::quick_exit(EXIT_FAILURE);
+       }
+
+       ::close(pipeFd[1]);
+
+       for (int status = 0; ::waitpid(pid, &status, 0) == -1;) {
+               if (errno != EINTR) {
+                       ::close(pipeFd[0]);
+                       throw runtime::Exception("Failed to wait child process");
+               }
+       }
+
+       auto closeFile = [](FILE *fp) { ::fclose(fp); };
+       std::unique_ptr<FILE, decltype(closeFile)> fp(::fdopen(pipeFd[0], "r"), closeFile);
+       if (fp == nullptr) {
+               ::close(pipeFd[0]);
+               throw runtime::Exception("Failed to open pipe descriptor");
+       }
+
+       char *line = nullptr;
+       size_t len = 0;
        try {
-               runtime::DirectoryIterator iter(INTERNAL_DEV_PATH), end;
-
-               while (iter != end) {
-                       const std::string& path = (*iter).getPath();
-                       std::string name = path.substr(path.rfind('/') + 1);
-                       std::string upper;
-                       upper.reserve(name.size());
-                       for (char c : name) {
-                               upper += std::toupper(c);
-                       }
-                       if (upper == INTERNAL_DEV_NAME) {
-                               source = path;
-                               break;
-                       }
-                       ++iter;
+               while ((::getline(&line, &len, fp.get())) != -1) {
+                       std::string log(line);
+                       log.erase(std::find_if(log.rbegin(), log.rend(),
+                                               [](unsigned char c){ return std::isgraph(c); }).base(), log.end());
+                       WARN(SINK, log);
                }
-       } catch (runtime::Exception &e) {}
+       } catch (std::exception &e) {
+               ERROR(SINK, e.what());
+       }
+       ::free(line);
+       ::close(pipeFd[0]);
+}
+
+bool isPartitionTerminated(const std::string &partition)
+{
+       bool ret = true;
+       const std::string cmd("fuser -m " + partition + " | grep -o '[0-9]*'");
+       char *line = nullptr;
+       size_t len = 0;
+
+       FILE *fp = ::popen(cmd.c_str(), "r");
+       if (fp == nullptr) {
+               ERROR(SINK, "Failed to get processes on partition");
+               return false;
+       }
+
+       if (::getline(&line, &len, fp) != -1)
+               ret = false;
 
-       engine.reset(new INTERNAL_ENGINE(
-               source, INTERNAL_PATH,
-               ProgressBar([](int v) {
-                       ::vconf_set_str(VCONFKEY_ODE_ENCRYPT_PROGRESS,
-                                                       std::to_string(v).c_str());
-               })
-       ));
+       ::free(line);
+       ::pclose(fp);
+
+       return ret;
 }
 
-InternalEncryption::~InternalEncryption()
+void unmountInternalStorage(const std::string& source)
 {
+       if (::umount2("/opt/usr", MNT_DETACH) == -1) {
+               if (errno != EBUSY && errno != EINVAL) {
+                       throw runtime::Exception("umount() error : " + runtime::GetSystemErrorMessage());
+               }
+       }
+
+       do {
+               ::sync();
+               std::vector<const char*> args = {
+                       "/usr/bin/fuser", "-m", "-k", "-v", "-SIGTERM", source.c_str(), nullptr
+               };
+               try {
+                       killProcesses(args);
+                       ::usleep((useconds_t)((unsigned int)(500)*1000));
+
+                       args[4] = "-SIGKILL";
+                       killProcesses(args);
+                       ::usleep((useconds_t)((unsigned int)(200)*1000));
+               } catch (runtime::Exception &e) {
+                       ERROR(e.what());
+               }
+       } while (!isPartitionTerminated(source));
+}
+
 }
 
-int InternalEncryption::setMountPassword(const std::string& password)
+InternalEncryptionServer::InternalEncryptionServer(ServerContext& srv,
+                                                                                                  KeyServer& key) :
+       server(srv),
+       keyServer(key)
 {
-       KeyManager::data pwData(password.begin(), password.end());
-       KeyManager keyManager(engine->getKeyMeta());
-       if (!keyManager.verifyPassword(pwData)) {
-               return -2;
+       server.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryptionServer::setMountPassword)(std::string));
+       server.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryptionServer::mount)(std::vector<unsigned char>, unsigned int));
+       server.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryptionServer::umount)());
+       server.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryptionServer::isMounted)());
+       server.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryptionServer::prepareEncryption)(unsigned int));
+       server.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryptionServer::prepareDecryption)());
+       server.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryptionServer::encrypt)(std::string, unsigned int));
+       server.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryptionServer::decrypt)(std::string));
+       server.expose(this, "", (int)(InternalEncryptionServer::isPasswordInitialized)());
+       server.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryptionServer::recovery)());
+       server.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryptionServer::initPassword)(std::string));
+       server.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryptionServer::cleanPassword)(std::string));
+       server.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryptionServer::changePassword)(std::string, std::string));
+       server.expose(this, PRIVILEGE_PLATFORM, (int)(InternalEncryptionServer::verifyPassword)(std::string));
+       server.expose(this, "", (int)(InternalEncryptionServer::getState)());
+       server.expose(this, "", (unsigned int)(InternalEncryptionServer::getSupportedOptions)());
+       server.expose(this, "", (std::string)(InternalEncryptionServer::getDevicePath)());
+
+       server.createNotification("InternalEncryptionServer::mount");
+
+       std::string source = findDevPath();
+
+       if (getStateInternal() == State::Encrypted) {
+               //"error_partially_encrypted"
+               if (!FileFooter::exist(source) && !UpgradeSupport::checkUpgradeFlag()) {
+                       // Trigger key migration process
+                       UpgradeSupport::createUpgradeFlag();
+               }
        }
 
-       ode::mountKey = keyManager.getMasterKey(pwData);
+       engine.reset(new INTERNAL_ENGINE(source,
+                                        INTERNAL_PATH,
+                                        ProgressBar(VCONFKEY_ODE_ENCRYPT_PROGRESS)));
 
-       return 0;
+       try {
+               dbus::Connection &systemDBus = dbus::Connection::getSystem();
+               systemDBus.registerObject(ODE_OBJECT_PATH, manifest, nullptr, nullptr);
+       } catch (runtime::Exception &e) {
+               ERROR(SINK, e.what());
+       }
 }
 
-int InternalEncryption::mount()
+InternalEncryptionServer::~InternalEncryptionServer()
 {
-       if (getState() != State::Encrypted) {
-               return -1;
+}
+
+int InternalEncryptionServer::migrateMasterKey(const std::string& dev, const std::string& password)
+{
+       try {
+               BinaryData masterKey = UpgradeSupport::loadMasterKey(dev);
+
+               // encrypt the master key with given password
+               return keyServer.changePassword2(dev, masterKey, password);
+       } catch (const runtime::Exception&) {
+               ERROR(SINK, "Failed to load the master key stored during upgrade.");
        }
 
-    if (engine->isMounted()) {
-               INFO(SINK, "Already mounted");
-               return 0;
+       return error::Unknown;
+}
+
+int InternalEncryptionServer::setMountPassword(const std::string& password)
+{
+       RequestLifetime rl(server);
+
+       const std::string& dev = engine->getSource();
+
+       // check if upgrade flag exists
+       if (UpgradeSupport::checkUpgradeFlag()) {
+               INFO(SINK, "Upgrade flag detected.");
+
+               int rc = migrateMasterKey(dev, password);
+               if (rc != error::None)
+                       return rc;
+               UpgradeSupport::removeUpgradeFlag();
        }
 
-       engine->mount(mountKey, getOptions());
+       return keyServer.get(dev, password, mountKey);
+}
+
+int InternalEncryptionServer::mount(const std::vector<unsigned char> &mk, unsigned int options)
+{
+       RequestLifetime rl(server);
+
+       if (mountKey.empty() && mk.empty()) {
+               ERROR(SINK, "You need to set master key first.");
+               return error::NoData;
+       }
+
+       BinaryData key = mk.empty() ? mountKey : mk;
        mountKey.clear();
 
-       context.notify("InternalEncryption::mount");
+       if (getStateInternal() != State::Encrypted) {
+               ERROR(SINK, "Cannot mount, SD partition's state incorrect.");
+               return error::NoSuchDevice;
+       }
+
+       if (engine->isMounted()) {
+               ERROR(SINK, "Partition already mounted.");
+               return error::None;
+       }
+
+       INFO(SINK, "Mounting internal storage.");
+       try {
+               engine->mount(key, getOptions());
+
+               server.notify("InternalEncryptionServer::mount");
+
+               runtime::File("/tmp/.lazy_mount").create(O_WRONLY);
+               runtime::File("/tmp/.unlock_mnt").create(O_WRONLY);
+       } catch (runtime::Exception &e) {
+               ERROR(SINK, "Mount failed: " + std::string(e.what()));
+               return error::Unknown;
+       }
+
+       return error::None;
+}
 
-       runtime::File("/tmp/.lazy_mount").create(O_WRONLY);
-       runtime::File("/tmp/.unlock_mnt").create(O_WRONLY);
+int InternalEncryptionServer::isMounted()
+{
+       RequestLifetime rl(server);
 
-       return 0;
+       int ret = 0;
+       try {
+               ret = engine->isMounted() ? 1 : 0;
+       } catch (runtime::Exception &e) {
+               ERROR(SINK, "Failed to access the mount flag");
+               return error::Unknown;
+       }
+       return ret;
 }
 
-int InternalEncryption::umount()
+int InternalEncryptionServer::umount()
 {
-       if (getState() != State::Encrypted) {
-               return -1;
+       RequestLifetime rl(server);
+
+       if (getStateInternal() != State::Encrypted) {
+               ERROR(SINK, "Cannot umount, partition's state incorrect.");
+               return error::NoSuchDevice;
        }
 
        if (!engine->isMounted()) {
-               INFO(SINK, "Already umounted");
-               return 0;
+               ERROR(SINK, "Partition already umounted.");
+               return error::None;
        }
 
-       INFO(SINK, "Close all processes that use internal storage...");
-       stopDependedSystemdServices();
-       killDependedProcesses();
-       INFO(SINK, "Umount internal storage...");
-       engine->umount();
+       INFO(SINK, "Closing all processes using internal storage.");
+       try {
+               stopSystemdUnits();
+               INFO(SINK, "Umounting internal storage.");
+               unmountInternalStorage("/dev/mapper/userdata");
+               engine->umount();
+       } catch (runtime::Exception &e) {
+               ERROR(SINK, "Umount failed: " + std::string(e.what()));
+               return error::Unknown;
+       }
 
-       return 0;
+       return error::None;
 }
 
-int InternalEncryption::encrypt(const std::string& password, unsigned int options)
+int InternalEncryptionServer::prepareEncryption(unsigned int options)
 {
-       if (getState() != State::Unencrypted) {
-               return -1;
+       RequestLifetime rl(server);
+
+       if (getStateInternal() != State::Unencrypted) {
+               ERROR(SINK, "Cannot encrypt, partition's state incorrect.");
+               return error::NoSuchDevice;
+       }
+
+       try {
+               runtime::File file("/opt/etc/.odeprogress");
+               file.create(MODE_0640);
+       } catch (runtime::Exception &e) {
+               ERROR(SINK, "Failed to create the flag file: " + std::string(e.what()));
+               return error::Unknown;
        }
 
-       KeyManager::data pwData(password.begin(), password.end());
-       KeyManager keyManager(engine->getKeyMeta());
+       setOptions(options & engine->getSupportedOptions());
+
+       ::vconf_set_str(VCONFKEY_ODE_CRYPTO_STATE, "prepared_encryption");
+       ::sync();
+       ::reboot(RB_AUTOBOOT);
+
+       return error::None;
+}
+
+int InternalEncryptionServer::prepareDecryption()
+{
+       RequestLifetime rl(server);
+
+       if (getStateInternal() != State::Encrypted) {
+               ERROR(SINK, "Cannot decrypt, partition's state incorrect.");
+               return error::NoSuchDevice;
+       }
+
+       try {
+               runtime::File file("/opt/etc/.odeprogress");
+               file.create(MODE_0640);
+       } catch (runtime::Exception &e) {
+               ERROR(SINK, "Failed to create the flag file: " + std::string(e.what()));
+               return error::Unknown;
+       }
+
+       ::vconf_set_str(VCONFKEY_ODE_CRYPTO_STATE, "prepared_decryption");
+       ::sync();
+       ::reboot(RB_AUTOBOOT);
+
+       return error::None;
+}
+
+int InternalEncryptionServer::encrypt(const std::string& password, unsigned int options)
+{
+       RequestLifetime rl(server);
 
-       if (!keyManager.verifyPassword(pwData)) {
-               return -2;
+       int state = getStateInternal();
+       if (state != State::Unencrypted && state != State::PreparedEncryption) {
+               ERROR(SINK, "Cannot encrypt, partition's state incorrect.");
+               return error::NoSuchDevice;
        }
 
-       KeyManager::data MasterKey = keyManager.getMasterKey(pwData);
-       auto encryptWorker = [MasterKey, options, this]() {
-               context.ref();
+       BinaryData masterKey;
+       int ret = keyServer.get(engine->getSource(), password, masterKey);
+       if (ret != error::None)
+               return ret;
+
+       auto encryptWorker = [masterKey, options, this](RequestLifetime&& rl) {
                try {
-                       INFO(SINK, "Close all processes that use internal storage...");
-                       stopDependedSystemdServices();
-                       killDependedProcesses();
-                       INFO(SINK, "Umount internal storage...");
-                       while (::umount(INTERNAL_PATH) == -1) {
-                               if (errno != EBUSY) {
-                                       throw runtime::Exception("Umount error - " + std::to_string(errno));
+                       if (::device_power_request_lock(POWER_LOCK_DISPLAY, 0) != 0)
+                               ERROR(SINK, "Failed to request to lock display");
+
+                       showProgressUI("encrypt");
+                       ::sleep(1);
+
+                       runtime::File file("/opt/etc/.odeprogress");
+                       if (getStateInternal() == State::Unencrypted) {
+                               /* For backward compatibility */
+                               file.create(MODE_0640);
+                               std::string source = engine->getSource();
+                               auto mntPaths = findMountPointsByDevice(source);
+
+                               if (!mntPaths.empty()) {
+                                       INFO(SINK, "Closing all processes using internal storage.");
+                                       stopSystemdUnits();
+
+                                       INFO(SINK, "Unmounting internal storage.");
+                                       unmountInternalStorage(source);
+                               }
+                               setOptions(options & engine->getSupportedOptions());
+                       }
+
+                       INFO(SINK, "Encryption started.");
+                       ::vconf_set_str(VCONFKEY_ODE_CRYPTO_STATE, "error_partially_encrypted");
+                       try {
+                               engine->encrypt(masterKey, getOptions());
+                       } catch (runtime::Exception &e) {
+                               ERROR(SINK, e.what());
+                               if (!engine->isStarted()) {
+                                       ::vconf_set_str(VCONFKEY_ODE_CRYPTO_STATE, "unencrypted");
+                                       file.remove();
                                }
-                               killDependedProcesses();
+                               ::sync();
+                               ::reboot(RB_AUTOBOOT);
                        }
 
-                       showProgressUI("Encrypting");
+                       INFO(SINK, "Encryption completed.");
+                       ::vconf_set_str(VCONFKEY_ODE_CRYPTO_STATE, "encrypted");
+                       server.notify("InternalEncryptionServer::mount");
 
-                       INFO(SINK, "Encryption started...");
-                       engine->encrypt(MasterKey, options);
-                       setOptions(options & getSupportedOptions());
-                       INFO(SINK, "Sync disk...");
-                       sync();
-                       INFO(SINK, "Encryption completed");
+                       file.remove();
 
-                       ::vconf_set_str(INTERNAL_STATE_VCONF_KEY, "encrypted");
-                       context.notify("InternalEncryption::mount");
+                       INFO(SINK, "Syncing disk and rebooting.");
+                       ::sync();
                        ::reboot(RB_AUTOBOOT);
                } catch (runtime::Exception &e) {
-                       ::vconf_set_str(INTERNAL_STATE_VCONF_KEY, "error_partially_encrypted");
-                       ERROR(SINK, "Encryption failed - " + std::string(e.what()));
+                       ERROR(SINK, "Encryption failed: " + std::string(e.what()));
                }
-               context.unref();
        };
 
-       std::thread asyncWork(encryptWorker);
+       std::thread asyncWork(encryptWorker, std::move(rl));
        asyncWork.detach();
 
-       return 0;
+       return error::None;
 }
 
-int InternalEncryption::decrypt(const std::string& password)
+int InternalEncryptionServer::decrypt(const std::string& password)
 {
-       if (getState() != State::Encrypted) {
-               return -1;
-       }
+       RequestLifetime rl(server);
 
-       KeyManager::data pwData(password.begin(), password.end());
-       KeyManager keyManager(engine->getKeyMeta());
+       int state = getStateInternal();
+       if (state != State::Encrypted && state != State::PreparedDecryption) {
+               ERROR(SINK, "Cannot decrypt, partition's state incorrect.");
+               return error::NoSuchDevice;
+       }
 
-       if (!keyManager.verifyPassword(pwData)) {
-               return -2;
+       // check if key migration is needed
+       if (UpgradeSupport::checkUpgradeFlag()) {
+               INFO(SINK, "Upgrade flag detected.");
+               const std::string& dev = engine->getSource();
+               int rc = migrateMasterKey(dev, password);
+               if (rc == error::None)
+                       UpgradeSupport::removeUpgradeFlag();
        }
 
-       KeyManager::data MasterKey = keyManager.getMasterKey(pwData);
-       auto decryptWorker = [MasterKey, this]() {
-               context.ref();
+       BinaryData masterKey;
+       int ret = keyServer.get(engine->getSource(), password, masterKey);
+       if (ret != error::None)
+               return ret;
+
+       auto decryptWorker = [masterKey, this](RequestLifetime&& rl) {
                try {
-                       INFO(SINK, "Umount internal storage...");
-                       stopDependedSystemdServices();
-                       killDependedProcesses();
-                       INFO(SINK, "Umount internal storage...");
-                       while (1) {
-                               try {
+                       if (::device_power_request_lock(POWER_LOCK_DISPLAY, 0) != 0)
+                               ERROR(SINK, "Failed to request to lock display");
+
+                       showProgressUI("decrypt");
+                       ::sleep(1);
+
+                       runtime::File file("/opt/etc/.odeprogress");
+                       if (getStateInternal() == State::Encrypted) {
+                               /* For backward compatibility */
+                               file.create(MODE_0640);
+
+                               if (engine->isMounted()) {
+                                       INFO(SINK, "Closing all processes using internal storage.");
+                                       stopSystemdUnits();
+
+                                       INFO(SINK, "Unmounting internal storage.");
+                                       unmountInternalStorage("/dev/mapper/userdata");
                                        engine->umount();
-                                       break;
-                               } catch (runtime::Exception& e) {
-                                       killDependedProcesses();
                                }
                        }
 
-                       showProgressUI("Decrypting");
+                       INFO(SINK, "Decryption started.");
+                       ::vconf_set_str(VCONFKEY_ODE_CRYPTO_STATE, "error_partially_decrypted");
+                       try {
+                               engine->decrypt(masterKey, getOptions());
+                       } catch (runtime::Exception &e) {
+                               ERROR(SINK, e.what());
+                               if (!engine->isStarted()) {
+                                       ::vconf_set_str(VCONFKEY_ODE_CRYPTO_STATE, "encrypted");
+                                       file.remove();
+                               }
+                               ::sync();
+                               ::reboot(RB_AUTOBOOT);
+                       }
+
+                       INFO(SINK, "Decryption complete.");
+                       ::vconf_set_str(VCONFKEY_ODE_CRYPTO_STATE, "unencrypted");
 
-                       INFO(SINK, "Decryption started...");
-                       engine->decrypt(MasterKey, getOptions());
-                       INFO(SINK, "Sync disk...");
-                       sync();
-                       INFO(SINK, "Decryption completed");
+                       file.remove();
 
-                       ::vconf_set_str(INTERNAL_STATE_VCONF_KEY, "unencrypted");
+                       INFO(SINK, "Syncing disk and rebooting.");
+                       ::sync();
                        ::reboot(RB_AUTOBOOT);
                } catch (runtime::Exception &e) {
-                       ::vconf_set_str(INTERNAL_STATE_VCONF_KEY, "error_partially_encrypted");
-                       ERROR(SINK, "Decryption failed - " + std::string(e.what()));
+                       ERROR(SINK, "Decryption failed: " + std::string(e.what()));
                }
-               context.unref();
        };
 
-       std::thread asyncWork(decryptWorker);
+       std::thread asyncWork(decryptWorker, std::move(rl));
        asyncWork.detach();
 
-       return 0;
+       return error::None;
 }
 
-int InternalEncryption::recovery()
+int InternalEncryptionServer::recovery()
 {
-       if (getState() != State::Unencrypted) {
-               return -1;
-       }
+       RequestLifetime rl(server);
 
-       //TODO
-       runtime::Process proc(PROG_FACTORY_RESET, wipeCommand);
-       if (proc.execute() == -1) {
-               ERROR(SINK, "Failed to launch factory-reset");
-               return -2;
+       int state = getStateInternal();
+
+       if (state == State::Unencrypted)
+               return error::NoSuchDevice;
+       if (state == State::PreparedEncryption) {
+               ::vconf_set_str(VCONFKEY_ODE_CRYPTO_STATE, "unencrypted");
+               ::sync();
+               return error::None;
+       }
+       if (state == State::PreparedDecryption) {
+               ::vconf_set_str(VCONFKEY_ODE_CRYPTO_STATE, "encrypted");
+               ::sync();
+               return error::None;
        }
 
-       return 0;
-}
+       runtime::File file("/opt/.factoryreset");
+       file.create(MODE_0640);
 
-int InternalEncryption::isPasswordInitialized()
-{
-       if (engine->isKeyMetaSet()) {
-               return 1;
+       ::sync();
+       try {
+               dbus::Connection& systemDBus = dbus::Connection::getSystem();
+               systemDBus.methodcall("org.tizen.system.deviced",
+                                                               "/Org/Tizen/System/DeviceD/Power",
+                                                               "org.tizen.system.deviced.power",
+                                                               "reboot",
+                                                               -1, "()", "(si)", "reboot", 0);
+       } catch (runtime::Exception &e) {
+               ::reboot(RB_AUTOBOOT);
        }
-       return 0;
+       return error::None;
 }
 
-int InternalEncryption::initPassword(const std::string& password)
+int InternalEncryptionServer::isPasswordInitialized()
 {
-       KeyManager::data pwData(password.begin(), password.end());
-       KeyManager keyManager;
+       return keyServer.isInitialized(engine->getSource());
+}
 
-       keyManager.initPassword(pwData);
-       engine->setKeyMeta(keyManager.serialize());
-       return 0;
+int InternalEncryptionServer::initPassword(const std::string& password)
+{
+       return keyServer.init(engine->getSource(), password, Key::DEFAULT_256BIT);
 }
 
-int InternalEncryption::cleanPassword(const std::string& password)
+int InternalEncryptionServer::cleanPassword(const std::string& password)
 {
-       KeyManager::data pwData(password.begin(), password.end());
-       KeyManager keyManager(engine->getKeyMeta());
+       return keyServer.remove(engine->getSource(), password);
+}
 
-       if (!keyManager.verifyPassword(pwData)) {
-               return -2;
-       }
+int InternalEncryptionServer::changePassword(const std::string& oldPassword,
+                                                                               const std::string& newPassword)
+{
+       return keyServer.changePassword(engine->getSource(), oldPassword, newPassword);
+}
 
-       engine->clearKeyMeta();
-       return 0;
+int InternalEncryptionServer::verifyPassword(const std::string& password)
+{
+       return keyServer.verifyPassword(engine->getSource(), password);
 }
 
-int InternalEncryption::changePassword(const std::string& oldPassword,
-                                                                               const std::string& newPassword)
+int InternalEncryptionServer::getState()
 {
-       KeyManager::data oldPwData(oldPassword.begin(), oldPassword.end());
-       KeyManager::data newPwData(newPassword.begin(), newPassword.end());
-       KeyManager keyManager(engine->getKeyMeta());
+       RequestLifetime rl(server);
 
-       if (!keyManager.verifyPassword(oldPwData)) {
-               return -2;
-       }
+       return getStateInternal();
+}
 
-       keyManager.changePassword(oldPwData, newPwData);
-       engine->setKeyMeta(keyManager.serialize());
+unsigned int InternalEncryptionServer::getSupportedOptions()
+{
+       RequestLifetime rl(server);
 
-       return 0;
+       return engine->getSupportedOptions();
 }
 
-int InternalEncryption::verifyPassword(const std::string& password)
+std::string InternalEncryptionServer::getDevicePath() const
 {
-       KeyManager::data pwData(password.begin(), password.end());
-       KeyManager keyManager(engine->getKeyMeta());
+       RequestLifetime rl(server);
 
-       if (keyManager.verifyPassword(pwData)) {
-               return 1;
-       }
-       return 0;
+       return engine->getSource();
 }
 
-int InternalEncryption::getState()
+int InternalEncryptionServer::getStateInternal() const
 {
-       char *value = ::vconf_get_str(INTERNAL_STATE_VCONF_KEY);
+       char *value = ::vconf_get_str(VCONFKEY_ODE_CRYPTO_STATE);
        if (value == NULL) {
-               throw runtime::Exception("Failed to get vconf value");
+               throw runtime::Exception("Failed to get vconf value.");
        }
 
        std::string valueStr(value);
        free(value);
 
-       if (valueStr == "encrypted") {
+       if (valueStr == "encrypted")
                return State::Encrypted;
-       } else if (valueStr == "unencrypted") {
+       else if (valueStr == "unencrypted")
                return State::Unencrypted;
-       } else {
+       else if (valueStr == "prepared_encryption")
+               return State::PreparedEncryption;
+       else if (valueStr == "prepared_decryption")
+               return State::PreparedDecryption;
+       else if (valueStr == "error_partially_encrypted" || valueStr == "error_partially_decrypted")
                return State::Corrupted;
-       }
-
-       return 0;
-}
 
-unsigned int InternalEncryption::getSupportedOptions()
-{
-       return engine->getSupportedOptions();
+       return State::NotSupported;
 }
 
 } // namespace ode