Fix static analysis issue
[platform/core/security/ode.git] / server / internal-encryption.cpp
index b115034..58edac5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2015-2017 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.
@@ -26,6 +26,7 @@
 #include <unistd.h>
 #include <sys/mount.h>
 #include <sys/reboot.h>
+#include <sys/wait.h>
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
@@ -37,7 +38,9 @@
 #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"
@@ -55,6 +58,17 @@ namespace ode {
 namespace {
 
 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 {
@@ -135,7 +149,7 @@ void JobWatch::jobRemoved(const dbus::Variant& parameters)
        const char* unit;
        const char* result;
        parameters.get("(uoss)", &id, &job, &unit, &result);
-       INFO(SINK, "id:" + std::to_string(id) + " job:" + job + " unit:" + unit + " result:" + result);
+       WARN(SINK, "id:" + std::to_string(id) + " job:" + job + " unit:" + unit + " result:" + result);
 
        {
                std::lock_guard<std::mutex> guard(jobsMutex);
@@ -144,28 +158,6 @@ void JobWatch::jobRemoved(const dbus::Variant& parameters)
        jobsCv.notify_one();
 }
 
-std::string getDecodedPath(const std::string &path, const std::string &prefix)
-{
-       std::string ret = path;
-       size_t pos = 0;
-
-       pos = ret.find(prefix);
-       if (pos != std::string::npos)
-               ret = ret.substr(prefix.size(), ret.size());
-
-       pos = 0;
-       while ((pos = ret.find("_", pos)) != std::string::npos) {
-               int a = std::stoi(std::string(1, ret.at(pos+1)), nullptr, 16);
-               int b = std::stoi(std::string(1, ret.at(pos+2)), nullptr, 16);
-               if (a < 0 || b < 0)
-                       ret.replace(pos, 3, "_");
-               else
-                       ret.replace(pos, 3, std::string(1, ((a << 4) | b)));
-               pos += 1;
-       }
-       return ret;
-}
-
 void stopSystemdUnits()
 {
        dbus::Connection& systemDBus = dbus::Connection::getSystem();
@@ -173,9 +165,18 @@ void stopSystemdUnits()
        std::vector<std::string> preprocessUnits;
        std::set<std::string> unitsToStop;
 
+       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);
-               INFO(SINK, "Stopping unit: " + unit);
+               WARN(SINK, "Stopping unit: " + unit);
                const char* job = NULL;
                try {
                        systemDBus.methodcall("org.freedesktop.systemd1",
@@ -183,7 +184,7 @@ void stopSystemdUnits()
                                                                        "org.freedesktop.systemd1.Manager",
                                                                        "StopUnit",
                                                                        -1, "(o)", "(ss)", unit.c_str(), "flush").get("(o)", &job);
-                       INFO(SINK, "Waiting for job: " + std::string(job));
+                       WARN(SINK, "Waiting for job: " + std::string(job));
                        if (!watch.waitForJob(job))
                                ERROR(SINK, "Stopping unit: " + unit + " failed");
                } catch (runtime::Exception &e) {
@@ -224,11 +225,17 @@ void stopSystemdUnits()
                }
        }
 
-       for (auto unit : preprocessUnits) {
+       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",
@@ -238,9 +245,17 @@ void stopSystemdUnits()
                                                                        -1, "(o)", "(u)", (unsigned int)pid)
                                                                                .get("(o)", &unit);
 
-                       auto unescapedName = getDecodedPath(unit, "/org/freedesktop/systemd1/unit/");
-                       if (unescapedName.find("dbus", 0, 4) == std::string::npos)
-                               unitsToStop.insert(unescapedName);
+                       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);
@@ -298,16 +313,67 @@ void setOptions(unsigned int options)
        ::vconf_set_bool(VCONFKEY_ODE_FAST_ENCRYPTION, value);
 }
 
-void execAndWait(const std::string &path, std::vector<std::string> &args)
+void killProcesses(std::vector<const char*> &args)
 {
-       runtime::Process proc(path, args);
-       int ret = proc.execute();
-       if (ret < 0)
-               ERROR(SINK, path + " failed for " + args.back());
+       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]);
 
-       ret = proc.waitForFinished();
-       if (ret < 0 || !WIFEXITED(ret) || WEXITSTATUS(ret) != 0)
-               ERROR(SINK, path + " failed for " + args.back());
+       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]);
 }
 
 bool isPartitionTerminated(const std::string &partition)
