Add exception handling in threads for encryption/decryption
[platform/core/security/ode.git] / server / external-encryption.cpp
index e538119..42d3b04 100644 (file)
  *  See the License for the specific language governing permissions and
  *  limitations under the License
  */
+#include <fstream>
+#include <sstream>
+
+#include <signal.h>
+#include <unistd.h>
+#include <sys/mount.h>
+
+#include <vconf.h>
+#include <tzplatform_config.h>
+
+#include <klay/file-user.h>
 #include <klay/filesystem.h>
 #include <klay/audit/logger.h>
 
+#include "launchpad.h"
+#include "app-bundle.h"
 #include "engine/ecryptfs-engine.h"
-#include "key-manager/keystore.h"
-#include "key-manager/key-generator.h"
+#include "key-manager/key-manager.h"
+#include <klay/dbus/variant.h>
+#include <klay/dbus/connection.h>
 
 #include "rmi/external-encryption.h"
+#include "progress-bar.h"
+#include "progress-vconf-backend.h"
+
+#define EXTERNAL_STORAGE_PATH   "/opt/media/SDCardA1"
+#define DEFAULT_USER "owner"
+#define EXTERNAL_STATE_VCONF_KEY VCONFKEY_SDE_CRYPTO_STATE
+#define EXTERNAL_OPTION_ONLY_NEW_FILE_VCONF_KEY VCONFKEY_SDE_ENCRYPT_NEWFILE
+#define EXTERNAL_OPTION_EXCEPT_FOR_MEDIA_FILE_VCONF_KEY VCONFKEY_SDE_EXCLUDE_MEDIAFILE
 
 namespace ode {
 
-ExternalEncryption::ExternalEncryption(ODEControlContext& ctx) :
+namespace {
+
+VConfBackend vconfBackend(VCONFKEY_SDE_ENCRYPT_PROGRESS);
+ProgressBar progressBar(std::bind(&VConfBackend::update, &vconfBackend, std::placeholders::_1));
+
+EcryptfsEngine engine(EXTERNAL_STORAGE_PATH, EXTERNAL_STORAGE_PATH, progressBar);
+
+void killDependedApplications()
+{
+       for (pid_t pid : runtime::FileUser::getList(EXTERNAL_STORAGE_PATH, true)) {
+               INFO("Close process - " + std::to_string(pid));
+               ::kill(pid, SIGKILL);
+       }
+}
+
+void externalCallback(dbus::Variant parameters)
+{
+       int intparams[6];
+       char* strparams[7];
+
+       parameters.get("(issssssisibii)",
+               &intparams[0], // block type: 0 - scsi, 1 : mmc
+               &strparams[0], // devnode
+               &strparams[1], // syspath
+               &strparams[2], // usage
+               &strparams[3], // fs type
+               &strparams[4], // fs version
+               &strparams[5], // fs uuid enc
+               &intparams[1], // readonly: 0 - rw, 1 - ro
+               &strparams[6], // mount point
+               &intparams[2], // state: 0 - unmount, 1 - mount
+               &intparams[3], // primary: 0 - flase, 1 - true
+               &intparams[4], // flags: 1 - unmounted 2 - broken filesystem 4 - no filesystem 8 - not supported 16 - readonly
+               &intparams[5]); // strage id
+
+       if(intparams[2] == 0) {
+               INFO("Unmounted");
+       } else {
+               INFO("Mounted");
+               char *value = ::vconf_get_str(EXTERNAL_STATE_VCONF_KEY);
+               if (value != NULL) {
+                       std::string valueStr(value);
+                       free(value);
+                       if (valueStr == "encrypted") {
+                               try {
+                                       INFO("Launch SD card password popup");
+                                       AppBundle bundle;
+                                       bundle.add("viewtype", "SD_CARD_PASSWORD");
+
+                                       Launchpad launchpad(::tzplatform_getuid(TZ_SYS_DEFAULT_USER));
+                                       launchpad.launch("org.tizen.ode", bundle);
+                               } catch (runtime::Exception &e) {
+                                       ERROR("Failed to launch SD card password popup");
+                               }
+                       }
+               }
+       }
+}
+
+void externalAddEventReceiver()
+{
+       dbus::Connection &systemDBus = dbus::Connection::getSystem();
+
+       systemDBus.subscribeSignal("",
+                                                               "/Org/Tizen/System/Storage/Block/Manager",
+                                                               "org.tizen.system.storage.BlockManager",
+                                                               "DeviceChanged",
+                                                               externalCallback);
+}
+
+unsigned int getOptions()
+{
+       unsigned int result = 0;
+       int value;
+
+       value = 0;
+       ::vconf_get_bool(EXTERNAL_OPTION_EXCEPT_FOR_MEDIA_FILE_VCONF_KEY, &value);
+       if (value) {
+               result |= ExternalEncryption::Option::OnlyNewFile;
+       }
+
+       value = 0;
+       ::vconf_get_bool(EXTERNAL_OPTION_ONLY_NEW_FILE_VCONF_KEY, &value);
+       if (value) {
+               result |= ExternalEncryption::Option::ExceptForMediaFile;
+       }
+
+       return result;
+}
+
+void setOptions(unsigned int options)
+{
+       bool value;
+
+       if (options & ExternalEncryption::Option::OnlyNewFile) {
+               value = true;
+       } else {
+               value = false;
+       }
+       ::vconf_set_bool(EXTERNAL_OPTION_EXCEPT_FOR_MEDIA_FILE_VCONF_KEY, value);
+
+       if (options & ExternalEncryption::Option::ExceptForMediaFile) {
+               value = true;
+       } else {
+               value = false;
+       }
+       ::vconf_set_bool(EXTERNAL_OPTION_ONLY_NEW_FILE_VCONF_KEY, value);
+}
+
+} // namsepace
+
+ExternalEncryption::ExternalEncryption(ODEControlContext &ctx) :
        context(ctx)
 {
        context.registerParametricMethod(this, "", (int)(ExternalEncryption::mount)(std::string));
        context.registerNonparametricMethod(this, "", (int)(ExternalEncryption::umount));
-       context.registerParametricMethod(this, "", (int)(ExternalEncryption::encrypt)(std::string));
+       context.registerParametricMethod(this, "", (int)(ExternalEncryption::encrypt)(std::string, unsigned int));
        context.registerParametricMethod(this, "", (int)(ExternalEncryption::decrypt)(std::string));
+       context.registerNonparametricMethod(this, "", (int)(ExternalEncryption::isPasswordInitialized));
+       context.registerParametricMethod(this, "", (int)(ExternalEncryption::initPassword)(std::string));
+       context.registerParametricMethod(this, "", (int)(ExternalEncryption::cleanPassword)(std::string));
        context.registerParametricMethod(this, "", (int)(ExternalEncryption::changePassword)(std::string, std::string));
+       context.registerParametricMethod(this, "", (int)(ExternalEncryption::verifyPassword)(std::string));
        context.registerNonparametricMethod(this, "", (int)(ExternalEncryption::getState));
-}
+       context.registerNonparametricMethod(this, "", (unsigned int)(ExternalEncryption::getSupportedOptions));
 
+       externalAddEventReceiver();
+}
 
 ExternalEncryption::~ExternalEncryption()
 {
 }
 
