Fix static analysis issue
[platform/core/security/ode.git] / server / internal-encryption.cpp
index a873a17..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/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 "misc.h"
 #include "logger.h"
 #include "progress-bar.h"
-#include "engine/encryption/dmcrypt-engine.h"
-#include "key-manager/key-manager.h"
+#include "rmi/common.h"
 
-#include "rmi/internal-encryption.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 {
 
-typedef DMCryptEngine INTERNAL_ENGINE;
-
-const char *INTERNAL_DEV_PATH  = "/dev/disk/by-partlabel";
-const char *INTERNAL_DEV_NAME  = "USER";
-const char *INTERNAL_PATH              = "/opt/usr";
-
 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;
+       };
 
-// TODO: see recovery()
-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"
+       dbus::Connection::SubscriptionId id;
+       dbus::Connection& systemDBus;
+       std::list<Job> removedJobs;
+       std::mutex jobsMutex;
+       std::condition_variable jobsCv;
 };
 
-std::unique_ptr<INTERNAL_ENGINE> engine;
-KeyManager::data mountKey;
+JobWatch::JobWatch(dbus::Connection& systemDBus) : systemDBus(systemDBus) {
+       auto callback = [this](dbus::Variant parameters) {
+               this->jobRemoved(parameters);
+       };
 
-std::string findDevPath()
-{
-       std::string source = INTERNAL_DEV_PATH + std::string("/") + INTERNAL_DEV_NAME;
-       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.compare(0, strlen(INTERNAL_DEV_NAME), INTERNAL_DEV_NAME) == 0) {
-                               source = path;
-                               break;
-                       }
-                       ++iter;
-               }
-       } catch (runtime::Exception &e) {}
+       id = systemDBus.subscribeSignal("",
+                                                                       "/org/freedesktop/systemd1",
+                                                                       "org.freedesktop.systemd1.Manager",
+                                                                       "JobRemoved",
+                                                                       callback);
+}
 
-       char *dev = ::realpath(source.c_str(), NULL);
-       if (dev == NULL) {
-               ERROR(SINK, "failed to get device path");
-               return "";
-       }
+JobWatch::~JobWatch() {
+       systemDBus.unsubscribeSignal(id);
+}
 
-       std::string devPath(dev);
-       free(dev);
+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;
+               }
 
-       return devPath;
+               while(!removedJobs.empty()) {
+                       bool match = (removedJobs.front().job == job);
+                       bool done = (removedJobs.front().result == "done");
+                       removedJobs.pop_front();
+                       if (match)
+                               return done;
+               }
+       };
 }
 