@@ -342,16 +408,19 @@ void unmountInternalStorage(const std::string& source)
 
        do {
                ::sync();
-               static const char *fuserPath = "/usr/bin/fuser";
-               std::vector<std::string> args = {
-                       fuserPath, "-m", "-k", "-s", "-SIGTERM", source,
+               std::vector<const char*> args = {
+                       "/usr/bin/fuser", "-m", "-k", "-v", "-SIGTERM", source.c_str(), nullptr
                };
-               execAndWait(fuserPath, args);
-               ::usleep((useconds_t)((unsigned int)(500)*1000));
+               try {
+                       killProcesses(args);
+                       ::usleep((useconds_t)((unsigned int)(500)*1000));
 
-               args[4] = "-SIGKILL";
-               execAndWait(fuserPath, args);
-               ::usleep((useconds_t)((unsigned int)(200)*1000));
+                       args[4] = "-SIGKILL";
+                       killProcesses(args);
+                       ::usleep((useconds_t)((unsigned int)(200)*1000));
+               } catch (runtime::Exception &e) {
+                       ERROR(e.what());
+               }
        } while (!isPartitionTerminated(source));
 }
 
@@ -366,6 +435,8 @@ InternalEncryptionServer::InternalEncryptionServer(ServerContext& srv,
        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)());
@@ -382,7 +453,7 @@ InternalEncryptionServer::InternalEncryptionServer(ServerContext& srv,
 
        std::string source = findDevPath();
 
-       if (getState() == State::Encrypted) {
+       if (getStateInternal() == State::Encrypted) {
                //"error_partially_encrypted"
                if (!FileFooter::exist(source) && !UpgradeSupport::checkUpgradeFlag()) {
                        // Trigger key migration process
@@ -390,13 +461,16 @@ InternalEncryptionServer::InternalEncryptionServer(ServerContext& srv,
                }
        }
 
-       engine.reset(new INTERNAL_ENGINE(
-               source, INTERNAL_PATH,
-               ProgressBar([](int v) {
-                       ::vconf_set_str(VCONFKEY_ODE_ENCRYPT_PROGRESS,
-                                                       std::to_string(v).c_str());
-               })
-       ));
+       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());
+       }
 }
 
 InternalEncryptionServer::~InternalEncryptionServer()