-int ExternalEncryption::mount(const std::stringpassword)
+int ExternalEncryption::mount(const std::string &password)
 {
-       //TODO
+       if (getState() != State::Encrypted) {
+               return -1;
+       }
+
+       KeyManager::data data(password.begin(), password.end());
+       KeyManager keyManager(engine.getKeyMeta());
+
+       if (!keyManager.verifyPassword(data)) {
+               return -2;
+       }
+
+       engine.mount(keyManager.getMasterKey(data), getOptions());
        return 0;
 }
 
 int ExternalEncryption::umount()
 {
-       //TODO
+       if (getState() != State::Encrypted) {
+               return -1;
+       }
+
+       INFO("Close all applications using external storage...");
+       killDependedApplications();
+       INFO("Umount external storage...");
+       engine.umount();
+
        return 0;
 }
 
-int ExternalEncryption::encrypt(const std::string& password)
+int ExternalEncryption::encrypt(const std::string &password, unsigned int options)
 {
-       //TODO
+       if (getState() != State::Unencrypted) {
+               return -1;
+       }
+
+       KeyManager::data pwData(password.begin(), password.end());
+       KeyManager keyManager(engine.getKeyMeta());
+
+       if (!keyManager.verifyPassword(pwData)) {
+               return -2;
+       }
+
+       KeyManager::data MasterKey = keyManager.getMasterKey(pwData);
+       auto encryptWorker = [&MasterKey, options, this]() {
+               try {
+                       INFO("Close all applications using external storage...");
+                       killDependedApplications();
+                       INFO("Encryption started...");
+                       ::vconf_set_str(EXTERNAL_STATE_VCONF_KEY, "error_partially_encrypted");
+                       engine.encrypt(MasterKey, options);
+                       setOptions(options & getSupportedOptions());
+                       INFO("Sync disk...");
+                       sync();
+                       INFO("Encryption completed");
+                       ::vconf_set_str(EXTERNAL_STATE_VCONF_KEY, "encrypted");
+               } catch (runtime::Exception &e) {
+                       ERROR("Encryption failed - " + std::string(e.what()));
+               }
+       };
+
+       std::thread asyncWork(encryptWorker);
+       asyncWork.detach();
+
+       return 0;
+}
+
+int ExternalEncryption::decrypt(const std::string &password)
+{
+       if (getState() != State::Encrypted) {
+               return -1;
+       }
+
+       KeyManager::data pwData(password.begin(), password.end());
+       KeyManager keyManager(engine.getKeyMeta());
+
+       if (!keyManager.verifyPassword(pwData)) {
+               return -2;
+       }
+
+       KeyManager::data MasterKey = keyManager.getMasterKey(pwData);
+       auto decryptWorker = [MasterKey, this]() {
+               try {
+                       INFO("Close all applications using external storage...");
+                       killDependedApplications();
+                       INFO("Umount external storage...");
+                       while (1) {
+                               try {
+                                       engine.umount();
+                                       break;
+                               } catch (runtime::Exception &e) {
+                                       killDependedApplications();
+                               }
+                       }
+
+                       INFO("Decryption started...");
+                       ::vconf_set_str(EXTERNAL_STATE_VCONF_KEY, "error_partially_encrypted");
+                       engine.decrypt(MasterKey, getOptions());
+                       INFO("Sync disk...");
+                       sync();
+                       INFO("Decryption completed");
+                       ::vconf_set_str(EXTERNAL_STATE_VCONF_KEY, "unencrypted");
+               } catch (runtime::Exception &e) {
+                       ERROR("Decryption failed - " + std::string(e.what()));
+               }
+       };
+
+       std::thread asyncWork(decryptWorker);
+       asyncWork.detach();
+
        return 0;
 }
 
