2 * Copyright (c) 2015 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
18 #include <sys/types.h>
19 #include <sys/mount.h>
23 #include <notification.h>
24 #include <notification_internal.h>
25 #include <tzplatform_config.h>
26 #include <auth-passwd-admin.h>
27 #include <gum/gum-user.h>
28 #include <gum/gum-user-service.h>
29 #include <gum/common/gum-user-types.h>
30 #include <klay/error.h>
31 #include <klay/process.h>
32 #include <klay/filesystem.h>
33 #include <klay/auth/user.h>
34 #include <klay/xml/parser.h>
35 #include <klay/xml/document.h>
36 #include <klay/dbus/connection.h>
37 #include <klay/audit/logger.h>
40 #include "launchpad.h"
42 #include "rmi/manager.h"
44 #define KRATE_DELEGATOR_APP "org.tizen.keyguard"
45 #define NOTIFICATION_SUB_ICON_PATH ICON_PATH "/notification_sub_icon.png"
51 const std::vector<std::string> defaultGroups = {
58 const std::vector<std::string> unitsToMask = {
63 const std::string KRATE_SKEL_PATH = "/etc/skel";
64 const std::string KRATE_CREATE_HOOK_PATH = "/etc/gumd/useradd.d";
65 const std::string KRATE_REMOVE_HOOK_PATH = "/etc/gumd/userdel.d";
67 std::string KRATE_DEFAULT_OWNER = "owner";
69 std::list<std::string> createdKrateList;
70 static std::atomic<bool> isKrateForeground(false);
72 std::unordered_map<int, notification_h> notiHandleMap;
74 inline void maskUserServices(const runtime::User& user)
76 ::tzplatform_set_user(user.getUid());
77 std::string pivot(::tzplatform_getenv(TZ_USER_HOME));
78 ::tzplatform_reset_user();
80 runtime::File unitbase(pivot + "/.config/systemd/user");
81 unitbase.makeDirectory(true);
82 unitbase.chmod(S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
84 for (const std::string& unit : unitsToMask) {
85 std::string target = unitbase.getPath() + "/" + unit;
86 if (::symlink("/dev/null", target.c_str()) == -1) {
87 throw runtime::Exception(runtime::GetSystemErrorMessage());
92 inline void setKrateState(uid_t id, int state)
94 dbus::Connection& systemDBus = dbus::Connection::getSystem();
95 systemDBus.methodcall("org.freedesktop.login1",
96 "/org/freedesktop/login1",
97 "org.freedesktop.login1.Manager",
99 -1, "", "(ubb)", id, state, 1);
102 inline const std::string convertPathForOwner(const std::string& path, const runtime::User& user, const runtime::User& owner)
104 ::tzplatform_set_user(owner.getUid());
105 std::string ownerHome(::tzplatform_getenv(TZ_USER_HOME));
106 ::tzplatform_reset_user();
107 ownerHome += "/.krate";
109 ::tzplatform_set_user(user.getUid());
110 std::string userHome(::tzplatform_getenv(TZ_USER_HOME));
111 ::tzplatform_reset_user();
113 std::string userHomeForOwner(ownerHome + "/" + user.getName());
115 std::string convertedPath(path);
117 if (convertedPath.compare(0, userHome.size(), userHome) == 0) {
118 convertedPath.replace(0, userHome.size(), userHomeForOwner);
121 return convertedPath;
124 inline void prepareFileForOwner(const std::string path, const runtime::User& pkgUser, const runtime::User& owner)
126 std::string pathLink = convertPathForOwner(path, pkgUser, owner);
128 if (path != pathLink) {
129 runtime::File linkFile(pathLink);
130 linkFile.makeBaseDirectory(pkgUser.getUid(), pkgUser.getGid());
131 if (linkFile.exists()) {
135 int ret = ::link(path.c_str(), pathLink.c_str());
137 //TODO: copy the icon instead of linking
138 throw runtime::Exception("Failed to link from " + path +
144 int packageEventHandler(uid_t target_uid, int req_id,
145 const char *pkg_type, const char *pkgid,
146 const char *key, const char *val,
147 const void *pmsg, void *data)
149 static std::string type;
150 std::string keystr = key;
152 if (target_uid == tzplatform_getuid(TZ_SYS_GLOBALAPP_USER)) {
156 std::transform(keystr.begin(), keystr.end(), keystr.begin(), ::tolower);
157 if (keystr == "start") {
159 std::transform(type.begin(), type.end(), type.begin(), ::tolower);
161 } else if (keystr != "end" && keystr != "ok") {
166 runtime::User owner(KRATE_DEFAULT_OWNER), pkgUser(target_uid);
168 if (type == "install" || type == "update") {
169 PackageInfo pkg(pkgid, pkgUser.getUid());
170 std::string icon = pkg.getIcon();
171 prepareFileForOwner(icon, pkgUser, owner);
173 for (const ApplicationInfo& app : pkg.getAppList()) {
174 std::string icon = app.getIcon();
175 prepareFileForOwner(icon, pkgUser, owner);
178 ::tzplatform_set_user(pkgUser.getUid());
179 std::string pkgPath(::tzplatform_getenv(TZ_USER_APP));
180 pkgPath = pkgPath + "/" + pkgid;
181 ::tzplatform_reset_user();
183 runtime::File pkgDirForOwner(convertPathForOwner(pkgPath, pkgUser, owner));
184 pkgDirForOwner.remove(true);
186 } catch (runtime::Exception &e) {
193 void initializeCreatedKrateList()
195 const gchar *type[] = {"security", NULL};
197 GumUserService *service;
201 service = gum_user_service_create_sync(FALSE);
206 users = gum_user_service_get_user_list_sync(service, type);
211 for (GumUserList *src_list = users; src_list != NULL; src_list = g_list_next(src_list)) {
212 user = (GumUser*) src_list->data;
214 g_object_get(G_OBJECT(user), "username", &username, NULL);
215 createdKrateList.push_back(username);
220 gum_user_service_list_free(users);
221 g_object_unref(service);
224 #define NT_TITLE NOTIFICATION_TEXT_TYPE_TITLE
225 #define NT_CONTENT NOTIFICATION_TEXT_TYPE_CONTENT
226 #define NT_ICON NOTIFICATION_IMAGE_TYPE_ICON
227 #define NT_INDICATOR NOTIFICATION_IMAGE_TYPE_ICON_FOR_INDICATOR
228 #define NT_NONE NOTIFICATION_VARIABLE_TYPE_NONE
229 #define NT_EVENT NOTIFICATION_LY_ONGOING_PROGRESS
230 #define NT_APP NOTIFICATION_DISPLAY_APP_INDICATOR
232 #define NT_ICON_PATH ICON_PATH "/indicator_icon.png"
233 #define NT_TEXT "Container Mode"
234 #define NT_APPINFO "Krate Application"
236 #define NT_ERROR_NONE NOTIFICATION_ERROR_NONE
238 void krateProcessCallback(GDBusConnection *connection,
239 const gchar *sender, const gchar *objectPath,
240 const gchar *interface, const gchar *signalName,
241 GVariant *params, gpointer userData)
243 static runtime::User owner(KRATE_DEFAULT_OWNER);
246 notification_h noti = reinterpret_cast<notification_h>(userData);
248 g_variant_get(params, "(ii)", &status, &pid);
255 std::string proc("/proc/" + std::to_string(pid));
256 if (::stat(proc.c_str(), &st) != 0) {
260 if (st.st_uid == owner.getUid() || st.st_uid == 0) {
261 if (isKrateForeground) {
262 notification_delete_for_uid(noti, owner.getUid());
263 isKrateForeground = false;
266 if (!isKrateForeground) {
267 notification_set_text(noti, NT_CONTENT, NT_APPINFO, NULL, NT_NONE);
268 notification_post_for_uid(noti, owner.getUid());
269 isKrateForeground = true;
274 notification_h createNotification()
276 notification_h noti = notification_create(NOTIFICATION_TYPE_ONGOING);
281 if (notification_set_text(noti, NT_TITLE, NT_TEXT, NULL, NT_NONE) != NT_ERROR_NONE) {
282 notification_free(noti);
285 if (notification_set_image(noti, NT_ICON, NT_ICON_PATH) != NT_ERROR_NONE) {
286 notification_free(noti);
290 if (notification_set_image(noti, NT_INDICATOR, NT_ICON_PATH) != NT_ERROR_NONE) {
291 notification_free(noti);
295 if (notification_set_layout(noti, NT_EVENT) != NT_ERROR_NONE) {
296 notification_free(noti);
300 if (notification_set_display_applist(noti, NT_APP) != NT_ERROR_NONE) {
301 notification_free(noti);
308 void krateProcessMonitor()
310 GError *error = NULL;
311 GDBusConnection* connection;
312 connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
313 if (connection == NULL) {
314 ERROR("GBus Connection failed");
319 notification_h noti = createNotification();
321 ERROR("Failed to created notification");
325 g_dbus_connection_signal_subscribe(connection,
327 "org.tizen.resourced.process",
329 "/Org/Tizen/ResourceD/Process",
331 G_DBUS_SIGNAL_FLAGS_NONE,
332 krateProcessCallback,
333 reinterpret_cast<gpointer>(noti),
337 void notiProxyInsert(const runtime::User& owner, const runtime::User& user, int privId, notification_h noti)
339 std::string krateLauncherUri;
340 notification_h newNoti;
341 app_control_h appControl;
344 notification_clone(noti, &newNoti);
346 notification_get_pkgname(noti, &pkgId);
347 PackageInfo pkg(pkgId, user.getUid());
348 notification_set_image(newNoti, NOTIFICATION_IMAGE_TYPE_ICON, pkg.getIcon().c_str());
349 notification_set_image(newNoti, NOTIFICATION_IMAGE_TYPE_ICON_SUB, NOTIFICATION_SUB_ICON_PATH);
351 notification_get_launch_option(newNoti, NOTIFICATION_LAUNCH_OPTION_APP_CONTROL, (void *)&appControl);
352 if (appControl != NULL) {
353 char* appId = NULL, *uri = NULL;
355 app_control_get_app_id(appControl, &appId);
360 krateLauncherUri = "krate://enter/" + user.getName() + "/" + appId;
362 app_control_get_uri(appControl, &uri);
364 krateLauncherUri += "?uri=";
365 krateLauncherUri += uri;
370 app_control_set_app_id(appControl, KRATE_DELEGATOR_APP);
371 app_control_set_uri(appControl, krateLauncherUri.c_str());
372 notification_set_launch_option(newNoti, NOTIFICATION_LAUNCH_OPTION_APP_CONTROL, appControl);
375 notification_post_for_uid(newNoti, owner.getUid());
376 notiHandleMap.insert(std::make_pair(privId, newNoti));
379 void notiProxyDelete(const runtime::User& owner, int privId)
381 std::unordered_map<int, notification_h>::iterator it;
383 it = notiHandleMap.find(privId);
384 if (it == notiHandleMap.end()) {
387 notification_delete_for_uid(it->second, owner.getUid());
388 notification_free(it->second);
389 notiHandleMap.erase(it);
392 void notiProxyUpdate(const runtime::User& owner, const runtime::User& user, int privId, notification_h noti) {
393 std::unordered_map<int, notification_h>::iterator it;
397 it = notiHandleMap.find(privId);
398 if (it == notiHandleMap.end()) {
402 notification_image_type_e imageTypes[] = {
403 NOTIFICATION_IMAGE_TYPE_ICON,
404 NOTIFICATION_IMAGE_TYPE_ICON_FOR_INDICATOR,
405 NOTIFICATION_IMAGE_TYPE_ICON_FOR_LOCK,
406 NOTIFICATION_IMAGE_TYPE_THUMBNAIL,
407 NOTIFICATION_IMAGE_TYPE_THUMBNAIL_FOR_LOCK,
408 NOTIFICATION_IMAGE_TYPE_ICON_SUB,
409 NOTIFICATION_IMAGE_TYPE_BACKGROUND,
410 NOTIFICATION_IMAGE_TYPE_LIST_1,
411 NOTIFICATION_IMAGE_TYPE_LIST_2,
412 NOTIFICATION_IMAGE_TYPE_LIST_3,
413 NOTIFICATION_IMAGE_TYPE_LIST_4,
414 NOTIFICATION_IMAGE_TYPE_LIST_5,
415 NOTIFICATION_IMAGE_TYPE_BUTTON_1,
416 NOTIFICATION_IMAGE_TYPE_BUTTON_2,
417 NOTIFICATION_IMAGE_TYPE_BUTTON_3,
418 NOTIFICATION_IMAGE_TYPE_BUTTON_4,
419 NOTIFICATION_IMAGE_TYPE_BUTTON_5,
420 NOTIFICATION_IMAGE_TYPE_BUTTON_6
423 for (notification_image_type_e type : imageTypes) {
424 notification_get_image(noti, type, &str);
425 notification_set_image(it->second, type, str);
428 notification_text_type_e textTypes[] = {
429 NOTIFICATION_TEXT_TYPE_TITLE,
430 NOTIFICATION_TEXT_TYPE_CONTENT,
431 NOTIFICATION_TEXT_TYPE_CONTENT_FOR_DISPLAY_OPTION_IS_OFF,
432 NOTIFICATION_TEXT_TYPE_EVENT_COUNT,
433 NOTIFICATION_TEXT_TYPE_INFO_1,
434 NOTIFICATION_TEXT_TYPE_INFO_SUB_1,
435 NOTIFICATION_TEXT_TYPE_INFO_2,
436 NOTIFICATION_TEXT_TYPE_INFO_SUB_2,
437 NOTIFICATION_TEXT_TYPE_INFO_3,
438 NOTIFICATION_TEXT_TYPE_INFO_SUB_3,
439 NOTIFICATION_TEXT_TYPE_GROUP_TITLE,
440 NOTIFICATION_TEXT_TYPE_GROUP_CONTENT,
441 NOTIFICATION_TEXT_TYPE_GROUP_CONTENT_FOR_DISPLAY_OPTION_IS_OFF,
442 NOTIFICATION_TEXT_TYPE_BUTTON_1,
443 NOTIFICATION_TEXT_TYPE_BUTTON_2,
444 NOTIFICATION_TEXT_TYPE_BUTTON_3,
445 NOTIFICATION_TEXT_TYPE_BUTTON_4,
446 NOTIFICATION_TEXT_TYPE_BUTTON_5,
447 NOTIFICATION_TEXT_TYPE_BUTTON_6,
450 for (notification_text_type_e type : textTypes) {
451 notification_get_text(noti, type, &str);
452 notification_set_text(it->second, type, str, NULL, NOTIFICATION_VARIABLE_TYPE_NONE);
455 notification_get_size(noti, &progress);
456 notification_set_size(it->second, progress);
458 notification_get_progress(noti, &progress);
459 notification_set_progress(it->second, progress);
461 notification_update_for_uid(it->second, owner.getUid());
464 void notiProxyCallback(void *data, notification_type_e type, notification_op *op_list, int num_op)
466 static runtime::User owner(KRATE_DEFAULT_OWNER);
467 runtime::User user(*reinterpret_cast<std::string*>(data));
469 if (user.getName() == owner.getName()) {
470 // TODO : should remove noti in the krate when related-krate is removed
471 // This will be imlemented when notification bug is fixed
475 for (int i = 0; i < num_op; i++) {
476 notification_h noti = NULL;
479 notification_op_get_data(op_list + i, NOTIFICATION_OP_DATA_TYPE, &opType);
480 notification_op_get_data(op_list + i, NOTIFICATION_OP_DATA_PRIV_ID, &privId);
483 case NOTIFICATION_OP_INSERT:
484 notification_op_get_data(op_list + i, NOTIFICATION_OP_DATA_NOTI, ¬i);
485 notiProxyInsert(owner, user, privId, noti);
487 case NOTIFICATION_OP_DELETE:
488 notiProxyDelete(owner, privId);
490 case NOTIFICATION_OP_UPDATE:
491 notification_op_get_data(op_list + i, NOTIFICATION_OP_DATA_NOTI, ¬i);
492 notiProxyUpdate(owner, user, privId, noti);
500 Manager::Manager(KrateControlContext& ctx) :
503 context.registerParametricMethod(this, "", (int)(Manager::createKrate)(std::string, std::string));
504 context.registerParametricMethod(this, "", (int)(Manager::removeKrate)(std::string));
505 context.registerParametricMethod(this, "", (int)(Manager::lockKrate)(std::string));
506 context.registerParametricMethod(this, "", (int)(Manager::unlockKrate)(std::string));
507 context.registerParametricMethod(this, "", (int)(Manager::getKrateState)(std::string));
508 context.registerParametricMethod(this, "", (std::vector<std::string>)(Manager::getKrateList)(int));
509 context.registerParametricMethod(this, "", (int)(Manager::resetKratePassword)(std::string, std::string));
511 context.createNotification("Manager::created");
512 context.createNotification("Manager::removed");
514 KRATE_DEFAULT_OWNER = ::tzplatform_getenv(TZ_SYS_DEFAULT_USER);
516 PackageManager& packageManager = PackageManager::instance();
517 packageManager.setEventCallback(packageEventHandler, this);
519 krateProcessMonitor();
521 initializeCreatedKrateList();
522 for (std::string& name : createdKrateList) {
523 runtime::User krate(name);
524 notification_register_detailed_changed_cb_for_uid(notiProxyCallback, &name, krate.getUid());
532 int Manager::createKrate(const std::string& name, const std::string& manifest)
534 const std::string& pkgId = context.getPeerPackageId();
536 auto provisioningWorker = [name, manifest, pkgId, this]() {
537 std::unique_ptr<xml::Document> manifestFile;
540 //create krate user by gumd
541 GumUser* guser = NULL;
542 while (guser == NULL) {
543 guser = gum_user_create_sync(FALSE);
545 g_object_set(G_OBJECT(guser), "username", name.c_str(),
546 "usertype", GUM_USERTYPE_SECURITY, NULL);
547 gboolean ret = gum_user_add_sync(guser);
548 g_object_unref(guser);
551 throw runtime::Exception("Failed to remove user (" + name + ") by gumd");
554 runtime::User user(name);
556 maskUserServices(user);
558 ::tzplatform_set_user(user.getUid());
559 runtime::File confDir(std::string(::tzplatform_getenv(TZ_USER_HOME)) + "/.config/krate");
560 ::tzplatform_reset_user();
561 if (!confDir.exists()) {
562 confDir.makeDirectory(true);
566 manifestFile.reset(xml::Parser::parseString(manifest));
567 //write container author info
568 if (!pkgId.empty()) {
569 std::cout << manifestFile->getRootNode().getName() << std::endl;
570 manifestFile->getRootNode().addNewChild("author").setContent(pkgId);
572 manifestFile->write(confDir.getPath() + "/" + name + ".xml", "UTF-8", true);
575 setKrateState(user.getUid(), 1);
577 //wait for launchpad in the krate
580 auto it = createdKrateList.insert(createdKrateList.end(), name);
581 notification_register_detailed_changed_cb_for_uid(notiProxyCallback, &(*it), user.getUid());
582 context.notify("Manager::created", name, "");
583 } catch (runtime::Exception& e) {
585 context.notify("Manager::created", name, "Error");
589 std::thread asyncWork(provisioningWorker);
595 int Manager::removeKrate(const std::string& name)
597 const std::string& pkgId = context.getPeerPackageId();
598 std::unique_ptr<xml::Document> manifestFile;
599 bool canRemove = false;
601 runtime::User user(name);
602 ::tzplatform_set_user(user.getUid());
603 std::string confPath(::tzplatform_getenv(TZ_USER_HOME));
604 confPath += "/.config/krate";
605 ::tzplatform_reset_user();
607 manifestFile.reset(xml::Parser::parseFile(confPath + "/" + name + ".xml"));
608 xml::Node::NodeList authors = manifestFile->evaluate("/manifest/author");
609 for (const xml::Node& author : authors) {
610 if (author.getContent() == pkgId) {
620 if (lockKrate(name) != 0) {
624 auto remove = [name, this] {
625 //wait for krate session close
629 runtime::User user(name);
631 //umount TZ_USER_CONTENT
632 ::tzplatform_set_user(user.getUid());
633 ::umount2(::tzplatform_getenv(TZ_USER_CONTENT), MNT_FORCE);
634 ::tzplatform_reset_user();
637 GumUser* guser = NULL;
638 while (guser == NULL) {
639 guser = gum_user_get_sync(user.getUid(), FALSE);
641 gboolean ret = gum_user_delete_sync(guser, TRUE);
642 g_object_unref(guser);
645 throw runtime::Exception("Failed to remove user " + name + "(" + std::to_string(user.getUid()) + ") by gumd");
648 auto it = createdKrateList.begin();
649 for (; it != createdKrateList.end(); it++) {
654 createdKrateList.erase(it);
656 notification_unregister_detailed_changed_cb_for_uid(notiProxyCallback, NULL, user.getUid());
657 context.notify("Manager::removed", name, "");
658 } catch (runtime::Exception& e) {
660 context.notify("Manager::removed", name, "Error");
665 std::thread asyncWork(remove);
671 int Manager::lockKrate(const std::string& name)
674 runtime::User user(name);
675 setKrateState(user.getUid(), 0);
676 } catch (runtime::Exception& e) {
684 int Manager::unlockKrate(const std::string& name)
687 runtime::User user(name);
688 setKrateState(user.getUid(), 1);
689 } catch (runtime::Exception& e) {
697 int Manager::getKrateState(const std::string& name)
699 auto it = std::find(createdKrateList.begin(), createdKrateList.end(), name);
700 if (it == createdKrateList.end()) {
705 runtime::User user(name);
707 dbus::Connection& systemDBus = dbus::Connection::getSystem();
708 const dbus::Variant& var = systemDBus.methodcall
709 ("org.freedesktop.login1",
710 "/org/freedesktop/login1",
711 "org.freedesktop.login1.Manager",
713 -1, "(o)", "(u)", user.getUid());
714 return Manager::State::Running;
715 } catch (runtime::Exception& e) {
716 return Manager::State::Locked;
718 } catch (runtime::Exception& e) {
726 std::vector<std::string> Manager::getKrateList(int state)
728 std::vector<std::string> list;
730 for (const std::string& name : createdKrateList) {
731 if (getKrateState(name) & state) {
732 list.push_back(name);
738 int Manager::resetKratePassword(const std::string& name, const std::string& newPassword)
741 runtime::User user(name);
742 int ret = auth_passwd_reset_passwd(AUTH_PWD_NORMAL, user.getUid(), newPassword.c_str());
743 if (ret != AUTH_PASSWD_API_SUCCESS) {
744 throw runtime::Exception("Failed to reset password for " + name);
746 } catch (runtime::Exception& e) {