2 * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
20 #include <sys/mount.h>
24 #include <klay/file-user.h>
25 #include <klay/filesystem.h>
26 #include <klay/dbus/variant.h>
27 #include <klay/dbus/connection.h>
30 #include "ext4-tool.h"
31 #include "engine/encryption/cryptsetup-engine.h"
32 #include "key-manager/key-manager.h"
34 #include "rmi/extension-encryption.h"
36 #define EXTENSION_ENGINE CryptsetupEngine
37 #define EXTENSION_NAME_DEF "extension"
43 const size_t DEFAULT_KEY_SIZE = 64;
45 const char *EXTENSION_DEV_PATH = "/dev/mmcblk1p1";
46 const char *EXTENSION_NAME = EXTENSION_NAME_DEF;
47 const char *EXTENSION_MAP_PATH = "/dev/mapper/" EXTENSION_NAME_DEF;
48 const char *EXTENSION_FS_TYPE = "crypto_LUKS";
50 const char *PRIVILEGE_PLATFORM = "http://tizen.org/privilege/internal/default/platform";
52 const char *STORAGED_DBUS_BUSNAME = "org.tizen.system.storage";
53 const char *STORAGED_DBUS_OBJECT = "/Org/Tizen/System/Storage/Block/Manager";
54 const char *STORAGED_DBUS_INTERFACE = "org.tizen.system.storage.BlockManager";
56 std::unique_ptr<EXTENSION_ENGINE> engine;
59 std::mutex stateGuard;
60 std::condition_variable storagedCv;
62 std::string findMntPath(const std::string &devPath)
66 FILE* mtab = setmntent("/etc/mtab", "r");
67 struct mntent* entry = NULL;
68 while ((entry = getmntent(mtab)) != NULL) {
69 if (devPath == entry->mnt_fsname) {
79 void killDependedApplications(const std::string &mntPath)
81 for (pid_t pid : runtime::FileUser::getList(mntPath, true)) {
82 INFO(SINK, "Close process - " + std::to_string(pid));
87 bool findKillAndUmount(const std::string &devPath)
89 std::string realMntPath = findMntPath(devPath);
90 if (!realMntPath.empty()) {
91 INFO(SINK, "Closing all applications using an SD card...");
92 killDependedApplications(realMntPath);
94 int ret = ::umount(realMntPath.c_str());
96 ERROR(SINK, "The card is still mounted, umount failed with: "
97 + std::to_string(ret));
107 ExtensionEncryption::ExtensionEncryption(ODEControlContext &ctx) :
109 currentReq(Request::NONE)
111 context.expose(this, PRIVILEGE_PLATFORM, (int)(ExtensionEncryption::mount)(std::string));
112 context.expose(this, PRIVILEGE_PLATFORM, (int)(ExtensionEncryption::umount)());
113 context.expose(this, PRIVILEGE_PLATFORM, (int)(ExtensionEncryption::format)(std::string));
114 context.expose(this, "", (int)(ExtensionEncryption::isPasswordInitialized)());
115 context.expose(this, PRIVILEGE_PLATFORM, (int)(ExtensionEncryption::initPassword)(std::string));
116 context.expose(this, PRIVILEGE_PLATFORM, (int)(ExtensionEncryption::cleanPassword)(std::string));
117 context.expose(this, PRIVILEGE_PLATFORM, (int)(ExtensionEncryption::changePassword)(std::string, std::string));
118 context.expose(this, PRIVILEGE_PLATFORM, (int)(ExtensionEncryption::verifyPassword)(std::string));
119 context.expose(this, "", (int)(ExtensionEncryption::getState)());
121 context.createNotification("ExtensionEncryption::mount");
123 // TODO: think about handling more than one card / more than one engine
124 // it would require global API change to select which card is handled atm
125 engine.reset(new EXTENSION_ENGINE(EXTENSION_DEV_PATH));
128 subscribeToStoraged();
131 ExtensionEncryption::~ExtensionEncryption()
133 unsubscribeFromStoraged();
136 int ExtensionEncryption::mount(const std::string& password)
138 std::lock_guard<std::mutex> guardLock(apiGuard);
139 std::unique_lock<std::mutex> stateLock(stateGuard);
141 if (getStatePriv() != State::Encrypted) {
142 ERROR(SINK, "Cannot mount, card not inserted or corrupted");
146 KeyManager::data pwData(password.begin(), password.end());
147 KeyManager keyManager(engine->getKeyMeta());
149 if (!keyManager.verifyPassword(pwData)) {
150 ERROR(SINK, "Wrong password passed.");
155 INFO(SINK, "Already mounted");
159 KeyManager::data mountKey = keyManager.getMasterKey(pwData);
161 INFO(SINK, "Mount extension storage...");
164 // Storaged will mount MAP automatically when it appears, let's prepare
165 currentReq = Request::MOUNT;
168 INFO(SINK, "Open the MAP of an extension storage...");
169 engine->open(CryptsetupEngine::DeviceType::LUKS, EXTENSION_NAME, mountKey);
170 } catch (runtime::Exception &e) {
171 ERROR(SINK, "Open failed: " + std::string(e.what()));
175 INFO(SINK, "Wait for the storaged to mount the MAP automatically...");
176 if (!storagedCv.wait_for(stateLock, std::chrono::seconds(1), [this] {
177 return currentReq == Request::NONE;
179 ERROR(SINK, "Storaged timed out mounting the MAP.");
183 INFO(SINK, "Ask storaged to mount extension storage...");
184 if (!storagedMount(stateLock)) {
189 context.notify("ExtensionEncryption::mount");
193 int ExtensionEncryption::umount()
195 std::lock_guard<std::mutex> guardLock(apiGuard);
196 std::unique_lock<std::mutex> stateLock(stateGuard);
198 if (getStatePriv() != State::Encrypted) {
199 ERROR(SINK, "Cannot umount, card not inserted or corrupted.");
203 if (!isMounted() && !isOpened()) {
204 INFO(SINK, "Already umounted.");
208 INFO(SINK, "Umount extension storage...");
211 INFO(SINK, "Close all applications using extension storage...");
212 killDependedApplications(info[Device::MAP].mntPath);
214 INFO(SINK, "Ask storaged to umount extension storage...");
215 if (!storagedUnmount(stateLock)) {
221 INFO(SINK, "Close the MAP of an extension storage...");
223 CryptsetupEngine::close(EXTENSION_NAME);
224 } catch (runtime::Exception &e) {
225 ERROR(SINK, "Close failed: " + std::string(e.what()));
233 int ExtensionEncryption::format(const std::string &password)
236 std::condition_variable workerCv;
238 auto formatWorker = [&, this]() {
240 std::lock_guard<std::mutex> guardLock(apiGuard);
241 std::unique_lock<std::mutex> stateLock(stateGuard);
244 ERROR(SINK, "There is no card inserted.");
247 workerCv.notify_one();
251 if (isMounted() || isOpened()) {
252 ERROR(SINK, "The card is still mounted and/or opened, umount and close first.");
255 workerCv.notify_one();
259 KeyManager::data pwData(password.begin(), password.end());
260 KeyManager keyManager(engine->getKeyMeta());
262 if (!keyManager.verifyPassword(pwData)) {
263 ERROR(SINK, "Wrong password passed.");
266 workerCv.notify_one();
270 // no error, let's notify the main thread it can return
273 workerCv.notify_one();
275 KeyManager::data masterKey = keyManager.getMasterKey(pwData);
277 // TODO: this probably needs a rework, some sort of communication
278 // and/or merge with External. The use case for it might be:
279 // 1. The mmc is mounted by something else, somewhere else. Do we care?
280 // 2. We take over the cd card from External.
281 if (!findKillAndUmount(EXTENSION_DEV_PATH))
284 INFO(SINK, "Creating LUKS...");
285 engine->format(CryptsetupEngine::DeviceType::LUKS, masterKey);
287 INFO(SINK, "Opening LUKS...");
288 std::string mappingPath = engine->open(CryptsetupEngine::DeviceType::LUKS,
292 INFO(SINK, "Creating EXT4...");
293 Ext4Tool::mkfs(mappingPath);
295 INFO(SINK, "Closing up the operation...");
296 CryptsetupEngine::close(EXTENSION_NAME);
299 INFO(SINK, "Formatting completed");
300 } catch (runtime::Exception &e) {
301 ERROR(SINK, "Formatting thread failed: " + std::string(e.what()));
305 std::thread asyncWork(formatWorker);
308 std::unique_lock<std::mutex> stateLock(stateGuard);
309 if(!workerCv.wait_for(stateLock, std::chrono::seconds(1), [&status] {
312 ERROR(SINK, "Timed out waiting for format status.");
322 int ExtensionEncryption::isPasswordInitialized()
324 std::lock_guard<std::mutex> guardLock(apiGuard);
326 if (engine->isKeyMetaSet()) {
333 int ExtensionEncryption::initPassword(const std::string& password)
335 std::lock_guard<std::mutex> guardLock(apiGuard);
337 KeyManager::data pwData(password.begin(), password.end());
338 KeyManager keyManager;
340 keyManager.initPassword(pwData, DEFAULT_KEY_SIZE);
341 engine->setKeyMeta(keyManager.serialize());
345 int ExtensionEncryption::cleanPassword(const std::string& password)
347 std::lock_guard<std::mutex> guardLock(apiGuard);
349 KeyManager::data pwData(password.begin(), password.end());
350 KeyManager keyManager(engine->getKeyMeta());
352 if (!keyManager.verifyPassword(pwData)) {
353 ERROR(SINK, "Wrong password passed.");
357 engine->clearKeyMeta();
361 int ExtensionEncryption::changePassword(const std::string &oldPassword,
362 const std::string &newPassword)
364 std::lock_guard<std::mutex> guardLock(apiGuard);
366 KeyManager::data oldPwData(oldPassword.begin(), oldPassword.end());
367 KeyManager::data newPwData(newPassword.begin(), newPassword.end());
368 KeyManager keyManager(engine->getKeyMeta());
370 if (!keyManager.verifyPassword(oldPwData)) {
371 ERROR(SINK, "Wrong password passed.");
375 keyManager.changePassword(oldPwData, newPwData);
376 engine->setKeyMeta(keyManager.serialize());
381 int ExtensionEncryption::verifyPassword(const std::string& password)
383 std::lock_guard<std::mutex> guardLock(apiGuard);
385 KeyManager::data pwData(password.begin(), password.end());
386 KeyManager keyManager(engine->getKeyMeta());
388 if (keyManager.verifyPassword(pwData)) {
395 int ExtensionEncryption::getState()
397 std::lock_guard<std::mutex> guardLock(apiGuard);
398 std::unique_lock<std::mutex> stateLock(stateGuard);
400 return getStatePriv();
403 void ExtensionEncryption::logStoragedEvent(Operation op, Device d)
405 DEBUG(SINK, "Storaged event:");
408 case Operation::ADDED:
409 DEBUG(SINK, " Operation: ADDED");
411 case Operation::CHANGED:
412 DEBUG(SINK, " Operation: CHANGED");
414 case Operation::REMOVED:
415 DEBUG(SINK, " Operation: REMOVED");
423 DEBUG(SINK, " Device: MMC");
426 DEBUG(SINK, " Device: MAP");
432 DEBUG(SINK, " Mnt Path: " + info[d].mntPath);
433 DEBUG(SINK, " Fs Type: " + info[d].fsType);
434 DEBUG(SINK, " Sys path: " + info[d].sysPath);
435 DEBUG(SINK, " Mounted: " + std::to_string(info[d].mounted));
436 DEBUG(SINK, " ID: " + std::to_string(info[d].storagedId));
439 void ExtensionEncryption::handleDevice(Operation op,
440 const std::vector<int> &intparams,
441 const std::vector<char*> &strparams)
445 if (std::string(strparams[0]) == EXTENSION_DEV_PATH && intparams[0] == 1) {
447 } else if (std::string(strparams[0]) == EXTENSION_MAP_PATH && intparams[0] == 2) {
450 DEBUG(SINK, "Storaged event: neither extension MMC nor extension mapping, ignoring");
454 std::unique_lock<std::mutex> stateLock(stateGuard);
456 if (op == Operation::REMOVED) {
459 info[d].mntPath = strparams[6];
460 info[d].fsType = strparams[3];
461 info[d].sysPath = strparams[1];
462 info[d].mounted = intparams[2];
463 info[d].storagedId = intparams[5];
466 logStoragedEvent(op, d);
468 // removing/reformatting the SD card does not automatically close the DM crypt mapping
469 if (d == Device::MMC && (op == Operation::REMOVED ||
470 (op == Operation::CHANGED &&
471 info[d].fsType != EXTENSION_FS_TYPE))) {
473 INFO(SINK, "MMC card removed while still mounted, umounting.");
474 if (!findKillAndUmount(EXTENSION_MAP_PATH)) {
480 INFO(SINK, "MMC card removed while LUKS is still opened, closing.");
482 CryptsetupEngine::close(EXTENSION_NAME);
483 } catch (runtime::Exception &e) {
484 ERROR(SINK, "Closing failed: " + std::string(e.what()));
492 if (d == Device::MMC && op == Operation::ADDED &&
493 getStatePriv() == State::Encrypted) {
494 // TODO: when UI gets written it should be triggered here.
499 if (d == Device::MAP && op == Operation::CHANGED) {
500 if ((currentReq == Request::MOUNT && info[Device::MAP].mounted) ||
501 (currentReq == Request::UMOUNT && !info[Device::MAP].mounted)) {
503 currentReq = Request::NONE;
505 storagedCv.notify_one();
512 void ExtensionEncryption::parseVariant(Operation op, dbus::Variant parameters)
514 std::vector<int> intparams(6);
515 std::vector<char*> strparams(7);
517 parameters.get("(issssssisibii)",
518 &intparams[0], // block type: 0 - scsi, 1 : mmc, 2 : mapper
519 &strparams[0], // devnode
520 &strparams[1], // syspath
521 &strparams[2], // usage
522 &strparams[3], // fs type
523 &strparams[4], // fs version
524 &strparams[5], // fs uuid enc
525 &intparams[1], // readonly: 0 - rw, 1 - ro
526 &strparams[6], // mount point
527 &intparams[2], // state: 0 - unmount, 1 - mount
528 &intparams[3], // primary: 0 - false, 1 - true
529 &intparams[4], // flags: 1 - unmounted 2 - broken filesystem 4 - no filesystem 8 - not supported 16 - readonly
530 &intparams[5]); // storage id
532 handleDevice(op, intparams, strparams);
535 void ExtensionEncryption::queryStoraged()
537 INFO(SINK, "Querying the storaged for devices...");
539 dbus::VariantIterator vi;
542 dbus::Connection::getSystem().methodcall(STORAGED_DBUS_BUSNAME,
543 STORAGED_DBUS_OBJECT,
544 STORAGED_DBUS_INTERFACE,
547 "(a(issssssisibii))",
549 "all").get("(a(issssssisibii))", &vi);
550 } catch (runtime::Exception &e) {
551 ERROR(SINK, "Failed to query storaged: " + std::string(e.what()));
554 std::vector<int> intparams(6);
555 std::vector<char*> strparams(7);
557 while(vi.get("(issssssisibii)",
558 &intparams[0], // block type: 0 - scsi, 1 : mmc, 2 : mapper
559 &strparams[0], // devnode
560 &strparams[1], // syspath
561 &strparams[2], // usage
562 &strparams[3], // fs type
563 &strparams[4], // fs version
564 &strparams[5], // fs uuid enc
565 &intparams[1], // readonly: 0 - rw, 1 - ro
566 &strparams[6], // mount point
567 &intparams[2], // state: 0 - unmount, 1 - mount
568 &intparams[3], // primary: 0 - false, 1 - true
569 &intparams[4], // flags: 1 - unmounted 2 - broken filesystem 4 - no filesystem 8 - not supported 16 - readonly
570 &intparams[5])) // storage id
572 handleDevice(Operation::ADDED, intparams, strparams);
576 void ExtensionEncryption::subscribeToStoraged()
578 dbus::Connection &systemDBus = dbus::Connection::getSystem();
580 auto subscribe = [&systemDBus, this](Operation op, const std::string &method) {
581 dbus::Connection::SignalCallback callback =
582 std::bind(&ExtensionEncryption::parseVariant,
585 std::placeholders::_1);
587 subId[op] = systemDBus.subscribeSignal("",
588 STORAGED_DBUS_OBJECT,
589 STORAGED_DBUS_INTERFACE,
594 subscribe(Operation::ADDED, "DeviceAdded");
595 subscribe(Operation::CHANGED, "DeviceChanged");
596 subscribe(Operation::REMOVED, "DeviceRemoved");
599 void ExtensionEncryption::unsubscribeFromStoraged()
601 dbus::Connection &systemDBus = dbus::Connection::getSystem();
603 systemDBus.unsubscribeSignal(subId[Operation::ADDED]);
604 systemDBus.unsubscribeSignal(subId[Operation::CHANGED]);
605 systemDBus.unsubscribeSignal(subId[Operation::REMOVED]);
608 bool ExtensionEncryption::storagedMount(std::unique_lock<std::mutex> &lock)
610 currentReq = Request::MOUNT;
614 dbus::Connection::getSystem().methodcall(STORAGED_DBUS_BUSNAME,
615 STORAGED_DBUS_OBJECT,
616 STORAGED_DBUS_INTERFACE,
621 info[Device::MAP].storagedId,
622 info[Device::MAP].mntPath.c_str()).get("(i)", &ret);
623 } catch (runtime::Exception &e) {
624 ERROR(SINK, "Failed to call mount in storaged: " + std::string(e.what()));
625 currentReq = Request::NONE;
630 ERROR(SINK, "Storaged failed to mount: " + std::to_string(ret));
631 currentReq = Request::NONE;
635 if (!storagedCv.wait_for(lock, std::chrono::seconds(1), [this] {
636 return currentReq == Request::NONE;
638 ERROR(SINK, "Storaged timed out mounting the MAP.");
639 currentReq = Request::NONE;
646 bool ExtensionEncryption::storagedUnmount(std::unique_lock<std::mutex> &lock)
648 currentReq = Request::UMOUNT;
652 dbus::Connection::getSystem().methodcall(STORAGED_DBUS_BUSNAME,
653 STORAGED_DBUS_OBJECT,
654 STORAGED_DBUS_INTERFACE,
659 info[Device::MAP].storagedId,
661 } catch (runtime::Exception &e) {
662 ERROR(SINK, "Failed to call unmount in storaged: " + std::string(e.what()));
663 currentReq = Request::NONE;
668 ERROR(SINK, "Storaged failed to unmount: " + std::to_string(ret));
669 currentReq = Request::NONE;
673 if (!storagedCv.wait_for(lock, std::chrono::seconds(1), [this] {
674 return currentReq == Request::NONE;
676 ERROR(SINK, "Storaged timed out unmounting the MAP.");
677 currentReq = Request::NONE;
684 int ExtensionEncryption::getStatePriv() const
687 ERROR(SINK, "Cannot check state, card not inserted");
691 if (info[Device::MMC].fsType == EXTENSION_FS_TYPE)
692 return State::Encrypted;
694 return State::Corrupted;
697 bool ExtensionEncryption::isInserted() const
699 return info[Device::MMC].storagedId >= 0;
702 bool ExtensionEncryption::isOpened() const
704 return info[Device::MAP].storagedId >= 0;
707 bool ExtensionEncryption::isMounted() const
709 return info[Device::MAP].mounted;