Extend AppInstallHelper with checker methods
[platform/core/test/security-tests.git] / src / security-manager-tests / common / sm_commons.cpp
index bd583a2..ce822cd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2016 - 2020 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.
  *    limitations under the License.
  */
 
-#include <algorithm>
 #include <cstring>
 #include <fcntl.h>
 #include <ftw.h>
-#include <grp.h>
 #include <string>
 #include <sys/capability.h>
+#include <sys/prctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <unistd.h>
-#include <unordered_map>
 #include <cstdlib>
 
-#include <unordered_set>
+#include <utility>
 #include <vector>
+#include <unordered_map>
 
 #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 <synchronization_pipe.h>
-#include <sm_request.h>
+
 #include <tests_common.h>
-#include <policy_configuration.h>
 #include "tzplatform.h"
+#include <template_parser.h>
+#include <temp_test_user.h>
 
 using namespace SecurityManagerTest;
 
-// Common implementation details
-
-std::string generateProcessLabel(const std::string &appId, const std::string &pkgId, bool isHybrid)
-{
-    std::string label = "User::Pkg::" + pkgId;
-    if (isHybrid) {
-        label += "::App::" + appId;
-    }
-    return label;
-}
-
-std::string generatePathRWLabel(const std::string &pkgId)
-{
-    return "User::Pkg::" + pkgId;
-}
-
-std::string generatePathROLabel(const std::string &pkgId)
-{
-    return generatePathRWLabel(pkgId) + "::RO";
-}
-
-std::string generatePathSharedROLabel(const std::string &pkgId)
-{
-    return generatePathRWLabel(pkgId) + "::SharedRO";
-}
-
-std::string generatePathTrustedLabel(int64_t authorId)
-{
-    return "User::Author::" + std::to_string(authorId);
-}
-
-std::string getPublicPathLabel()
-{
-    return "User::Home";
-}
+#define ALLOW 0
+#define DENY -1
 
 // Common DB/nftw checks
 
@@ -184,116 +148,6 @@ int nftw_remove_labels(const char *fpath, const struct stat* /*sb*/,
     return 0;
 }
 
-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, isHybrid);
-
-    CynaraTestClient::Client ctc;
-
-    for (auto &priv : allowed_privs) {
-        ctc.check(smackLabel.c_str(), "", user, priv.c_str(), CYNARA_API_ACCESS_ALLOWED);
-    }
-
-    for (auto &priv : denied_privs) {
-        ctc.check(smackLabel.c_str(), "", user, priv.c_str(), CYNARA_API_ACCESS_DENIED);
-    }
-}
-
-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 );
-    }
-}
-
-static void check_app(const std::string &appId, const std::string &pkgId, bool shouldBeInstalled)
-{
-    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.");
-    }
-}
-
-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();
-    std::unordered_set<gid_t> reference_gids(allowed_gids.begin(), allowed_gids.end());
-
-    // Reset supplementary groups
-    ret = setgroups(0, NULL);
-    RUNNER_ASSERT_MSG(ret != -1, "Unable to set supplementary groups");
-
-    Api::setProcessGroups(app_id);
-
-    ret = getgroups(0, nullptr);
-    RUNNER_ASSERT_MSG(ret != -1, "Unable to get supplementary groups");
-
-    std::vector<gid_t> actual_gids(ret);
-    ret = getgroups(ret, actual_gids.data());
-    RUNNER_ASSERT_MSG(ret != -1, "Unable to get supplementary groups");
-
-    for (const auto &gid : actual_gids) {
-        RUNNER_ASSERT_MSG(gid == main_gid || reference_gids.count(gid) > 0,
-            "Application shouldn't get access to group " << gid);
-        reference_gids.erase(gid);
-    }
-
-    RUNNER_ASSERT_MSG(reference_gids.empty(), "Application didn't get access to some groups");
-}
-
-static const char *const ANY_USER_REPRESENTATION = "anyuser";/*this may be actually any string*/
-
-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,
-                             bool isHybrid)
-{
-    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, isHybrid);
-
-    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());
-        RUNNER_ASSERT_ERRNO_MSG(grp, "Group: " << groupName << " not found");
-        allowed_gids.push_back(grp->gr_gid);
-    }
-
-    check_app_gids(app_id, allowed_gids);
-}
-
 void check_path(const std::string &path, const std::string &label, bool transmute, bool execute) {
     nftw_expected_label = label;
     nftw_expected_transmute = transmute;
@@ -304,57 +158,6 @@ void check_path(const std::string &path, const std::string &label, bool transmut
     RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << path);
 }
 