-void stopKnownSystemdServices() {
-       std::vector<std::string> knownSystemdServices;
+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 stopSystemdUnits()
+{
        dbus::Connection& systemDBus = dbus::Connection::getSystem();
        dbus::VariantIterator iter;
+       std::vector<std::string> preprocessUnits;
+       std::set<std::string> unitsToStop;
 
-       systemDBus.methodcall("org.freedesktop.systemd1",
-                                                       "/org/freedesktop/systemd1",
-                                                       "org.freedesktop.systemd1.Manager",
-                                                       "ListUnits",
-                                                       -1, "(a(ssssssouso))", "")
-                                                               .get("(a(ssssssouso))", &iter);
+       try {
+               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 dataUint;
+               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, &dataUint, dataStr + 7,
-                                               dataStr + 8);
-
-               if (!ret) {
+                                               dataStr + 6, &dataUnit, dataStr + 7, dataStr + 8);
+               if (!ret)
                        break;
-               }
 
-               std::string service(dataStr[0]);
-               if (service.compare(0, 5, "user@") == 0 ||
-                       service == "tlm.service" ||
-                       service == "resourced.service") {
-                       knownSystemdServices.push_back(service);
+               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 (const std::string& service : knownSystemdServices) {
-               INFO(SINK, "Stop service - " + service);
-               systemDBus.methodcall("org.freedesktop.systemd1",
-                                                               "/org/freedesktop/systemd1",
-                                                               "org.freedesktop.systemd1.Manager",
-                                                               "StopUnit",
-                                                               -1, "", "(ss)", service.c_str(), "flush");
+       for (auto &unit : preprocessUnits) {
+               stopUnit(unit);
        }
 
-       sleep(1);
-}
-
-void stopDependedSystemdServices()
-{
-       dbus::Connection& systemDBus = dbus::Connection::getSystem();
-       std::set<std::string> servicesToStop;
-
        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 *service;
+                       char *unit = nullptr;
                        systemDBus.methodcall("org.freedesktop.systemd1",
                                                                        "/org/freedesktop/systemd1",
                                                                        "org.freedesktop.systemd1.Manager",
                                                                        "GetUnitByPID",
                                                                        -1, "(o)", "(u)", (unsigned int)pid)
-                                                                               .get("(o)", &service);
-                       servicesToStop.insert(service);
+                                                                               .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) {
-                       INFO(SINK, "Close process - " + std::to_string(pid));
+                       ERROR(SINK, "Killing process: " + std::to_string(pid));
                        ::kill(pid, SIGKILL);
                }
        }
 
-       for (const std::string& service : servicesToStop) {
-               INFO(SINK, "Close service - " + service);
-               systemDBus.methodcall("org.freedesktop.systemd1",
-                                                               service,
-                                                               "org.freedesktop.systemd1.Unit",
-                                                               "Stop",
-                                                               -1, "", "(s)", "flush");
+       for (auto unit : unitsToStop) {
+               stopUnit(unit);
        }
 }
 
-void showProgressUI(const std::string type) {
-       ::tzplatform_set_user(::tzplatform_getuid(TZ_SYS_DEFAULT_USER));
-       std::string defaultUserHome(::tzplatform_getenv(TZ_USER_HOME));
-       ::tzplatform_reset_user();
+void showProgressUI(const std::string type)
+{
+       dbus::Connection& systemDBus = dbus::Connection::getSystem();
+       std::string unit("ode-progress-ui@"+type+".service");
 
-       try {
-               runtime::File shareDirectory("/opt/home/root/share");
-               if (!shareDirectory.exists()) {
-                       shareDirectory.makeDirectory(true);
-               }
+       JobWatch watch(systemDBus);
+       INFO(SINK, "Start unit: " + unit);
 
-               runtime::File elmConfigDir(shareDirectory.getPath() + "/.elementary");
-               if (!elmConfigDir.exists()) {
-                       runtime::File defaultElmConfigDir(defaultUserHome + "/share/.elementary");
-                       defaultElmConfigDir.copyTo(shareDirectory.getPath());
-               }
-       } catch (runtime::Exception &e) {
-               ERROR(SINK, "Failed to set up elm configuration");
-       }
-
-       std::vector<std::string> args = {
-               "ode", "progress", type, "Internal"
-       };
+       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);
 
-       runtime::Process proc("/usr/bin/ode", args);
-       proc.execute();
+       INFO(SINK, "Waiting for job: " + std::string(job));
+       if (!watch.waitForJob(job))
+               ERROR(SINK, "Starting unit: " + unit + " failed");
 }
 
 unsigned int getOptions()
@@ -233,323 +313,592 @@ void setOptions(unsigned int options)
        ::vconf_set_bool(VCONFKEY_ODE_FAST_ENCRYPTION, value);
 }
 
+void killProcesses(std::vector<const char*> &args)
+{
+       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 {
+               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 (std::exception &e) {
+               ERROR(SINK, e.what());
+       }
+       ::free(line);
+       ::close(pipeFd[0]);
 }
 
-InternalEncryption::InternalEncryption(ODEControlContext& ctx) :
-       context(ctx)
+bool isPartitionTerminated(const std::string &partition)
 {
-       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");
+       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;
+
+       ::free(line);
+       ::pclose(fp);
+
+       return ret;
+}
+
+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));
+}
+
+}
+
+InternalEncryptionServer::InternalEncryptionServer(ServerContext& srv,
+                                                                                                  KeyServer& key) :
+       server(srv),
+       keyServer(key)
+{
+       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();
 
-       engine.reset(new INTERNAL_ENGINE(
-               source, INTERNAL_PATH,
-               ProgressBar([](int v) {
-                       ::vconf_set_str(VCONFKEY_ODE_ENCRYPT_PROGRESS,
-                                                       std::to_string(v).c_str());
-               })
-       ));
+       if (getStateInternal() == State::Encrypted) {
+               //"error_partially_encrypted"
+               if (!FileFooter::exist(source) && !UpgradeSupport::checkUpgradeFlag()) {
+                       // Trigger key migration process
+                       UpgradeSupport::createUpgradeFlag();
+               }
+       }
+
+       engine.reset(new INTERNAL_ENGINE(source,
+                                        INTERNAL_PATH,
+                                        ProgressBar(VCONFKEY_ODE_ENCRYPT_PROGRESS)));
+
+       try {
+               dbus::Connection &systemDBus = dbus::Connection::getSystem();
+               systemDBus.registerObject(ODE_OBJECT_PATH, manifest, nullptr, nullptr);
+       } catch (runtime::Exception &e) {
+               ERROR(SINK, e.what());
+       }
 }
 
