Extend AppInstallHelper with checker methods
[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
34 namespace {
35 constexpr char SMACK_RULES_PATH[] = "/sys/fs/smackfs/load2";
36 constexpr char ANY_USER_REPRESENTATION[] = "anyuser";/*this may be actually any string*/
37
38 void assertNoLabelInRule(const AccessRequest &rule, const std::string &label)
39 {
40     RUNNER_ASSERT_MSG(rule.object != label && rule.subject != label,
41                       "Smack rule left after uninstallation process." <<
42                       " Subject: " << rule.subject <<
43                       " object: " << rule.object <<
44                       " access: " << rule.access);
45 }
46
47 std::string accessOpposite(std::string &access)
48 {
49     static const std::map<char, int> accessMapping = {{'r', 0}, {'w', 1}, {'x', 2}, {'a', 3},
50                                                       {'t', 4}, {'l', 5}};
51     // May write implies may lock
52     if (access.find('w') != std::string::npos && access.find('l') == std::string::npos) {
53         access.append("l");
54     }
55     std::string oppositeAccess = "rwxatl";
56     for (char c : access) {
57         oppositeAccess[accessMapping.at(c)] = '-';
58     }
59     auto it = std::remove_if(oppositeAccess.begin(), oppositeAccess.end(), [](char c) {return c == '-';});
60     oppositeAccess.erase(it, oppositeAccess.end());
61     return oppositeAccess;
62 }
63
64 void checkExactSmackAccesses(const std::string &subject, const std::string &object,
65                              const std::string &access)
66 {
67     std::string access_str(access);
68     auto no_access = accessOpposite(access_str);
69     for (char c : access_str) {
70         int ret = smack_have_access(subject.c_str(), object.c_str(), std::string(1, c).c_str());
71         RUNNER_ASSERT_MSG(ret >= 0, "smack_have_access failed: <" << subject << ">, <" << object
72                           << ">, <" << c << "> errno=" << strerror(errno));
73         RUNNER_ASSERT_MSG(ret == 1, "Access " << c << " from " << subject << " to "
74                           << object << " not given");
75     }
76
77     for (char c : no_access) {
78         int ret = smack_have_access(subject.c_str(), object.c_str(), std::string(1, c).c_str());
79         RUNNER_ASSERT_MSG(ret >= 0, "smack_have_access failed: <" << subject << ">, <" << object
80                           << ">, <" << c << "> errno=" << strerror(errno));
81         RUNNER_ASSERT_MSG(ret == 0, "Access " << c << " from " << subject << " to "
82                           << object << " unnecessarily given");
83     }
84 }
85
86 } // namespace anonymous
87
88 namespace SecurityManagerTest
89 {
90
91 void AppInstallHelperExt::checkPrivileges(const PolicyConfiguration::PrivVector &allowedPrivs,
92                                           const PolicyConfiguration::PrivVector &deniedPrivs) const
93 {
94     /* Privileges should be granted to all users if root installs app */
95     auto user = (m_uidGid == 0 ? ANY_USER_REPRESENTATION : std::to_string(m_uidGid));
96
97     std::string smackLabel = generateProcessLabel(m_appName, m_pkgName, m_isHybrid);
98
99     CynaraTestClient::Client ctc;
100     int result;
101
102     for (auto &priv : allowedPrivs) {
103         ctc.check(smackLabel.c_str(), "", user, priv.c_str(), CYNARA_API_ACCESS_ALLOWED);
104
105         Api::appHasPrivilege(m_appName, priv, m_uidGid, result);
106         RUNNER_ASSERT_MSG(result == 1,
107                           "Application " << m_appName << " has no access to " << priv);
108     }
109
110     for (auto &priv : deniedPrivs) {
111         ctc.check(smackLabel.c_str(), "", user, priv.c_str(), CYNARA_API_ACCESS_DENIED);
112
113         Api::appHasPrivilege(m_appName, priv, m_uidGid, result);
114         RUNNER_ASSERT_MSG(result == 0,
115                           "Application " << m_appName << " has unexpected access to " << priv);
116     }
117 }
118
119 void AppInstallHelperExt::checkDeniedPrivileges(const PolicyConfiguration::PrivVector &deniedPrivs) const
120 {
121     checkPrivileges({}, deniedPrivs);
122 }
123
124 void AppInstallHelperExt::checkPrivilegeGroups(const PolicyConfiguration::PrivVector &allowedPrivs) const
125 {
126     static PolicyConfiguration policy;
127     const auto allowed_groups = policy.privToGroup(allowedPrivs);
128     RUNNER_ASSERT_MSG(allowed_groups.size() == allowedPrivs.size(),
129                       "Some privileges given were not found in the policy");
130
131     std::vector<gid_t> allowed_gids;
132     for (const auto &groupName : allowed_groups) {
133         errno = 0;
134         struct group* grp = getgrnam(groupName.c_str());
135         RUNNER_ASSERT_ERRNO_MSG(grp, "Group: " << groupName << " not found");
136         allowed_gids.push_back(grp->gr_gid);
137     }
138
139     checkGids(allowed_gids);
140 }
141
142 void AppInstallHelperExt::checkAfterInstall() const
143 {
144     static const std::vector<AccessRequest> staticRules[] =
145         {parseSmackRulesFile(PolicyConfiguration::getPkgRulesFilePath()),
146          parseSmackRulesFile(PolicyConfiguration::getAppRulesFilePath())};
147
148     checkAppIdExistence(true);
149
150     checkSmackAccesses(staticRules[m_isHybrid]);
151
152     checkPrivileges(getPrivilegesNames(), {});
153 }
154
155 void AppInstallHelperExt::checkAfterUninstall(bool removePkg) const
156 {
157     checkAppIdExistence(false);
158
159     if (removePkg) {
160         checkPkgSmackRulesAfterUninstall();
161     }
162     // there should be no hybrid rules regardless of the app type
163     checkHybridAppSmackRulesAterUninstall();
164
165     checkDeniedPrivileges(getPrivilegesNames());
166 }
167
168 void AppInstallHelperExt::checkSmackAccesses(std::vector<AccessRequest> rules, bool hasAccess) const
169 {
170     const std::pair<std::string, std::string> switchAliases[] = {
171         {"~PATH_RW~", generatePathRWLabel(m_pkgName)},
172         {"~PATH_RO~", generatePathROLabel(m_pkgName)},
173         {"~PROCESS~", generateProcessLabel(m_appName, m_pkgName, m_isHybrid)}
174     };
175
176     for (auto& rule : rules) {
177         if (rule.object == "~PATH_TRUSTED~") {
178             continue;
179         }
180
181         for (const auto &alias : switchAliases) {
182             if (rule.subject == alias.first) {
183                 rule.subject = alias.second;
184             }
185             if (rule.object == alias.first) {
186                 rule.object = alias.second;
187             }
188         }
189
190         if (!hasAccess)
191             rule.access = "";
192
193         // Special label that may occur in the template. Other special labels will not be used.
194         if (rule.object == "_") {
195             rule.access = "rx" + rule.access;
196         }
197
198         checkExactSmackAccesses(rule.subject, rule.object, rule.access);
199     }
200 }
201
202 void AppInstallHelperExt::checkPkgSmackRulesAfterUninstall() const
203 {
204     const std::vector<AccessRequest> rules(std::move(parseSmackRulesFile(SMACK_RULES_PATH)));
205     const std::string labels[] = {generatePathRWLabel(m_pkgName),
206                                   generatePathROLabel(m_pkgName),
207                                   generateProcessLabel(m_appName, m_pkgName, true),
208                                   generateProcessLabel(m_appName, m_pkgName)};
209
210     for (const auto &rule : rules) {
211         for (const auto &label : labels) {
212             assertNoLabelInRule(rule, label);
213         }
214     }
215 }
216
217 void AppInstallHelperExt::checkHybridAppSmackRulesAterUninstall() const
218 {
219     const std::vector<AccessRequest> rules(parseSmackRulesFile(SMACK_RULES_PATH));
220     const std::string appLabel = generateProcessLabel(m_appName, m_pkgName, true);
221
222     for (const auto &rule : rules) {
223         assertNoLabelInRule(rule, appLabel);
224     }
225 }
226
227 void AppInstallHelperExt::checkAppIdExistence(bool expected) const
228 {
229     lib_retcode ret = expected ? SECURITY_MANAGER_SUCCESS : SECURITY_MANAGER_ERROR_NO_SUCH_OBJECT;
230     std::string retPkgId = Api::getPkgId(m_appName, ret);
231
232     if (expected) {
233         RUNNER_ASSERT_MSG(m_pkgName == retPkgId,
234                           "The given appId does not belong to the given pkgId.");
235     }
236 }
237
238 void AppInstallHelperExt::checkGids(const std::vector<gid_t> &allowedGids) const
239 {
240     int ret;
241     std::unordered_set<gid_t> referenceGids(allowedGids.begin(), allowedGids.end());
242
243     // Reset supplementary groups
244     ret = setgroups(0, NULL);
245     RUNNER_ASSERT_MSG(ret != -1, "Unable to set supplementary groups");
246
247     Api::setProcessGroups(m_appName);
248
249     ret = getgroups(0, nullptr);
250     RUNNER_ASSERT_MSG(ret != -1, "Unable to get supplementary groups");
251
252     std::vector<gid_t> actualGids(ret);
253     ret = getgroups(ret, actualGids.data());
254     RUNNER_ASSERT_MSG(ret != -1, "Unable to get supplementary groups");
255
256     for (const auto &gid : actualGids) {
257         RUNNER_ASSERT_MSG(referenceGids.count(gid) > 0,
258                           "Application shouldn't get access to group " << gid);
259         referenceGids.erase(gid);
260     }
261
262     RUNNER_ASSERT_MSG(referenceGids.empty(), "Application didn't get access to some groups");
263 }
264
265 } // namespace SecurityManagerTest