2 * Copyright (c) 2020 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.
17 #include "app_install_helper_ext.h"
21 #include <sys/smack.h>
25 #include <unordered_set>
28 #include <security-manager-types.h>
31 #include <label_generator.h>
32 #include <cynara_test_client.h>
33 #include <policy_configuration.h>
36 constexpr char SMACK_RULES_PATH[] = "/sys/fs/smackfs/load2";
37 constexpr char ANY_USER_REPRESENTATION[] = "anyuser";/*this may be actually any string*/
39 void assertNoLabelInRule(const AccessRequest &rule, const std::string &label)
41 RUNNER_ASSERT_MSG(rule.object != label && rule.subject != label,
42 "Smack rule left after uninstallation process." <<
43 " Subject: " << rule.subject <<
44 " object: " << rule.object <<
45 " access: " << rule.access);
48 std::string accessOpposite(std::string &access)
50 static const std::map<char, int> accessMapping = {{'r', 0}, {'w', 1}, {'x', 2}, {'a', 3},
52 // May write implies may lock
53 if (access.find('w') != std::string::npos && access.find('l') == std::string::npos) {
56 std::string oppositeAccess = "rwxatl";
57 for (char c : access) {
58 oppositeAccess[accessMapping.at(c)] = '-';
60 auto it = std::remove_if(oppositeAccess.begin(), oppositeAccess.end(), [](char c) {return c == '-';});
61 oppositeAccess.erase(it, oppositeAccess.end());
62 return oppositeAccess;
65 void checkExactSmackAccesses(const std::string &subject, const std::string &object,
66 const std::string &access)
68 std::string access_str(access);
69 auto no_access = accessOpposite(access_str);
70 for (char c : access_str) {
71 int ret = smack_have_access(subject.c_str(), object.c_str(), std::string(1, c).c_str());
72 RUNNER_ASSERT_MSG(ret >= 0, "smack_have_access failed: <" << subject << ">, <" << object
73 << ">, <" << c << "> errno=" << strerror(errno));
74 RUNNER_ASSERT_MSG(ret == 1, "Access " << c << " from " << subject << " to "
75 << object << " not given");
78 for (char c : no_access) {
79 int ret = smack_have_access(subject.c_str(), object.c_str(), std::string(1, c).c_str());
80 RUNNER_ASSERT_MSG(ret >= 0, "smack_have_access failed: <" << subject << ">, <" << object
81 << ">, <" << c << "> errno=" << strerror(errno));
82 RUNNER_ASSERT_MSG(ret == 0, "Access " << c << " from " << subject << " to "
83 << object << " unnecessarily given");
87 } // namespace anonymous
89 namespace SecurityManagerTest
92 void AppInstallHelperExt::checkPrivileges(const PrivilegeVector &allowedPrivs,
93 const PrivilegeVector &deniedPrivs) const
95 /* Privileges should be granted to all users if root installs app */
96 auto user = (m_uidGid == 0 ? ANY_USER_REPRESENTATION : std::to_string(m_uidGid));
98 std::string smackLabel = generateProcessLabel(m_appName, m_pkgName, m_isHybrid);
100 CynaraTestClient::Client ctc;
103 for (auto &priv : allowedPrivs) {
104 ctc.check(smackLabel, "", user, priv.getName(), CYNARA_API_ACCESS_ALLOWED);
106 Api::appHasPrivilege(m_appName, priv, m_uidGid, result);
107 RUNNER_ASSERT_MSG(result == 1,
108 "Application " << m_appName << " has no access to " << priv);
111 for (auto &priv : deniedPrivs) {
112 ctc.check(smackLabel, "", user, priv.getName(), CYNARA_API_ACCESS_DENIED);
114 Api::appHasPrivilege(m_appName, priv, m_uidGid, result);
115 RUNNER_ASSERT_MSG(result == 0,
116 "Application " << m_appName << " has unexpected access to " << priv);
120 void AppInstallHelperExt::checkDeniedPrivileges(const PrivilegeVector &deniedPrivs) const
122 checkPrivileges({}, deniedPrivs);
125 void AppInstallHelperExt::checkGroupPrivileges(const PrivilegeVector &expectedPrivs) const
127 static PolicyConfiguration policy;
129 // get expected groups
130 auto expectedGids = policy.groupToGid(policy.privToGroup(expectedPrivs));
131 RUNNER_ASSERT_MSG(expectedGids.size() == expectedPrivs.size(),
132 "Some privileges given were not found in the policy");
133 std::sort(expectedGids.begin(), expectedGids.end());
135 // get current process groups
136 int ret = getgroups(0, nullptr);
137 RUNNER_ASSERT_MSG(ret != -1, "Unable to get supplementary groups");
139 std::vector<gid_t> actualGids(ret);
140 ret = getgroups(ret, actualGids.data());
141 RUNNER_ASSERT_MSG(ret != -1, "Unable to get supplementary groups");
143 // remove groups unrelated to privileges
144 const auto allPrivGids = policy.getGid();
145 auto notPrivGid = [&](gid_t gid){
146 return std::find(allPrivGids.begin(), allPrivGids.end(), gid) == allPrivGids.end();
148 actualGids.erase(std::remove_if(actualGids.begin(), actualGids.end(), notPrivGid),
150 std::sort(actualGids.begin(), actualGids.end());
152 // expected but not allowed
153 std::vector<gid_t> notAllowedGids;
154 std::set_difference(expectedGids.begin(), expectedGids.end(),
155 actualGids.begin(), actualGids.end(),
156 std::back_inserter(notAllowedGids));
158 RUNNER_ASSERT_MSG(notAllowedGids.empty(),
159 notAllowedGids.size() << " expected groups were not assigned");
161 // allowed but not expected
162 std::vector<gid_t> notDeniedGids;
163 std::set_difference(actualGids.begin(), actualGids.end(),
164 expectedGids.begin(), expectedGids.end(),
165 std::back_inserter(notDeniedGids));
167 RUNNER_ASSERT_MSG(notDeniedGids.empty(),
168 notDeniedGids.size() << " unexpected groups were assigned");
171 void AppInstallHelperExt:: checkSmackPrivileges(const PrivilegeVector &allowedPrivs,
172 const PrivilegeVector &deniedPrivs) const
174 auto& smackPrivilegeRules = PolicyConfiguration::getSmackPrivRulesMap();
176 auto getPrivilegeRules = [&](const PrivilegeVector &privs) {
177 std::vector<AccessRequest> rules;
179 for (auto &priv : privs) {
180 auto it = smackPrivilegeRules.find(priv);
181 RUNNER_ASSERT_MSG(it != smackPrivilegeRules.end(), priv << " is not a smack privilege");
183 rules.insert(rules.end(), it->second.begin(), it->second.end());
188 checkSmackAccesses(getPrivilegeRules(allowedPrivs));
189 checkSmackAccesses(getPrivilegeRules(deniedPrivs), false);
192 void AppInstallHelperExt::checkAfterInstall() const
194 static const std::vector<AccessRequest> staticRules[] =
195 {parseSmackRulesFile(PolicyConfiguration::getPkgRulesFilePath()),
196 parseSmackRulesFile(PolicyConfiguration::getAppRulesFilePath())};
198 checkAppIdExistence(true);
200 checkSmackAccesses(staticRules[m_isHybrid]);
202 checkPrivileges(m_privileges, {});
205 void AppInstallHelperExt::checkAfterUninstall(bool removePkg) const
207 checkAppIdExistence(false);
210 checkPkgSmackRulesAfterUninstall();
212 // there should be no hybrid rules regardless of the app type
213 checkHybridAppSmackRulesAterUninstall();
215 checkDeniedPrivileges(m_privileges);
218 void AppInstallHelperExt::checkSmackAccesses(std::vector<AccessRequest> rules, bool hasAccess) const
220 const std::pair<std::string, std::string> switchAliases[] = {
221 {"~PATH_RW~", generatePathRWLabel(m_pkgName)},
222 {"~PATH_RO~", generatePathROLabel(m_pkgName)},
223 {"~PROCESS~", generateProcessLabel(m_appName, m_pkgName, m_isHybrid)}
226 for (auto& rule : rules) {
227 if (rule.object == "~PATH_TRUSTED~") {
231 for (const auto &alias : switchAliases) {
232 if (rule.subject == alias.first) {
233 rule.subject = alias.second;
235 if (rule.object == alias.first) {
236 rule.object = alias.second;
243 // Special label that may occur in the template. Other special labels will not be used.
244 if (rule.object == "_") {
245 rule.access = "rx" + rule.access;
248 checkExactSmackAccesses(rule.subject, rule.object, rule.access);
252 void AppInstallHelperExt::checkPkgSmackRulesAfterUninstall() const
254 const std::vector<AccessRequest> rules(std::move(parseSmackRulesFile(SMACK_RULES_PATH)));
255 const std::string labels[] = {generatePathRWLabel(m_pkgName),
256 generatePathROLabel(m_pkgName),
257 generateProcessLabel(m_appName, m_pkgName, true),
258 generateProcessLabel(m_appName, m_pkgName)};
260 for (const auto &rule : rules) {
261 for (const auto &label : labels) {
262 assertNoLabelInRule(rule, label);
267 void AppInstallHelperExt::checkHybridAppSmackRulesAterUninstall() const
269 const std::vector<AccessRequest> rules(parseSmackRulesFile(SMACK_RULES_PATH));
270 const std::string appLabel = generateProcessLabel(m_appName, m_pkgName, true);
272 for (const auto &rule : rules) {
273 assertNoLabelInRule(rule, appLabel);
277 void AppInstallHelperExt::checkAppIdExistence(bool expected) const
279 lib_retcode ret = expected ? SECURITY_MANAGER_SUCCESS : SECURITY_MANAGER_ERROR_NO_SUCH_OBJECT;
280 std::string retPkgId = Api::getPkgId(m_appName, ret);
283 RUNNER_ASSERT_MSG(m_pkgName == retPkgId,
284 "The given appId does not belong to the given pkgId.");
288 } // namespace SecurityManagerTest