-InternalEncryption::~InternalEncryption()
+InternalEncryptionServer::~InternalEncryptionServer()
 {
 }
 
-int InternalEncryption::setMountPassword(const std::string& password)
+int InternalEncryptionServer::migrateMasterKey(const std::string& dev, const std::string& password)
 {
-       KeyManager::data pwData(password.begin(), password.end());
-       KeyManager keyManager(engine->getKeyMeta());
-       if (!keyManager.verifyPassword(pwData)) {
-               return -2;
+       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.");
        }
 
-       ode::mountKey = keyManager.getMasterKey(pwData);
+       return error::Unknown;
+}
+
+int InternalEncryptionServer::setMountPassword(const std::string& password)
+{
+       RequestLifetime rl(server);
 
-       return 0;
+       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();
+       }
+
+       return keyServer.get(dev, password, mountKey);
 }
 
-int InternalEncryption::mount()
+int InternalEncryptionServer::mount(const std::vector<unsigned char> &mk, unsigned int options)
 {
-       if (getState() != State::Encrypted) {
-               return -1;
+       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();
+
+       if (getStateInternal() != State::Encrypted) {
+               ERROR(SINK, "Cannot mount, SD partition's state incorrect.");
+               return error::NoSuchDevice;
        }
 
-    if (engine->isMounted()) {
-               INFO(SINK, "Already mounted");
-               return 0;
+       if (engine->isMounted()) {
+               ERROR(SINK, "Partition already mounted.");
+               return error::None;
        }
 
+       INFO(SINK, "Mounting internal storage.");
        try {
-               INFO(SINK, "Mount internal storage...");
-               engine->mount(mountKey, getOptions());
-               mountKey.clear();
+               engine->mount(key, getOptions());
 
-               context.notify("InternalEncryption::mount");
+               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 -1;
+               ERROR(SINK, "Mount failed: " + std::string(e.what()));
+               return error::Unknown;
        }
 
-       return 0;
+       return error::None;
 }
 
-int InternalEncryption::umount()
+int InternalEncryptionServer::isMounted()
 {
-       if (getState() != State::Encrypted) {
-               return -1;
+       RequestLifetime rl(server);
+
+       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 InternalEncryptionServer::umount()
+{
+       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, "Closing all processes using internal storage.");
        try {
-               INFO(SINK, "Close all processes using internal storage...");
-               stopDependedSystemdServices();
-               INFO(SINK, "Umount internal storage...");
+               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 -1;
+               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);
 
-       if (!keyManager.verifyPassword(pwData)) {
-               return -2;
+       return error::None;
+}
+
+int InternalEncryptionServer::prepareDecryption()
+{
+       RequestLifetime rl(server);
+
+       if (getStateInternal() != State::Encrypted) {
+               ERROR(SINK, "Cannot decrypt, partition's state incorrect.");
+               return error::NoSuchDevice;
        }
 
-       KeyManager::data MasterKey = keyManager.getMasterKey(pwData);
-       auto encryptWorker = [MasterKey, options, this]() {
+       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);
+
+       int state = getStateInternal();
+       if (state != State::Unencrypted && state != State::PreparedEncryption) {
+               ERROR(SINK, "Cannot encrypt, partition's state incorrect.");
+               return error::NoSuchDevice;
+       }
+
+       BinaryData masterKey;
+       int ret = keyServer.get(engine->getSource(), password, masterKey);
+       if (ret != error::None)
+               return ret;
+
+       auto encryptWorker = [masterKey, options, this](RequestLifetime&& rl) {
                try {
-                       std::string source = engine->getSource();
-                       std::string mntPath = findMntPath(source);
-
-                       if (!mntPath.empty()) {
-                               INFO(SINK, "Close all known systemd services that might be using internal storage...");
-                               stopKnownSystemdServices();
-                               INFO(SINK, "Close all processes using internal storage...");
-                               stopDependedSystemdServices();
-                       }
+                       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();
 
-                       while (!mntPath.empty()) {
-                               INFO(SINK, "Umount internal storage...");
-                               while (::umount(mntPath.c_str()) == -1) {
-                                       if (errno != EBUSY) {
-                                               throw runtime::Exception("Umount error - " + std::to_string(errno));
-                                       }
-                                       stopDependedSystemdServices();
+                                       INFO(SINK, "Unmounting internal storage.");
+                                       unmountInternalStorage(source);
                                }
-                               mntPath = findMntPath(source);
+                               setOptions(options & engine->getSupportedOptions());
                        }
 
-                       showProgressUI("Encrypting");
-
-                       INFO(SINK, "Encryption started...");
-                       engine->encrypt(MasterKey, options);
-                       setOptions(options & 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();
+                               }
+                               ::sync();
+                               ::reboot(RB_AUTOBOOT);
+                       }
 
-                       INFO(SINK, "Encryption completed");
+                       INFO(SINK, "Encryption completed.");
                        ::vconf_set_str(VCONFKEY_ODE_CRYPTO_STATE, "encrypted");
-                       context.notify("InternalEncryption::mount");
+                       server.notify("InternalEncryptionServer::mount");
+
+                       file.remove();
 
-                       INFO(SINK, "Syncing disk and rebooting...");
+                       INFO(SINK, "Syncing disk and rebooting.");
                        ::sync();
                        ::reboot(RB_AUTOBOOT);
                } catch (runtime::Exception &e) {
-                       ::vconf_set_str(VCONFKEY_ODE_CRYPTO_STATE, "error_partially_encrypted");
-                       ERROR(SINK, "Encryption failed - " + std::string(e.what()));
+                       ERROR(SINK, "Encryption failed: " + std::string(e.what()));
                }
        };
 
-       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]() {
+       BinaryData masterKey;
+       int ret = keyServer.get(engine->getSource(), password, masterKey);
+       if (ret != error::None)
+               return ret;
+
+       auto decryptWorker = [masterKey, this](RequestLifetime&& rl) {
                try {
-                       if (engine->isMounted()) {
-                               INFO(SINK, "Close all known systemd services that might be using internal storage...");
-                               stopKnownSystemdServices();
-                               INFO(SINK, "Close all processes using internal storage...");
-                               stopDependedSystemdServices();
-
-                               INFO(SINK, "Umount internal storage...");
-                               while (1) {
-                                       try {
-                                               engine->umount();
-                                               break;
-                                       } catch (runtime::Exception& e) {
-                                               stopDependedSystemdServices();
-                                       }
+                       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();
                                }
                        }
 
-                       showProgressUI("Decrypting");
-
-                       INFO(SINK, "Decryption started...");
-                       engine->decrypt(MasterKey, getOptions());
+                       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 completed");
+                       INFO(SINK, "Decryption complete.");
                        ::vconf_set_str(VCONFKEY_ODE_CRYPTO_STATE, "unencrypted");
 
-                       INFO(SINK, "Syncing disk and rebooting...");
+                       file.remove();
+
+                       INFO(SINK, "Syncing disk and rebooting.");
                        ::sync();
                        ::reboot(RB_AUTOBOOT);
                } catch (runtime::Exception &e) {
-                       ::vconf_set_str(VCONFKEY_ODE_CRYPTO_STATE, "error_partially_encrypted");
-                       ERROR(SINK, "Decryption failed - " + std::string(e.what()));
+                       ERROR(SINK, "Decryption failed: " + std::string(e.what()));
                }
        };
 
-       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(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