Spring cleaning
[platform/core/test/security-tests.git] / src / security-manager-tests / common / app_install_helper_ext.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved
3  *
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
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include "app_install_helper_ext.h"
18
19 #include <grp.h>
20 #include <unistd.h>
21 #include <sys/smack.h>
22
23 #include <utility>
24 #include <string>
25 #include <unordered_set>
26 #include <algorithm>
27
28 #include <security-manager-types.h>
29
30 #include <sm_api.h>
31 #include <label_generator.h>
32 #include <cynara_test_client.h>
33 #include <policy_configuration.h>
34
35 namespace {
36 constexpr char SMACK_RULES_PATH[] = "/sys/fs/smackfs/load2";
37 constexpr char ANY_USER_REPRESENTATION[] = "anyuser";/*this may be actually any string*/
38
39 void assertNoLabelInRule(const AccessRequest &rule, const std::string &label)
40 {
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);
46 }
47
48 std::string accessOpposite(std::string &access)
49 {
50     static const std::map<char, int> accessMapping = {{'r', 0}, {'w', 1}, {'x', 2}, {'a', 3},
51                                                       {'t', 4}, {'l', 5}};
52     // May write implies may lock
53     if (access.find('w') != std::string::npos && access.find('l') == std::string::npos) {
54         access.append("l");
55     }
56     std::string oppositeAccess = "rwxatl";
57     for (char c : access) {
58         oppositeAccess[accessMapping.at(c)] = '-';
59     }
60     auto it = std::remove_if(oppositeAccess.begin(), oppositeAccess.end(), [](char c) {return c == '-';});
61     oppositeAccess.erase(it, oppositeAccess.end());
62     return oppositeAccess;
63 }
64
65 void checkExactSmackAccesses(const std::string &subject, const std::string &object,
66                              const std::string &access)
67 {
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");
76     }
77
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");
84     }
85 }
86
87 } // namespace anonymous
88
89 namespace SecurityManagerTest
90 {
91
92 void AppInstallHelperExt::checkPrivileges(const PrivilegeVector &allowedPrivs,
93                                           const PrivilegeVector &deniedPrivs) const
94 {
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));
97
98     std::string smackLabel = generateProcessLabel(m_appName, m_pkgName, m_isHybrid);
99
100     CynaraTestClient::Client ctc;
101     int result;
102
103     for (auto &priv : allowedPrivs) {
104         ctc.check(smackLabel, "", user, priv.getName(), CYNARA_API_ACCESS_ALLOWED);
105
106         Api::appHasPrivilege(m_appName, priv, m_uidGid, result);
107         RUNNER_ASSERT_MSG(result == 1,
108                           "Application " << m_appName << " has no access to " << priv);
109     }
110
111     for (auto &priv : deniedPrivs) {
112         ctc.check(smackLabel, "", user, priv.getName(), CYNARA_API_ACCESS_DENIED);
113
114         Api::appHasPrivilege(m_appName, priv, m_uidGid, result);
115         RUNNER_ASSERT_MSG(result == 0,
116                           "Application " << m_appName << " has unexpected access to " << priv);
117     }
118 }
119
120 void AppInstallHelperExt::checkDeniedPrivileges(const PrivilegeVector &deniedPrivs) const
121 {
122     checkPrivileges({}, deniedPrivs);
123 }
124
125 void AppInstallHelperExt::checkGroupPrivileges(const PrivilegeVector &expectedPrivs) const
126 {
127     static PolicyConfiguration policy;
128
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());
134
135     // get current process groups
136     int ret = getgroups(0, nullptr);
137     RUNNER_ASSERT_MSG(ret != -1, "Unable to get supplementary groups");
138
139     std::vector<gid_t> actualGids(ret);
140     ret = getgroups(ret, actualGids.data());
141     RUNNER_ASSERT_MSG(ret != -1, "Unable to get supplementary groups");
142
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();
147     };
148     actualGids.erase(std::remove_if(actualGids.begin(), actualGids.end(), notPrivGid),
149                      actualGids.end());
150     std::sort(actualGids.begin(), actualGids.end());
151
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));
157
158     RUNNER_ASSERT_MSG(notAllowedGids.empty(),
159                       notAllowedGids.size() << " expected groups were not assigned");
160
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));
166
167     RUNNER_ASSERT_MSG(notDeniedGids.empty(),
168                       notDeniedGids.size() << " unexpected groups were assigned");
169 }
170
171 void AppInstallHelperExt:: checkSmackPrivileges(const PrivilegeVector &allowedPrivs,
172                                                 const PrivilegeVector &deniedPrivs) const
173 {
174     auto& smackPrivilegeRules = PolicyConfiguration::getSmackPrivRulesMap();
175
176     auto getPrivilegeRules = [&](const PrivilegeVector &privs) {
177         std::vector<AccessRequest> rules;
178
179         for (auto &priv : privs) {
180             auto it = smackPrivilegeRules.find(priv);
181             RUNNER_ASSERT_MSG(it != smackPrivilegeRules.end(), priv << " is not a smack privilege");
182
183             rules.insert(rules.end(), it->second.begin(), it->second.end());
184         }
185         return rules;
186     };
187
188     checkSmackAccesses(getPrivilegeRules(allowedPrivs));
189     checkSmackAccesses(getPrivilegeRules(deniedPrivs), false);
190 }
191
192 void AppInstallHelperExt::checkAfterInstall() const
193 {
194     static const std::vector<AccessRequest> staticRules[] =
195         {parseSmackRulesFile(PolicyConfiguration::getPkgRulesFilePath()),
196          parseSmackRulesFile(PolicyConfiguration::getAppRulesFilePath())};
197
198     checkAppIdExistence(true);
199
200     checkSmackAccesses(staticRules[m_isHybrid]);
201
202     checkPrivileges(m_privileges, {});
203 }
204
205 void AppInstallHelperExt::checkAfterUninstall(bool removePkg) const
206 {
207     checkAppIdExistence(false);
208
209     if (removePkg) {
210         checkPkgSmackRulesAfterUninstall();
211     }
212     // there should be no hybrid rules regardless of the app type
213     checkHybridAppSmackRulesAterUninstall();
214
215     checkDeniedPrivileges(m_privileges);
216 }
217
218 void AppInstallHelperExt::checkSmackAccesses(std::vector<AccessRequest> rules, bool hasAccess) const
219 {
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)}
224     };
225
226     for (auto& rule : rules) {
227         if (rule.object == "~PATH_TRUSTED~") {
228             continue;
229         }
230
231         for (const auto &alias : switchAliases) {
232             if (rule.subject == alias.first) {
233                 rule.subject = alias.second;
234             }
235             if (rule.object == alias.first) {
236                 rule.object = alias.second;
237             }
238         }
239
240         if (!hasAccess)
241             rule.access = "";
242
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;
246         }
247
248         checkExactSmackAccesses(rule.subject, rule.object, rule.access);
249     }
250 }
251
252 void AppInstallHelperExt::checkPkgSmackRulesAfterUninstall() const
253 {
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)};
259
260     for (const auto &rule : rules) {
261         for (const auto &label : labels) {
262             assertNoLabelInRule(rule, label);
263         }
264     }
265 }
266
267 void AppInstallHelperExt::checkHybridAppSmackRulesAterUninstall() const
268 {
269     const std::vector<AccessRequest> rules(parseSmackRulesFile(SMACK_RULES_PATH));
270     const std::string appLabel = generateProcessLabel(m_appName, m_pkgName, true);
271
272     for (const auto &rule : rules) {
273         assertNoLabelInRule(rule, appLabel);
274     }
275 }
276
277 void AppInstallHelperExt::checkAppIdExistence(bool expected) const
278 {
279     lib_retcode ret = expected ? SECURITY_MANAGER_SUCCESS : SECURITY_MANAGER_ERROR_NO_SUCH_OBJECT;
280     std::string retPkgId = Api::getPkgId(m_appName, ret);
281
282     if (expected) {
283         RUNNER_ASSERT_MSG(m_pkgName == retPkgId,
284                           "The given appId does not belong to the given pkgId.");
285     }
286 }
287
288 } // namespace SecurityManagerTest