/*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2016 - 2019 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 <algorithm>
#include <cstring>
+#include <fcntl.h>
#include <ftw.h>
#include <grp.h>
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <unordered_map>
#include <cstdlib>
#include <unordered_set>
#include <vector>
#include <security-manager-types.h>
+#include <app-runtime.h>
#include <sys/smack.h>
+#include <privilege_info.h>
+#include <scoped_process_label.h>
#include <cynara_test_client.h>
#include <dpl/test/test_runner.h>
#include <memory.h>
#include <sm_api.h>
#include <sm_commons.h>
-#include <sm_db.h>
#include <synchronization_pipe.h>
#include <sm_request.h>
#include <tests_common.h>
+#include <policy_configuration.h>
#include "tzplatform.h"
+#include <label_generator.h>
using namespace SecurityManagerTest;
-// Common const values
-
-const privileges_t SM_ALLOWED_PRIVILEGES = {
- "http://tizen.org/privilege/display",
- "http://tizen.org/privilege/nfc"
-};
-
-const privileges_t SM_DENIED_PRIVILEGES = {
- "http://tizen.org/privilege/bluetooth",
- "http://tizen.org/privilege/power"
-};
-
-const privileges_t SM_NO_PRIVILEGES = {
-};
-
-const std::vector<std::string> SM_ALLOWED_GROUPS = {"db_browser", "db_alarm"};
-
-const std::string uidToStr(const uid_t uid)
-{
- return std::to_string(static_cast<unsigned int>(uid));
-}
-
-// Common implementation details
-
-std::string generateProcessLabel(const std::string &appId, const std::string &pkgId, bool isHybrid)
-{
- (void) pkgId;
- (void) isHybrid;
- return "User::App::" + appId;
-}
-
-std::string generatePathRWLabel(const std::string &pkgId)
-{
- return "User::Pkg::" + pkgId;
-}
-
-static std::string genPath(int app_num, const char *postfix) {
- char buf[16];
- sprintf(buf, "%02d", app_num);
- return TzPlatformConfig::globalAppDir() + "/sm_test_" + std::string(buf) + "_pkg_id_full/" + std::string(postfix);
-}
-
-std::string genRWPath(int app_num) {
- return genPath(app_num, "app_dir");
-}
-std::string genROPath(int app_num) {
- return genPath(app_num, "app_dir_ro");
-}
-std::string genPublicROPath(int app_num) {
- return genPath(app_num, "app_dir_public_ro");
-}
-
-std::string genOwnerRWOthersROPath(int app_num) {
- return genPath(app_num, "app_dir_rw_others_ro");
-}
-
-static std::string generatePkgLabelOwnerRWothersRO(const std::string &pkgId)
-{
- return "User::Pkg::" + pkgId + "::SharedRO";
-}
-
// Common DB/nftw checks
// nftw doesn't allow passing user data to functions. Work around by using global variable
return 0;
}
-static int nftw_set_labels_non_app_dir(const char *fpath, const struct stat* /*sb*/,
- int /*typeflag*/, struct FTW* /*ftwbuf*/)
-{
- smack_lsetlabel(fpath, "canary_label", SMACK_LABEL_ACCESS);
- smack_lsetlabel(fpath, "canary_label", SMACK_LABEL_EXEC);
- smack_lsetlabel(fpath, nullptr, SMACK_LABEL_TRANSMUTE);
-
- return 0;
-}
-
int nftw_remove_labels(const char *fpath, const struct stat* /*sb*/,
int /*typeflag*/, struct FTW* /*ftwbuf*/)
{
return 0;
}
-static const std::string SM_DENIED_PATH = TzPlatformConfig::globalAppDir() + "/non_app_dir";
-
-void check_app_permissions(const char *const app_id, const char *const pkg_id,
- const char *const user, const privileges_t &allowed_privs,
- const privileges_t &denied_privs)
+void check_app_permissions(const std::string &app_id, const std::string &pkg_id,
+ const std::string &user, const privileges_t &allowed_privs,
+ const privileges_t &denied_privs, bool isHybrid)
{
(void) pkg_id;
- std::string smackLabel = generateProcessLabel(app_id, pkg_id);
+ std::string smackLabel = generateProcessLabel(app_id, pkg_id, isHybrid);
CynaraTestClient::Client ctc;
}
}
+void sm_app_has_privileges(const AppInstallHelper &app,
+ const std::vector<std::string> &privileges,
+ int expectedResult)
+{
+ for (auto const &privilege : privileges) {
+ int result;
+ Api::appHasPrivilege(app.getAppId(), privilege, app.getUID(), result);
+ RUNNER_ASSERT_MSG(result == expectedResult, "Application " << app.getAppId()
+ << " has unexpected access to " << privilege << ", is : "
+ << " should be : " << expectedResult );
+ }
+}
-void check_app_after_install(const char *const app_id, const char *const pkg_id)
+static void check_app(const std::string &appId, const std::string &pkgId, bool shouldBeInstalled)
{
- TestSecurityManagerDatabase dbtest;
- dbtest.test_db_after__app_install(app_id, pkg_id);
+ char *retPkgId;
+ int ret = security_manager_get_app_pkgid(&retPkgId, appId.c_str());
+
+ if (shouldBeInstalled) {
+ RUNNER_ASSERT_MSG(ret == SECURITY_MANAGER_SUCCESS, "The given appId is not installed.");
+
+ if (ret == SECURITY_MANAGER_SUCCESS) {
+ CStringPtr retPkgIdPtr(retPkgId);
+ RUNNER_ASSERT_MSG(strcmp(pkgId.c_str(), retPkgId) == 0,
+ "The given appId does not belong to the given pkgId.");
+ }
+ } else {
+ RUNNER_ASSERT_MSG(ret == SECURITY_MANAGER_ERROR_NO_SUCH_OBJECT, "The given appId is installed.");
+ }
}
-static void check_app_gids(const char *const app_id, const std::vector<gid_t> &allowed_gids)
+void check_app_after_install(const std::string &app_id, const std::string &pkg_id)
+{
+ check_app(app_id, pkg_id, true);
+}
+
+static void check_app_gids(const std::string &app_id, const std::vector<gid_t> &allowed_gids)
{
int ret;
gid_t main_gid = getgid();
static const char *const ANY_USER_REPRESENTATION = "anyuser";/*this may be actually any string*/
-void check_app_after_install(const char *const app_id, const char *const pkg_id,
+void check_app_after_install(const std::string &app_id, const std::string &pkg_id,
const privileges_t &allowed_privs,
const privileges_t &denied_privs,
- const std::vector<std::string> &allowed_groups)
+ bool isHybrid)
{
- TestSecurityManagerDatabase dbtest;
- dbtest.test_db_after__app_install(app_id, pkg_id);
+ check_app(app_id, pkg_id, true);
- /*Privileges should be granted to all users if root installs app*/
- check_app_permissions(app_id, pkg_id, ANY_USER_REPRESENTATION, allowed_privs, denied_privs);
+ /* Privileges should be granted to all users if root installs app */
+ check_app_permissions(app_id, pkg_id, ANY_USER_REPRESENTATION, allowed_privs, denied_privs, isHybrid);
- /* Setup mapping of gids to privileges */
- /* Do this for each privilege for extra check */
- for (const auto &privilege : allowed_privs) {
- dbtest.setup_privilege_groups(privilege, allowed_groups);
- }
+ PolicyConfiguration policy;
+ const PolicyConfiguration::GroupVector allowed_groups = policy.privToGroup(allowed_privs);
+ RUNNER_ASSERT_MSG(allowed_groups.size() == allowed_privs.size(),
+ "Some privileges given were not found in the policy");
std::vector<gid_t> allowed_gids;
-
for (const auto &groupName : allowed_groups) {
errno = 0;
struct group* grp = getgrnam(groupName.c_str());
check_app_gids(app_id, allowed_gids);
}
-void check_path(const std::string &path, const std::string &label) {
+void check_path(const std::string &path, const std::string &label, bool transmute, bool execute) {
nftw_expected_label = label;
- nftw_expected_transmute = true;
- nftw_expected_exec = false;
+ nftw_expected_transmute = transmute;
+ nftw_expected_exec = execute;
// check labels
int result = nftw(path.c_str(), &nftw_check_sm_labels, FTW_MAX_FDS, FTW_PHYS);
RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << path);
}
-void check_app_path_after_install(int app_num, const char *pkgId, bool others_enabled)
+void check_app_after_uninstall(const std::string &app_id, const std::string &pkg_id)
{
- std::string SM_RW_PATH = genRWPath(app_num);
- std::string SM_RO_PATH = genROPath(app_num);
- std::string SM_PUBLIC_RO_PATH = genPublicROPath(app_num);
- int result;
-
- nftw_expected_label = generatePathRWLabel(pkgId);
- nftw_expected_transmute = true;
- nftw_expected_exec = false;
-
- result = nftw(SM_RW_PATH.c_str(), &nftw_check_sm_labels, FTW_MAX_FDS, FTW_PHYS);
- RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << SM_RW_PATH);
-
- nftw_expected_label = generatePathRWLabel(pkgId) + "::RO";
- nftw_expected_transmute = false;
- nftw_expected_exec = false;
-
- result = nftw(SM_RO_PATH.c_str(), &nftw_check_sm_labels, FTW_MAX_FDS, FTW_PHYS);
- RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << SM_RO_PATH);
-
- nftw_expected_label = "User::Home";
- nftw_expected_transmute = true;
- nftw_expected_exec = false;
-
- result = nftw(SM_PUBLIC_RO_PATH.c_str(), &nftw_check_sm_labels, FTW_MAX_FDS, FTW_PHYS);
- RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << SM_PUBLIC_RO_PATH);
-
- result = nftw(SM_DENIED_PATH.c_str(), &nftw_check_labels_non_app_dir, FTW_MAX_FDS, FTW_PHYS);
- RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << SM_DENIED_PATH);
-
- // owner RW, others RO
- if(others_enabled) {
- std::string SM_OWNER_RW_OTHERS_RO_PATH = genOwnerRWOthersROPath(app_num);
- nftw_expected_label = generatePkgLabelOwnerRWothersRO(pkgId);
- nftw_expected_transmute = true;
- nftw_expected_exec = false;
-
- result = nftw(SM_OWNER_RW_OTHERS_RO_PATH.c_str(), &nftw_check_sm_labels, FTW_MAX_FDS, FTW_PHYS);
- RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << SM_OWNER_RW_OTHERS_RO_PATH);
- }
+ check_app(app_id, pkg_id, false);
}
-void check_app_after_uninstall(const char *const app_id, const char *const pkg_id,
- const bool is_pkg_removed)
+void check_app_after_uninstall(const std::string &app_id, const std::string &pkg_id,
+ const privileges_t &privileges, bool isHybrid)
{
- TestSecurityManagerDatabase dbtest;
- dbtest.test_db_after__app_uninstall(app_id, pkg_id, is_pkg_removed);
-}
-
-void check_app_after_uninstall(const char *const app_id, const char *const pkg_id,
- const privileges_t &privileges, const bool is_pkg_removed)
-{
- TestSecurityManagerDatabase dbtest;
- dbtest.test_db_after__app_uninstall(app_id, pkg_id, is_pkg_removed);
+ check_app(app_id, pkg_id, false);
-
- /*Privileges should not be granted anymore to any user*/
- check_app_permissions(app_id, pkg_id, ANY_USER_REPRESENTATION, SM_NO_PRIVILEGES, privileges);
+ /* Privileges should not be granted anymore to any user */
+ check_app_permissions(app_id, pkg_id, ANY_USER_REPRESENTATION, {}, privileges, isHybrid);
}
std::string access_opposite(std::string &access) {
static const std::map<char, int> access_mapping = {{'r', 0}, {'w', 1}, {'x', 2}, {'a', 3},
{'t', 4}, {'l', 5}};
- //May write implies may lock
+ // May write implies may lock
if (access.find('w') != std::string::npos && access.find('l') == std::string::npos) {
access.append("l");
}
return caps;
}
-static void prepare_app_path(int app_num, bool others_enabled = false)
-{
- std::string SM_RW_PATH = genRWPath(app_num);
- std::string SM_RO_PATH = genROPath(app_num);
- std::string SM_PUBLIC_RO_PATH = genPublicROPath(app_num);
- int result;
-
- result = nftw(SM_RW_PATH.c_str(), &nftw_remove_labels, FTW_MAX_FDS, FTW_PHYS);
- RUNNER_ASSERT_MSG(result == 0, "Unable to clean Smack labels in " << SM_RW_PATH);
-
- result = nftw(SM_RO_PATH.c_str(), &nftw_remove_labels, FTW_MAX_FDS, FTW_PHYS);
- RUNNER_ASSERT_MSG(result == 0, "Unable to clean Smack labels in " << SM_RO_PATH);
-
- result = nftw(SM_PUBLIC_RO_PATH.c_str(), &nftw_remove_labels, FTW_MAX_FDS, FTW_PHYS);
- RUNNER_ASSERT_MSG(result == 0, "Unable to clean Smack labels in " << SM_PUBLIC_RO_PATH);
-
- if(others_enabled) {
- std::string SM_OWNER_RW_OTHERS_RO_PATH = genOwnerRWOthersROPath(app_num);
- result = nftw(SM_OWNER_RW_OTHERS_RO_PATH.c_str(), &nftw_remove_labels, FTW_MAX_FDS, FTW_PHYS);
- RUNNER_ASSERT_MSG(result == 0, "Unable to clean Smack labels in " << SM_OWNER_RW_OTHERS_RO_PATH);
- }
-
- result = nftw(SM_DENIED_PATH.c_str(), &nftw_set_labels_non_app_dir, FTW_MAX_FDS, FTW_PHYS);
- RUNNER_ASSERT_MSG(result == 0, "Unable to set Smack labels in " << SM_DENIED_PATH);
+static int getOppositeAccessType(int accessType) {
+ return accessType ^ (R_OK | W_OK | X_OK);
}
-void prepare_app_env(int app_num, bool others_enabled)
-{
- prepare_app_path(app_num, others_enabled);
-}
+static const std::unordered_map<int, const char* const> accessTypeToString {
+ std::make_pair(0, "F_OK"),
+ std::make_pair(1, "X_OK"),
+ std::make_pair(2, "W_OK"),
+ std::make_pair(3, "W_OK|X_OK"),
+ std::make_pair(4, "R_OK"),
+ std::make_pair(5, "R_OK|X_OK"),
+ std::make_pair(6, "R_OK|W_OK"),
+ std::make_pair(7, "R_OK|W_OK|X_OK")
+};
-void install_app(const char *app_id, const char *pkg_id, uid_t uid, app_install_type type,
- bool check_after)
+void accessCheck(const std::string &id, const std::string &path, int accessType,
+ int expected)
{
- InstallRequest request;
- request.setAppId(app_id);
- request.setPkgId(pkg_id);
- request.setUid(uid);
- if (type != SM_APP_INSTALL_NONE)
- request.setInstallType(type);
- Api::install(request);
-
- if (check_after)
- check_app_after_install(app_id, pkg_id);
+ RUNNER_ASSERT_MSG(::access(path.c_str(), accessType) == expected,
+ "access from " << id << " to path " << path
+ << (expected == 0 ? " not granted" : " unnecessarily granted")
+ << " (" << accessTypeToString.at(accessType) << ")");
}
-void uninstall_app(const char *app_id, const char *pkg_id, bool expect_pkg_removed,
- app_install_type type, bool check_after)
-{
- InstallRequest request;
- request.setAppId(app_id);
- if (type != SM_APP_INSTALL_NONE)
- request.setInstallType(type);
- Api::uninstall(request);
-
- if (check_after)
- check_app_after_uninstall(app_id, pkg_id, expect_pkg_removed);
-}
+void runAccessTest(const std::string &label, uid_t uid, gid_t gid,
+ const std::string &testPath, int accessType) {
+ auto fun = [&](){
+ int oppositeAccessType = getOppositeAccessType(accessType);
+ ScopedProcessLabel spl(label, false);
+ RUNNER_ASSERT_ERRNO_MSG(0 == drop_root_privileges(uid, gid),
+ "drop_root_privileges failed.");
-static const std::string EXEC_FILE("exec");
-static const std::string NORMAL_FILE("normal");
-static const std::string LINK_PREFIX("link_to_");
+ if (accessType != 0)
+ accessCheck(label, testPath, accessType, 0);
+ if (oppositeAccessType != 0) {
+ std::vector<int> singleAccessTypes = {R_OK, W_OK, X_OK};
+ for (auto singleAccessType : singleAccessTypes)
+ if (oppositeAccessType & singleAccessType)
+ accessCheck(label, testPath, singleAccessType, -1);
+ }
+ };
-static void createTestDir(const std::string &dir)
-{
- mode_t dirMode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
- mode_t execFileMode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
- mode_t normalFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
-
- mktreeSafe(dir, dirMode);
- creatSafe(dir + "/" + EXEC_FILE, execFileMode);
- creatSafe(dir + "/" + NORMAL_FILE, normalFileMode);
- symlinkSafe(dir + "/" + EXEC_FILE, dir + "/" + LINK_PREFIX + EXEC_FILE);
- symlinkSafe(dir + "/" + NORMAL_FILE, dir + "/" + LINK_PREFIX + NORMAL_FILE);
+ runInChildParentWait(fun);
}
-static void createInnerAppDir(const std::string &dir, const std::string &nonAppDir)
-{
- createTestDir(dir);
+void runAccessTest(const AppInstallHelper &app, const std::string &testPath, int accessType) {
+ auto fun = [&](){
+ int oppositeAccessType = getOppositeAccessType(accessType);
+ Api::setProcessLabel(app.getAppId());
+ RUNNER_ASSERT_ERRNO_MSG(0 == drop_root_privileges(app.getUID(), app.getGID()),
+ "drop_root_privileges failed.");
+ if (accessType != 0)
+ accessCheck(app.getAppId(), testPath, accessType, 0);
+ if (oppositeAccessType != 0) {
+ std::vector<int> singleAccessTypes = {R_OK, W_OK, X_OK};
+ for (auto singleAccessType : singleAccessTypes)
+ if (oppositeAccessType & singleAccessType)
+ accessCheck(app.getAppId(), testPath, singleAccessType, -1);
+ }
+ };
- symlinkSafe(nonAppDir, dir + "/" + LINK_PREFIX + "non_app_dir");
- symlinkSafe(nonAppDir + "/" + EXEC_FILE,
- dir + "/" + LINK_PREFIX + "non_app_" + EXEC_FILE);
- symlinkSafe(nonAppDir + "/" + NORMAL_FILE,
- dir + "/" + LINK_PREFIX + "non_app_" + NORMAL_FILE);
+ runInChildParentWait(fun);
}
-static const std::string nonAppDirPath(const TemporaryTestUser &user)
-{
- return TMP_DIR + "/" + user.getUserName();
+static const std::vector<std::string> SM_SYSTEM_LABELS = {"System", "System::Privileged", "User"};
+
+void runSystemAccessTest(uid_t uid, gid_t gid, const std::string &testPath, int accessType) {
+ for (const auto &label : SM_SYSTEM_LABELS)
+ runAccessTest(label, uid, gid, testPath, accessType);
}
-static void generateAppDir(const TemporaryTestUser &user,
- const std::string &appId, const std::string &pkgId)
-{
- const std::string dir = TzPlatformConfig::appDirPath(user, appId, pkgId);
- const std::string nonAppDir = nonAppDirPath(user);
+bool isAskuserDisabled() {
+ static bool isAskuserDisabled = false;
+ static bool isChecked = false;
- createInnerAppDir(dir, nonAppDir);
- createInnerAppDir(dir + "/.inner_dir", nonAppDir);
- createInnerAppDir(dir + "/inner_dir", nonAppDir);
-}
+ if (isChecked)
+ return isAskuserDisabled;
-static void generateNonAppDir(const TemporaryTestUser &user)
-{
- const std::string dir = nonAppDirPath(user);
+ std::string sysShare = TzPlatformConfig::getPath(TZ_SYS_SHARE);
+ std::string askDisableFile = sysShare + "/askuser_disable";
- createTestDir(dir);
- createTestDir(dir + "/.inner_dir");
- createTestDir(dir + "/inner_dir");
+ isAskuserDisabled = (access(askDisableFile.c_str(), F_OK) != -1);
+ isChecked = true;
+ return isAskuserDisabled;
}
-void createTestDirs(const TemporaryTestUser &user,
- const std::string &appId, const std::string &pkgId)
-{
- generateAppDir(user, appId, pkgId);
- generateNonAppDir(user);
+bool isPrivilegePrivacy(const std::string &priv) {
+ return (1 == privilege_info_is_privacy(priv.c_str()));
}
-void removeTestDirs(const TemporaryTestUser &user,
- const std::string &appId, const std::string &pkgId)
-{
- removeDir(TzPlatformConfig::appDirPath(user, appId, pkgId));
- removeDir(nonAppDirPath(user));
+int countPrivacyPrivileges(const PrivilegeVector &privs) {
+ return std::count_if(privs.begin(), privs.end(), isPrivilegePrivacy);
}
-pid_t runInChild(const std::function<void(void)> &process) {
- pid_t pid = fork();
- RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "fork failed");
-
- if (pid == 0) {
- process();
- exit(EXIT_SUCCESS);
- }
- return pid;
+int countPrivacyPrivileges(const std::vector<std::string> &privs) {
+ return std::count_if(privs.begin(), privs.end(), isPrivilegePrivacy);
}
-void runInChildParentWait(const std::function<void(void)> &process) {
- SynchronizationPipe pipe;
- pid_t pid = fork();
- RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "fork failed");
-
- if (pid == 0) {
- pipe.claimChildEp();
-
- process();
-
- pipe.post();
- exit(EXIT_SUCCESS);
- } else {
- pipe.claimParentEp();
- pipe.wait();
- }
-}