2 * Copyright (c) 2016 - 2019 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.
23 #include <sys/capability.h>
24 #include <sys/prctl.h>
26 #include <sys/types.h>
28 #include <unordered_map>
32 #include <unordered_set>
36 #include <security-manager-types.h>
37 #include <app-runtime.h>
38 #include <sys/smack.h>
39 #include <privilege_info.h>
40 #include <scoped_process_label.h>
42 #include <cynara_test_client.h>
43 #include <dpl/test/test_runner.h>
46 #include <sm_commons.h>
47 #include <synchronization_pipe.h>
48 #include <sm_request.h>
49 #include <tests_common.h>
50 #include <policy_configuration.h>
51 #include "tzplatform.h"
52 #include <label_generator.h>
53 #include <template_parser.h>
54 #include <temp_test_user.h>
56 using namespace SecurityManagerTest;
58 #define SMACK_RULES_PATH "/sys/fs/smackfs/load2"
63 // Common DB/nftw checks
65 // nftw doesn't allow passing user data to functions. Work around by using global variable
66 static std::string nftw_expected_label;
67 bool nftw_expected_transmute;
68 bool nftw_expected_exec;
70 static int nftw_check_sm_labels_app_dir(const char *fpath, const struct stat *sb,
71 const char* correctLabel, bool transmute_test, bool exec_test)
75 char* label = nullptr;
78 result = smack_lgetlabel(fpath, &label, SMACK_LABEL_ACCESS);
79 RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
80 labelPtr.reset(label);
81 RUNNER_ASSERT_MSG(label != nullptr, "ACCESS label on " << fpath << " is not set");
82 result = strcmp(correctLabel, label);
83 RUNNER_ASSERT_MSG(result == 0, "ACCESS label on " << fpath << " is incorrect"
84 " (should be '" << correctLabel << "' and is '" << label << "')");
88 result = smack_lgetlabel(fpath, &label, SMACK_LABEL_EXEC);
89 RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
90 labelPtr.reset(label);
92 if (S_ISREG(sb->st_mode) && (sb->st_mode & S_IXUSR) && exec_test) {
93 RUNNER_ASSERT_MSG(label != nullptr, "EXEC label on " << fpath << " is not set");
94 result = strcmp(correctLabel, label);
95 RUNNER_ASSERT_MSG(result == 0, "Incorrect EXEC label on executable file " << fpath);
97 RUNNER_ASSERT_MSG(label == nullptr, "EXEC label on " << fpath << " is set");
101 result = smack_lgetlabel(fpath, &label, SMACK_LABEL_TRANSMUTE);
102 RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
103 labelPtr.reset(label);
105 if (S_ISDIR(sb->st_mode) && transmute_test == true) {
106 RUNNER_ASSERT_MSG(label != nullptr, "TRANSMUTE label on " << fpath << " is not set at all");
107 RUNNER_ASSERT_MSG(strcmp(label,"TRUE") == 0,
108 "TRANSMUTE label on " << fpath << " is not set properly: '"<<label<<"'");
110 RUNNER_ASSERT_MSG(label == nullptr, "TRANSMUTE label on " << fpath << " is set");
116 static int nftw_check_sm_labels(const char *fpath, const struct stat *sb,
117 int /*typeflag*/, struct FTW* /*ftwbuf*/)
119 return nftw_check_sm_labels_app_dir(fpath, sb,
120 nftw_expected_label.c_str(), nftw_expected_transmute, nftw_expected_exec);
123 int nftw_check_labels_non_app_dir(const char *fpath, const struct stat* /*sb*/,
124 int /*typeflag*/, struct FTW* /*ftwbuf*/)
128 char* label = nullptr;
131 result = smack_lgetlabel(fpath, &label, SMACK_LABEL_ACCESS);
132 labelPtr.reset(label);
133 RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
134 result = strcmp("canary_label", labelPtr.get());
135 RUNNER_ASSERT_MSG(result == 0, "ACCESS label on " << fpath << " is overwritten");
138 result = smack_lgetlabel(fpath, &label, SMACK_LABEL_EXEC);
139 labelPtr.reset(label);
140 RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
141 result = strcmp("canary_label", labelPtr.get());
142 RUNNER_ASSERT_MSG(result == 0, "EXEC label on " << fpath << " is overwritten");
145 result = smack_lgetlabel(fpath, &label, SMACK_LABEL_TRANSMUTE);
146 labelPtr.reset(label);
147 RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
148 RUNNER_ASSERT_MSG(labelPtr.get() == nullptr, "TRANSMUTE label on " << fpath << " is set");
153 int nftw_remove_labels(const char *fpath, const struct stat* /*sb*/,
154 int /*typeflag*/, struct FTW* /*ftwbuf*/)
156 smack_lsetlabel(fpath, nullptr, SMACK_LABEL_ACCESS);
157 smack_lsetlabel(fpath, nullptr, SMACK_LABEL_EXEC);
158 smack_lsetlabel(fpath, nullptr, SMACK_LABEL_TRANSMUTE);
163 void check_app_permissions(const std::string &app_id, const std::string &pkg_id,
164 const std::string &user, const privileges_t &allowed_privs,
165 const privileges_t &denied_privs, bool isHybrid)
168 std::string smackLabel = generateProcessLabel(app_id, pkg_id, isHybrid);
170 CynaraTestClient::Client ctc;
172 for (auto &priv : allowed_privs) {
173 ctc.check(smackLabel.c_str(), "", user, priv.c_str(), CYNARA_API_ACCESS_ALLOWED);
176 for (auto &priv : denied_privs) {
177 ctc.check(smackLabel.c_str(), "", user, priv.c_str(), CYNARA_API_ACCESS_DENIED);
181 void sm_app_has_privileges(const AppInstallHelper &app,
182 const std::vector<std::string> &privileges,
185 for (auto const &privilege : privileges) {
187 Api::appHasPrivilege(app.getAppId(), privilege, app.getUID(), result);
188 RUNNER_ASSERT_MSG(result == expectedResult, "Application " << app.getAppId()
189 << " has unexpected access to " << privilege << ", is : "
190 << " should be : " << expectedResult );
194 static void check_app_smack_accesses(const std::string &appId, const std::string &pkgId,
195 bool isHybrid = false)
197 static const std::vector<AccessRequest> rules[] =
198 {parseSmackRulesFile(PolicyConfiguration::getPkgRulesFilePath()),
199 parseSmackRulesFile(PolicyConfiguration::getAppRulesFilePath())};
201 const std::pair<std::string, std::string> switchAliases[] =
202 {std::make_pair("~PATH_RW~", generatePathRWLabel(pkgId)),
203 std::make_pair("~PATH_RO~", generatePathROLabel(pkgId)),
204 std::make_pair("~PROCESS~", generateProcessLabel(appId, pkgId, isHybrid))};
206 for (auto rule : rules[isHybrid]) {
207 if (rule.object == "~PATH_TRUSTED~") {
211 for (const auto &alias : switchAliases) {
212 if (rule.subject == alias.first) {
213 rule.subject = alias.second;
215 if (rule.object == alias.first) {
216 rule.object = alias.second;
220 if (rule.object == "_") {
221 rule.access = "rx" + rule.access;
224 check_exact_smack_accesses(rule.subject, rule.object, rule.access);
228 static void assert_no_label_in_rule(const AccessRequest &rule, const std::string &label)
230 RUNNER_ASSERT_MSG(rule.object != label && rule.subject != label,
231 "Smack rule left after uninstallation process." <<
232 " Subject: " << rule.subject <<
233 " object: " << rule.object <<
234 " access: " << rule.access);
237 static void check_pkg_smack_rules_after_uninstall(const std::string &appId, const std::string &pkgId)
239 const std::vector<AccessRequest> rules(std::move(parseSmackRulesFile(SMACK_RULES_PATH)));
240 const std::string labels[] = {generatePathRWLabel(pkgId),
241 generatePathROLabel(pkgId),
242 generateProcessLabel(appId, pkgId, true),
243 generateProcessLabel(appId, pkgId)};
244 for (const auto &rule : rules) {
245 for (const auto &label : labels) {
246 assert_no_label_in_rule(rule, label);
251 static void check_hybrid_app_smack_rules_after_uninstall(const std::string &appId, const std::string &pkgId)
253 const std::vector<AccessRequest> rules(std::move(parseSmackRulesFile(SMACK_RULES_PATH)));
254 const std::string appLabel = generateProcessLabel(appId, pkgId, true);
255 for (const auto &rule : rules) {
256 assert_no_label_in_rule(rule, appLabel);
260 static void check_app(const std::string &appId, const std::string &pkgId,
261 bool shouldBeInstalled, bool isHybrid, bool removePkg)
264 int ret = security_manager_get_app_pkgid(&retPkgId, appId.c_str());
266 if (shouldBeInstalled) {
267 RUNNER_ASSERT_MSG(ret == SECURITY_MANAGER_SUCCESS, "The given appId is not installed.");
269 if (ret == SECURITY_MANAGER_SUCCESS) {
270 CStringPtr retPkgIdPtr(retPkgId);
271 RUNNER_ASSERT_MSG(strcmp(pkgId.c_str(), retPkgId) == 0,
272 "The given appId does not belong to the given pkgId.");
274 check_app_smack_accesses(appId, pkgId, isHybrid);
276 RUNNER_ASSERT_MSG(ret == SECURITY_MANAGER_ERROR_NO_SUCH_OBJECT, "The given appId is installed.");
279 check_pkg_smack_rules_after_uninstall(appId, pkgId);
280 } else if (isHybrid) {
281 check_hybrid_app_smack_rules_after_uninstall(appId, pkgId);
286 void check_app_after_install(const std::string &app_id, const std::string &pkg_id, bool isHybrid)
288 check_app(app_id, pkg_id, true, isHybrid, false);
291 static void check_app_gids(const std::string &app_id, const std::vector<gid_t> &allowed_gids)
294 gid_t main_gid = getgid();
295 std::unordered_set<gid_t> reference_gids(allowed_gids.begin(), allowed_gids.end());
297 // Reset supplementary groups
298 ret = setgroups(0, NULL);
299 RUNNER_ASSERT_MSG(ret != -1, "Unable to set supplementary groups");
301 Api::setProcessGroups(app_id);
303 ret = getgroups(0, nullptr);
304 RUNNER_ASSERT_MSG(ret != -1, "Unable to get supplementary groups");
306 std::vector<gid_t> actual_gids(ret);
307 ret = getgroups(ret, actual_gids.data());
308 RUNNER_ASSERT_MSG(ret != -1, "Unable to get supplementary groups");
310 for (const auto &gid : actual_gids) {
311 RUNNER_ASSERT_MSG(gid == main_gid || reference_gids.count(gid) > 0,
312 "Application shouldn't get access to group " << gid);
313 reference_gids.erase(gid);
316 RUNNER_ASSERT_MSG(reference_gids.empty(), "Application didn't get access to some groups");
319 static const char *const ANY_USER_REPRESENTATION = "anyuser";/*this may be actually any string*/
321 void check_app_after_install(const std::string &app_id, const std::string &pkg_id,
322 const privileges_t &allowed_privs,
323 const privileges_t &denied_privs,
326 check_app(app_id, pkg_id, true, isHybrid, false);
327 /* Privileges should be granted to all users if root installs app */
328 check_app_permissions(app_id, pkg_id, ANY_USER_REPRESENTATION, allowed_privs, denied_privs, isHybrid);
330 PolicyConfiguration policy;
331 const PolicyConfiguration::GroupVector allowed_groups = policy.privToGroup(allowed_privs);
332 RUNNER_ASSERT_MSG(allowed_groups.size() == allowed_privs.size(),
333 "Some privileges given were not found in the policy");
335 std::vector<gid_t> allowed_gids;
336 for (const auto &groupName : allowed_groups) {
338 struct group* grp = getgrnam(groupName.c_str());
339 RUNNER_ASSERT_ERRNO_MSG(grp, "Group: " << groupName << " not found");
340 allowed_gids.push_back(grp->gr_gid);
343 check_app_gids(app_id, allowed_gids);
346 void check_path(const std::string &path, const std::string &label, bool transmute, bool execute) {
347 nftw_expected_label = label;
348 nftw_expected_transmute = transmute;
349 nftw_expected_exec = execute;
352 int result = nftw(path.c_str(), &nftw_check_sm_labels, FTW_MAX_FDS, FTW_PHYS);
353 RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << path);
356 void check_app_after_uninstall(const std::string &app_id, const std::string &pkg_id,
357 bool isHybrid, bool removePkg)
359 check_app(app_id, pkg_id, false, isHybrid, removePkg);
362 void check_app_after_uninstall(const std::string &app_id, const std::string &pkg_id,
363 const privileges_t &privileges, bool isHybrid,
366 check_app(app_id, pkg_id, false, isHybrid, removePkg);
368 /* Privileges should not be granted anymore to any user */
369 check_app_permissions(app_id, pkg_id, ANY_USER_REPRESENTATION, {}, privileges, isHybrid);
372 std::string access_opposite(std::string &access) {
373 static const std::map<char, int> access_mapping = {{'r', 0}, {'w', 1}, {'x', 2}, {'a', 3},
375 // May write implies may lock
376 if (access.find('w') != std::string::npos && access.find('l') == std::string::npos) {
379 std::string access_opposite = "rwxatl";
380 for (char c : access) {
381 access_opposite[access_mapping.at(c)] = '-';
383 auto it = std::remove_if(access_opposite.begin(), access_opposite.end(), [](char c) {return c == '-';});
384 access_opposite.erase(it, access_opposite.end());
385 return access_opposite;
388 void check_exact_smack_accesses(const std::string &subject, const std::string &object,
389 const std::string &access) {
390 std::string access_str(access);
391 auto no_access = access_opposite(access_str);
392 for (char c : access_str) {
393 int ret = smack_have_access(subject.c_str(), object.c_str(), std::string(1, c).c_str());
394 RUNNER_ASSERT_MSG(ret >= 0, "smack_have_access failed: <" << subject << ">, <" << object
395 << ">, <" << c << "> errno=" << strerror(errno));
396 RUNNER_ASSERT_MSG(ret == 1, "Access " << c << " from " << subject << " to "
397 << object << " not given");
400 for (char c : no_access) {
401 int ret = smack_have_access(subject.c_str(), object.c_str(), std::string(1, c).c_str());
402 RUNNER_ASSERT_MSG(ret >= 0, "smack_have_access failed: <" << subject << ">, <" << object
403 << ">, <" << c << "> errno=" << strerror(errno));
404 RUNNER_ASSERT_MSG(ret == 0, "Access " << c << " from " << subject << " to "
405 << object << " unnecessarily given");
409 CapsSetsUniquePtr setCaps(const char *cap_string)
411 CapsSetsUniquePtr caps(cap_init());
412 caps.reset(cap_from_text(cap_string));
413 RUNNER_ASSERT_MSG(caps, "can't convert capabilities from text");
414 int result = cap_set_proc(caps.get());
415 RUNNER_ASSERT_MSG(result == 0, "can't set capabilities. Result: " << result);
419 static int getOppositeAccessType(int accessType) {
420 return accessType ^ (R_OK | W_OK | X_OK);
423 static const std::unordered_map<int, const char* const> accessTypeToString {
424 std::make_pair(0, "F_OK"),
425 std::make_pair(1, "X_OK"),
426 std::make_pair(2, "W_OK"),
427 std::make_pair(3, "W_OK|X_OK"),
428 std::make_pair(4, "R_OK"),
429 std::make_pair(5, "R_OK|X_OK"),
430 std::make_pair(6, "R_OK|W_OK"),
431 std::make_pair(7, "R_OK|W_OK|X_OK")
434 void accessCheck(const std::string &id, const std::string &path, int accessType,
437 RUNNER_ASSERT_MSG(::access(path.c_str(), accessType) == expected,
438 "access from " << id << " to path " << path
439 << (expected == 0 ? " not granted" : " unnecessarily granted")
440 << " (" << accessTypeToString.at(accessType) << ")");
443 void accessTest(const std::string &id, const std::string &testPath, int accessType) {
444 int oppositeAccessType = getOppositeAccessType(accessType);
446 if (accessType != 0) {
447 accessCheck(id, testPath, accessType, ALLOW);
449 if (oppositeAccessType != 0) {
450 static const std::vector<int> singleAccessTypes = {R_OK, W_OK, X_OK};
451 for (auto singleAccessType : singleAccessTypes) {
452 if (oppositeAccessType & singleAccessType) {
453 accessCheck(id, testPath, singleAccessType, DENY);
459 void runAccessTest(const std::string &label, uid_t uid, gid_t gid,
460 const std::string &testPath, int accessType) {
462 ScopedProcessLabel spl(label, false);
463 RUNNER_ASSERT_ERRNO_MSG(0 == drop_root_privileges(uid, gid),
464 "drop_root_privileges failed.");
466 accessTest(label, testPath, accessType);
469 runInChildParentWait(fun);
472 void runAccessTest(const AppInstallHelper &app, const std::string &testPath, int accessType) {
474 Api::setProcessLabel(app.getAppId());
475 RUNNER_ASSERT_ERRNO_MSG(0 == drop_root_privileges(app.getUID(), app.getGID()),
476 "drop_root_privileges failed.");
477 accessTest(app.getAppId(), testPath, accessType);
480 runInChildParentWait(fun);
483 static const std::vector<std::string> SM_SYSTEM_LABELS = {"System", "System::Privileged", "User"};
485 void runSystemAccessTest(uid_t uid, gid_t gid, const std::string &testPath, int accessType) {
486 for (const auto &label : SM_SYSTEM_LABELS)
487 runAccessTest(label, uid, gid, testPath, accessType);
490 bool isAskuserDisabled() {
491 static bool isAskuserDisabled = false;
492 static bool isChecked = false;
495 return isAskuserDisabled;
497 std::string sysShare = TzPlatformConfig::getPath(TZ_SYS_SHARE);
498 std::string askDisableFile = sysShare + "/askuser_disable";
500 isAskuserDisabled = (access(askDisableFile.c_str(), F_OK) != -1);
502 return isAskuserDisabled;
505 bool isPrivilegePrivacy(const std::string &priv) {
506 return (1 == privilege_info_is_privacy(priv.c_str()));
509 int countPrivacyPrivileges(const PrivilegeVector &privs) {
510 return std::count_if(privs.begin(), privs.end(), isPrivilegePrivacy);
513 int countPrivacyPrivileges(const std::vector<std::string> &privs) {
514 return std::count_if(privs.begin(), privs.end(), isPrivilegePrivacy);
517 int setLauncherSecurityAttributes(uid_t uid, gid_t gid)
519 // Add launcher capabilities (cap_dac_override, cap_setgid, cap_sys_admin, cap_mac_admin),
520 // launcher is user process, we must drop root privileges (cap_setgid, cap_setuid are needed).
521 // By default, the permitted capability set is cleared when credentials change is made
522 // (if a process drops a capability from its permitted set, it can never reacquire that capability),
523 // setting the "keep capabilities" flag prevents it from being cleared.
524 // Effective capability set is always cleared when credential change is made, we need to add them again.
526 setCaps("cap_dac_override+ep cap_setgid+ep cap_sys_admin+ep cap_mac_admin+ep cap_setuid+ep");
527 int ret = prctl(PR_SET_KEEPCAPS, 1, 0, 0);
531 ret = drop_root_privileges(uid, gid);
535 setCaps("cap_dac_override+ep cap_setgid+ep cap_sys_admin+ep cap_mac_admin+ep");
538 int setLauncherSecurityAttributes(TemporaryTestUser &user)
540 return setLauncherSecurityAttributes(user.getUid(), user.getGid());