@@ -419,6 +493,8 @@ int InternalEncryptionServer::migrateMasterKey(const std::string& dev, const std
 
 int InternalEncryptionServer::setMountPassword(const std::string& password)
 {
+       RequestLifetime rl(server);
+
        const std::string& dev = engine->getSource();
 
        // check if upgrade flag exists
@@ -436,6 +512,8 @@ int InternalEncryptionServer::setMountPassword(const std::string& password)
 
 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;
@@ -444,7 +522,7 @@ int InternalEncryptionServer::mount(const std::vector<unsigned char> &mk, unsign
        BinaryData key = mk.empty() ? mountKey : mk;
        mountKey.clear();
 
-       if (getState() != State::Encrypted) {
+       if (getStateInternal() != State::Encrypted) {
                ERROR(SINK, "Cannot mount, SD partition's state incorrect.");
                return error::NoSuchDevice;
        }
@@ -472,6 +550,8 @@ int InternalEncryptionServer::mount(const std::vector<unsigned char> &mk, unsign
 
 int InternalEncryptionServer::isMounted()
 {
+       RequestLifetime rl(server);
+
        int ret = 0;
        try {
                ret = engine->isMounted() ? 1 : 0;
@@ -484,7 +564,9 @@ int InternalEncryptionServer::isMounted()
 
 int InternalEncryptionServer::umount()
 {
-       if (getState() != State::Encrypted) {
+       RequestLifetime rl(server);
+
+       if (getStateInternal() != State::Encrypted) {
                ERROR(SINK, "Cannot umount, partition's state incorrect.");
                return error::NoSuchDevice;
        }
@@ -508,9 +590,62 @@ int InternalEncryptionServer::umount()
        return error::None;
 }
 
+int InternalEncryptionServer::prepareEncryption(unsigned int options)
+{
+       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;
+       }
+
+       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)
 {
-       if (getState() != State::Unencrypted) {
+       RequestLifetime rl(server);
+
+       int state = getStateInternal();
+       if (state != State::Unencrypted && state != State::PreparedEncryption) {
                ERROR(SINK, "Cannot encrypt, partition's state incorrect.");
                return error::NoSuchDevice;
        }
@@ -520,7 +655,7 @@ int InternalEncryptionServer::encrypt(const std::string& password, unsigned int
        if (ret != error::None)
                return ret;
 
-       auto encryptWorker = [masterKey, options, this]() {
+       auto encryptWorker = [masterKey, options, this](RequestLifetime&& rl) {
                try {
                        if (::device_power_request_lock(POWER_LOCK_DISPLAY, 0) != 0)
                                ERROR(SINK, "Failed to request to lock display");
@@ -529,23 +664,26 @@ int InternalEncryptionServer::encrypt(const std::string& password, unsigned int
                        ::sleep(1);
 
                        runtime::File file("/opt/etc/.odeprogress");
-                       file.create(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);
+                       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, options);
+                               engine->encrypt(masterKey, getOptions());
                        } catch (runtime::Exception &e) {
                                ERROR(SINK, e.what());
                                if (!engine->isStarted()) {
@@ -555,7 +693,6 @@ int InternalEncryptionServer::encrypt(const std::string& password, unsigned int
                                ::sync();
                                ::reboot(RB_AUTOBOOT);
                        }
-                       setOptions(options & getSupportedOptions());
 
                        INFO(SINK, "Encryption completed.");
                        ::vconf_set_str(VCONFKEY_ODE_CRYPTO_STATE, "encrypted");
@@ -571,7 +708,7 @@ int InternalEncryptionServer::encrypt(const std::string& password, unsigned int
                }
        };
 
-       std::thread asyncWork(encryptWorker);
+       std::thread asyncWork(encryptWorker, std::move(rl));
        asyncWork.detach();
 
        return error::None;
@@ -579,7 +716,10 @@ int InternalEncryptionServer::encrypt(const std::string& password, unsigned int
 
 int InternalEncryptionServer::decrypt(const std::string& password)
 {
-       if (getState() != State::Encrypted) {
+       RequestLifetime rl(server);
+
+       int state = getStateInternal();
+       if (state != State::Encrypted && state != State::PreparedDecryption) {
                ERROR(SINK, "Cannot decrypt, partition's state incorrect.");
                return error::NoSuchDevice;
        }
@@ -598,7 +738,7 @@ int InternalEncryptionServer::decrypt(const std::string& password)
        if (ret != error::None)
                return ret;
 
-       auto decryptWorker = [masterKey, this]() {
+       auto decryptWorker = [masterKey, this](RequestLifetime&& rl) {
                try {
                        if (::device_power_request_lock(POWER_LOCK_DISPLAY, 0) != 0)
                                ERROR(SINK, "Failed to request to lock display");
@@ -607,15 +747,18 @@ int InternalEncryptionServer::decrypt(const std::string& password)
                        ::sleep(1);
 
                        runtime::File file("/opt/etc/.odeprogress");
-                       file.create(0640);
+                       if (getStateInternal() == State::Encrypted) {
+                               /* For backward compatibility */
+                               file.create(MODE_0640);
 
-                       if (engine->isMounted()) {
-                               INFO(SINK, "Closing all processes using internal storage.");
-                               stopSystemdUnits();
+                               if (engine->isMounted()) {
+                                       INFO(SINK, "Closing all processes using internal storage.");
+                                       stopSystemdUnits();
 
-                               INFO(SINK, "Umounting internal storage.");
-                               unmountInternalStorage("/dev/mapper/userdata");
-                               engine->umount();
+                                       INFO(SINK, "Unmounting internal storage.");
+                                       unmountInternalStorage("/dev/mapper/userdata");
+                                       engine->umount();
+                               }
                        }
 
                        INFO(SINK, "Decryption started.");
@@ -645,7 +788,7 @@ int InternalEncryptionServer::decrypt(const std::string& password)
                }
        };
 
-       std::thread asyncWork(decryptWorker);
+       std::thread asyncWork(decryptWorker, std::move(rl));
        asyncWork.detach();
 
        return error::None;
@@ -653,13 +796,25 @@ int InternalEncryptionServer::decrypt(const std::string& password)
 
 int InternalEncryptionServer::recovery()
 {
-       int state = getState();
+       RequestLifetime rl(server);
+
+       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;
+       }
 
        runtime::File file("/opt/.factoryreset");
-       file.create(0640);
+       file.create(MODE_0640);
 
        ::sync();
        try {
@@ -703,6 +858,27 @@ int InternalEncryptionServer::verifyPassword(const std::string& password)
 
 int InternalEncryptionServer::getState()
 {
+       RequestLifetime rl(server);
+
+       return getStateInternal();
+}
+
+unsigned int InternalEncryptionServer::getSupportedOptions()
+{
+       RequestLifetime rl(server);
+
+       return engine->getSupportedOptions();
+}
+
+std::string InternalEncryptionServer::getDevicePath() const
+{
+       RequestLifetime rl(server);
+
+       return engine->getSource();
+}
+
+int InternalEncryptionServer::getStateInternal() const
+{
        char *value = ::vconf_get_str(VCONFKEY_ODE_CRYPTO_STATE);
        if (value == NULL) {
                throw runtime::Exception("Failed to get vconf value.");
@@ -715,20 +891,14 @@ int InternalEncryptionServer::getState()
                return State::Encrypted;
        else if (valueStr == "unencrypted")
                return State::Unencrypted;
+       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 State::NotSupported;
 }
 
-unsigned int InternalEncryptionServer::getSupportedOptions()
-{
-       return engine->getSupportedOptions();
-}
-
-std::string InternalEncryptionServer::getDevicePath() const
-{
-       return engine->getSource();
-}
-
 } // namespace ode