e14f1421ae6cee808be88463e01305c0ad718062
[platform/core/test/security-tests.git] / src / security-manager-tests / common / sm_commons.cpp
1 /*
2  * Copyright (c) 2016 - 2019 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 <algorithm>
18 #include <cstring>
19 #include <fcntl.h>
20 #include <ftw.h>
21 #include <grp.h>
22 #include <string>
23 #include <sys/capability.h>
24 #include <sys/prctl.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 #include <unordered_map>
29 #include <cstdlib>
30
31 #include <array>
32 #include <unordered_set>
33 #include <utility>
34 #include <vector>
35
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>
41
42 #include <cynara_test_client.h>
43 #include <dpl/test/test_runner.h>
44 #include <memory.h>
45 #include <sm_api.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>
55
56 using namespace SecurityManagerTest;
57
58 #define SMACK_RULES_PATH "/sys/fs/smackfs/load2"
59
60 #define ALLOW 0
61 #define DENY -1
62
63 // Common DB/nftw checks
64
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;
69
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)
72 {
73     int result;
74     CStringPtr labelPtr;
75     char* label = nullptr;
76
77     /* ACCESS */
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 << "')");
85
86
87     /* EXEC */
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);
91
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);
96     } else
97         RUNNER_ASSERT_MSG(label == nullptr, "EXEC label on " << fpath << " is set");
98
99
100     /* TRANSMUTE */
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);
104
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<<"'");
109     } else {
110         RUNNER_ASSERT_MSG(label == nullptr, "TRANSMUTE label on " << fpath << " is set");
111     }
112
113     return 0;
114 }
115
116 static int nftw_check_sm_labels(const char *fpath, const struct stat *sb,
117                                int /*typeflag*/, struct FTW* /*ftwbuf*/)
118 {
119     return nftw_check_sm_labels_app_dir(fpath, sb,
120         nftw_expected_label.c_str(), nftw_expected_transmute, nftw_expected_exec);
121 }
122
123 int nftw_check_labels_non_app_dir(const char *fpath, const struct stat* /*sb*/,
124                                   int /*typeflag*/, struct FTW* /*ftwbuf*/)
125 {
126     int result;
127     CStringPtr labelPtr;
128     char* label = nullptr;
129
130     /* ACCESS */
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");
136
137     /* EXEC */
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");
143
144     /* TRANSMUTE */
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");
149
150     return 0;
151 }
152
153 int nftw_remove_labels(const char *fpath, const struct stat* /*sb*/,
154                        int /*typeflag*/, struct FTW* /*ftwbuf*/)
155 {
156     smack_lsetlabel(fpath, nullptr, SMACK_LABEL_ACCESS);
157     smack_lsetlabel(fpath, nullptr, SMACK_LABEL_EXEC);
158     smack_lsetlabel(fpath, nullptr, SMACK_LABEL_TRANSMUTE);
159
160     return 0;
161 }
162
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)
166 {
167     (void) pkg_id;
168     std::string smackLabel = generateProcessLabel(app_id, pkg_id, isHybrid);
169
170     CynaraTestClient::Client ctc;
171
172     for (auto &priv : allowed_privs) {
173         ctc.check(smackLabel.c_str(), "", user, priv.c_str(), CYNARA_API_ACCESS_ALLOWED);
174     }
175
176     for (auto &priv : denied_privs) {
177         ctc.check(smackLabel.c_str(), "", user, priv.c_str(), CYNARA_API_ACCESS_DENIED);
178     }
179 }
180
181 void sm_app_has_privileges(const AppInstallHelper &app,
182                            const std::vector<std::string> &privileges,
183                            int expectedResult)
184 {
185     for (auto const &privilege : privileges) {
186         int result;
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 );
191     }
192 }
193
194 static void check_app_smack_accesses(const std::string &appId, const std::string &pkgId,
195                                      bool isHybrid = false)
196 {
197     static const std::vector<AccessRequest> rules[] =
198         {parseSmackRulesFile(PolicyConfiguration::getPkgRulesFilePath()),
199          parseSmackRulesFile(PolicyConfiguration::getAppRulesFilePath())};
200
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))};
205
206     for (auto rule : rules[isHybrid]) {
207         if (rule.object == "~PATH_TRUSTED~") {
208             continue;
209         }
210
211         for (const auto &alias : switchAliases) {
212             if (rule.subject == alias.first) {
213                 rule.subject = alias.second;
214             }
215             if (rule.object == alias.first) {
216                 rule.object = alias.second;
217             }
218         }
219
220         if (rule.object == "_") {
221             rule.access = "rx" + rule.access;
222         }
223
224         check_exact_smack_accesses(rule.subject, rule.object, rule.access);
225     }
226 }
227
228 static void assert_no_label_in_rule(const AccessRequest &rule, const std::string &label)
229 {
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);
235 }
236
237 static void check_pkg_smack_rules_after_uninstall(const std::string &appId, const std::string &pkgId)
238 {
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);
247         }
248     }
249 }
250
251 static void check_hybrid_app_smack_rules_after_uninstall(const std::string &appId, const std::string &pkgId)
252 {
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);
257     }
258 }
259
260 static void check_app(const std::string &appId, const std::string &pkgId,
261                       bool shouldBeInstalled, bool isHybrid, bool removePkg)
262 {
263     char *retPkgId;
264     int ret = security_manager_get_app_pkgid(&retPkgId, appId.c_str());
265
266     if (shouldBeInstalled) {
267         RUNNER_ASSERT_MSG(ret == SECURITY_MANAGER_SUCCESS, "The given appId is not installed.");
268
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.");
273         }
274         check_app_smack_accesses(appId, pkgId, isHybrid);
275     } else {
276         RUNNER_ASSERT_MSG(ret == SECURITY_MANAGER_ERROR_NO_SUCH_OBJECT, "The given appId is installed.");
277
278         if (removePkg) {
279             check_pkg_smack_rules_after_uninstall(appId, pkgId);
280         } else if (isHybrid) {
281             check_hybrid_app_smack_rules_after_uninstall(appId, pkgId);
282         }
283     }
284 }
285
286 void check_app_after_install(const std::string &app_id, const std::string &pkg_id, bool isHybrid)
287 {
288     check_app(app_id, pkg_id, true, isHybrid, false);
289 }
290
291 static void check_app_gids(const std::string &app_id, const std::vector<gid_t> &allowed_gids)
292 {
293     int ret;
294     gid_t main_gid = getgid();
295     std::unordered_set<gid_t> reference_gids(allowed_gids.begin(), allowed_gids.end());
296
297     // Reset supplementary groups
298     ret = setgroups(0, NULL);
299     RUNNER_ASSERT_MSG(ret != -1, "Unable to set supplementary groups");
300
301     Api::setProcessGroups(app_id);
302
303     ret = getgroups(0, nullptr);
304     RUNNER_ASSERT_MSG(ret != -1, "Unable to get supplementary groups");
305
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");
309
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);
314     }
315
316     RUNNER_ASSERT_MSG(reference_gids.empty(), "Application didn't get access to some groups");
317 }
318
319 static const char *const ANY_USER_REPRESENTATION = "anyuser";/*this may be actually any string*/
320
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,
324                              bool isHybrid)
325 {
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);
329
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");
334
335     std::vector<gid_t> allowed_gids;
336     for (const auto &groupName : allowed_groups) {
337         errno = 0;
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);
341     }
342
343     check_app_gids(app_id, allowed_gids);
344 }
345
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;
350
351     // check labels
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);
354 }
355
356 void check_app_after_uninstall(const std::string &app_id, const std::string &pkg_id,
357                                bool isHybrid, bool removePkg)
358 {
359     check_app(app_id, pkg_id, false, isHybrid, removePkg);
360 }
361
362 void check_app_after_uninstall(const std::string &app_id, const std::string &pkg_id,
363                                const privileges_t &privileges, bool isHybrid,
364                                bool removePkg)
365 {
366     check_app(app_id, pkg_id, false, isHybrid, removePkg);
367
368     /* Privileges should not be granted anymore to any user */
369     check_app_permissions(app_id, pkg_id, ANY_USER_REPRESENTATION, {}, privileges, isHybrid);
370 }
371
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},
374                                                        {'t', 4}, {'l', 5}};
375     // May write implies may lock
376     if (access.find('w') != std::string::npos && access.find('l') == std::string::npos) {
377         access.append("l");
378     }
379     std::string access_opposite = "rwxatl";
380     for (char c : access) {
381         access_opposite[access_mapping.at(c)] = '-';
382     }
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;
386 }
387
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");
398     }
399
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");
406     }
407 }
408
409 CapsSetsUniquePtr setCaps(const char *cap_string)
410 {
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);
416     return caps;
417 }
418
419 static int getOppositeAccessType(int accessType) {
420     return accessType ^ (R_OK | W_OK | X_OK);
421 }
422
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")
432 };
433
434 void accessCheck(const std::string &id, const std::string &path, int accessType,
435                  int expected)
436 {
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) << ")");
441 }
442
443 void accessTest(const std::string &id, const std::string &testPath, int accessType) {
444     int oppositeAccessType = getOppositeAccessType(accessType);
445
446     if (accessType != 0) {
447         accessCheck(id, testPath, accessType, ALLOW);
448     }
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);
454             }
455         }
456     }
457 }
458
459 void runAccessTest(const std::string &label, uid_t uid, gid_t gid,
460                    const std::string &testPath, int accessType) {
461     auto fun = [&](){
462         ScopedProcessLabel spl(label, false);
463         RUNNER_ASSERT_ERRNO_MSG(0 == drop_root_privileges(uid, gid),
464                                 "drop_root_privileges failed.");
465
466         accessTest(label, testPath, accessType);
467     };
468
469     runInChildParentWait(fun);
470 }
471
472 void runAccessTest(const AppInstallHelper &app, const std::string &testPath, int accessType) {
473     auto fun = [&](){
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);
478     };
479
480     runInChildParentWait(fun);
481 }
482
483 static const std::vector<std::string> SM_SYSTEM_LABELS = {"System", "System::Privileged", "User"};
484
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);
488 }
489
490 bool isAskuserDisabled() {
491     static bool isAskuserDisabled = false;
492     static bool isChecked = false;
493
494     if (isChecked)
495         return isAskuserDisabled;
496
497     std::string sysShare = TzPlatformConfig::getPath(TZ_SYS_SHARE);
498     std::string askDisableFile = sysShare + "/askuser_disable";
499
500     isAskuserDisabled = (access(askDisableFile.c_str(), F_OK) != -1);
501     isChecked = true;
502     return isAskuserDisabled;
503 }
504
505 bool isPrivilegePrivacy(const std::string &priv) {
506     return (1 == privilege_info_is_privacy(priv.c_str()));
507 }
508
509 int countPrivacyPrivileges(const PrivilegeVector &privs) {
510     return std::count_if(privs.begin(), privs.end(), isPrivilegePrivacy);
511 }
512
513 int countPrivacyPrivileges(const std::vector<std::string> &privs) {
514     return std::count_if(privs.begin(), privs.end(), isPrivilegePrivacy);
515 }
516
517 int setLauncherSecurityAttributes(uid_t uid, gid_t gid)
518 {
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.
525
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);
528     if (ret != 0)
529         return ret;
530
531     ret = drop_root_privileges(uid, gid);
532     if (ret != 0)
533         return ret;
534
535     setCaps("cap_dac_override+ep cap_setgid+ep cap_sys_admin+ep cap_mac_admin+ep");
536     return ret;
537 }
538 int setLauncherSecurityAttributes(TemporaryTestUser &user)
539 {
540     return setLauncherSecurityAttributes(user.getUid(), user.getGid());
541 }
542