-void check_app_after_uninstall(const std::string &app_id, const std::string &pkg_id)
-{
-    check_app(app_id, pkg_id, false);
-}
-
-void check_app_after_uninstall(const std::string &app_id, const std::string &pkg_id,
-                               const privileges_t &privileges, bool isHybrid)
-{
-    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, {}, 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
-    if (access.find('w') != std::string::npos && access.find('l') == std::string::npos) {
-        access.append("l");
-    }
-    std::string access_opposite = "rwxatl";
-    for (char c : access) {
-        access_opposite[access_mapping.at(c)] = '-';
-    }
-    auto it = std::remove_if(access_opposite.begin(), access_opposite.end(), [](char c) {return c == '-';});
-    access_opposite.erase(it, access_opposite.end());
-    return access_opposite;
-}
-
-void check_exact_smack_accesses(const std::string &subject, const std::string &object,
-                                const std::string &access) {
-    std::string access_str(access);
-    auto no_access = access_opposite(access_str);
-    for (char c : access_str) {
-        int ret = smack_have_access(subject.c_str(), object.c_str(), std::string(1, c).c_str());
-        RUNNER_ASSERT_MSG(ret >= 0, "smack_have_access failed: <" << subject << ">, <" << object
-                          << ">, <" << c << "> errno=" << strerror(errno));
-        RUNNER_ASSERT_MSG(ret == 1, "Access " << c << " from " << subject << " to "
-                          << object << " not given");
-    }
-
-    for (char c : no_access) {
-        int ret = smack_have_access(subject.c_str(), object.c_str(), std::string(1, c).c_str());
-        RUNNER_ASSERT_MSG(ret >= 0, "smack_have_access failed: <" << subject << ">, <" << object
-                          << ">, <" << c << "> errno=" << strerror(errno));
-        RUNNER_ASSERT_MSG(ret == 0, "Access " << c << " from " << subject << " to "
-                          << object << " unnecessarily given");
-    }
-}
-
 CapsSetsUniquePtr setCaps(const char *cap_string)
 {
     CapsSetsUniquePtr caps(cap_init());
@@ -365,28 +168,6 @@ CapsSetsUniquePtr setCaps(const char *cap_string)
     return caps;
 }
 
-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;
-}
-
-void runInChildParentWait(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);
-    } else {
-        waitPid(pid);
-    }
-}
-
 static int getOppositeAccessType(int accessType) {
     return accessType ^ (R_OK | W_OK | X_OK);
 }
@@ -411,22 +192,30 @@ void accessCheck(const std::string &id, const std::string &path, int accessType,
                        << " (" << accessTypeToString.at(accessType) << ")");
 }
 
+void accessTest(const std::string &id, const std::string &testPath, int accessType) {
+    int oppositeAccessType = getOppositeAccessType(accessType);
+
+    if (accessType != 0) {
+        accessCheck(id, testPath, accessType, ALLOW);
+    }
+    if (oppositeAccessType != 0) {
+        static const std::vector<int> singleAccessTypes = {R_OK, W_OK, X_OK};
+        for (auto singleAccessType : singleAccessTypes) {
+            if (oppositeAccessType & singleAccessType) {
+                accessCheck(id, testPath, singleAccessType, DENY);
+            }
+        }
+    }
+}
+
 void runAccessTest(const std::string &label, uid_t uid, gid_t gid,
                    const std::string &testPath, int accessType) {
     auto fun = [&](){
-        int oppositeAccessType = getOppositeAccessType(accessType);
-        change_label(label.c_str());
+        ScopedProcessLabel spl(label, false);
         RUNNER_ASSERT_ERRNO_MSG(0 == drop_root_privileges(uid, gid),
                                 "drop_root_privileges failed.");
 
-        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);
-        }
+        accessTest(label, testPath, accessType);
     };
 
     runInChildParentWait(fun);
@@ -434,18 +223,10 @@ void runAccessTest(const std::string &label, uid_t uid, gid_t gid,
 
 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);
-        }
+        accessTest(app.getAppId(), testPath, accessType);
     };
 
     runInChildParentWait(fun);
@@ -485,3 +266,29 @@ int countPrivacyPrivileges(const std::vector<std::string> &privs) {
     return std::count_if(privs.begin(), privs.end(), isPrivilegePrivacy);
 }
 
+int setLauncherSecurityAttributes(uid_t uid, gid_t gid)
+{
+    // Add launcher capabilities (cap_dac_override, cap_setgid, cap_sys_admin, cap_mac_admin),
+    // launcher is user process, we must drop root privileges (cap_setgid, cap_setuid are needed).
+    // By default, the permitted capability set is cleared when credentials change is made
+    // (if a process drops a capability from its permitted set, it can never reacquire that capability),
+    // setting the "keep capabilities" flag prevents it from being cleared.
+    // Effective capability set is always cleared when credential change is made, we need to add them again.
+
+    setCaps("cap_dac_override+ep cap_setgid+ep cap_sys_admin+ep cap_mac_admin+ep cap_setuid+ep");
+    int ret = prctl(PR_SET_KEEPCAPS, 1, 0, 0);
+    if (ret != 0)
+        return ret;
+
+    ret = drop_root_privileges(uid, gid);
+    if (ret != 0)
+        return ret;
+
+    setCaps("cap_dac_override+ep cap_setgid+ep cap_sys_admin+ep cap_mac_admin+ep");
+    return ret;
+}
+int setLauncherSecurityAttributes(TemporaryTestUser &user)
+{
+    return setLauncherSecurityAttributes(user.getUid(), user.getGid());
+}
+