-int ExternalEncryption::decrypt(const std::string& password)
+int ExternalEncryption::isPasswordInitialized()
 {
-       //TODO
+       if (engine.isKeyMetaSet()) {
+               return 1;
+       }
        return 0;
 }
 
-int ExternalEncryption::changePassword(const std::string& oldPassword,
-                                                                               const std::string& newPassword)
+int ExternalEncryption::initPassword(const std::string& password)
 {
-       //TODO
+       KeyManager::data pwData(password.begin(), password.end());
+       KeyManager keyManager;
+
+       keyManager.initPassword(pwData);
+       engine.setKeyMeta(keyManager.serialize());
+       return 0;
+}
+
+int ExternalEncryption::cleanPassword(const std::string& password)
+{
+       KeyManager::data pwData(password.begin(), password.end());
+       KeyManager keyManager(engine.getKeyMeta());
+
+       if (!keyManager.verifyPassword(pwData)) {
+               return -2;
+       }
+
+       engine.clearKeyMeta();
+       return 0;
+}
+
+int ExternalEncryption::changePassword(const std::string &oldPassword,
+                                                                          const std::string &newPassword)
+{
+       KeyManager::data oldPwData(oldPassword.begin(), oldPassword.end());
+       KeyManager::data newPwData(newPassword.begin(), newPassword.end());
+       KeyManager keyManager(engine.getKeyMeta());
+
+       if (!keyManager.verifyPassword(oldPwData)) {
+               return -2;
+       }
+
+       keyManager.changePassword(oldPwData, newPwData);
+       engine.setKeyMeta(keyManager.serialize());
+
+       return 0;
+}
+
+int ExternalEncryption::verifyPassword(const std::string& password)
+{
+       KeyManager::data pwData(password.begin(), password.end());
+       KeyManager keyManager(engine.getKeyMeta());
+
+       if (keyManager.verifyPassword(pwData)) {
+               return 1;
+       }
        return 0;
 }
 
 int ExternalEncryption::getState()
 {
-       //TODO
+       char *value = ::vconf_get_str(EXTERNAL_STATE_VCONF_KEY);
+       if (value == NULL) {
+               throw runtime::Exception("Failed to get vconf value");
+       }
+
+       std::string valueStr(value);
+       free(value);
+
+       if (valueStr == "encrypted") {
+               return State::Encrypted;
+       } else if (valueStr == "unencrypted") {
+               return State::Unencrypted;
+       } else {
+               return State::Corrupted;
+       }
+
        return 0;
 }
 
+unsigned int ExternalEncryption::getSupportedOptions()
+{
+       return engine.getSupportedOptions();
+}
+
 } // namespace ode