Remove unused shared ro template
[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 CONF_DIR "/usr/share/security-manager/policy/"
59 #define SMACK_RULES_PATH "/sys/fs/smackfs/load2"
60
61 #define ALLOW 0
62 #define DENY -1
63
64 // Common DB/nftw checks
65
66 // nftw doesn't allow passing user data to functions. Work around by using global variable
67 static std::string nftw_expected_label;
68 bool nftw_expected_transmute;
69 bool nftw_expected_exec;
70
71 static int nftw_check_sm_labels_app_dir(const char *fpath, const struct stat *sb,
72                               const char* correctLabel, bool transmute_test, bool exec_test)
73 {
74     int result;
75     CStringPtr labelPtr;
76     char* label = nullptr;
77
78     /* ACCESS */
79     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_ACCESS);
80     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
81     labelPtr.reset(label);
82     RUNNER_ASSERT_MSG(label != nullptr, "ACCESS label on " << fpath << " is not set");
83     result = strcmp(correctLabel, label);
84     RUNNER_ASSERT_MSG(result == 0, "ACCESS label on " << fpath << " is incorrect"
85             " (should be '" << correctLabel << "' and is '" << label << "')");
86
87
88     /* EXEC */
89     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_EXEC);
90     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
91     labelPtr.reset(label);
92
93     if (S_ISREG(sb->st_mode) && (sb->st_mode & S_IXUSR) && exec_test) {
94         RUNNER_ASSERT_MSG(label != nullptr, "EXEC label on " << fpath << " is not set");
95         result = strcmp(correctLabel, label);
96         RUNNER_ASSERT_MSG(result == 0, "Incorrect EXEC label on executable file " << fpath);
97     } else
98         RUNNER_ASSERT_MSG(label == nullptr, "EXEC label on " << fpath << " is set");
99
100
101     /* TRANSMUTE */
102     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_TRANSMUTE);
103     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
104     labelPtr.reset(label);
105
106     if (S_ISDIR(sb->st_mode) && transmute_test == true) {
107         RUNNER_ASSERT_MSG(label != nullptr, "TRANSMUTE label on " << fpath << " is not set at all");
108         RUNNER_ASSERT_MSG(strcmp(label,"TRUE") == 0,
109                 "TRANSMUTE label on " << fpath << " is not set properly: '"<<label<<"'");
110     } else {
111         RUNNER_ASSERT_MSG(label == nullptr, "TRANSMUTE label on " << fpath << " is set");
112     }
113
114     return 0;
115 }
116
117 static int nftw_check_sm_labels(const char *fpath, const struct stat *sb,
118                                int /*typeflag*/, struct FTW* /*ftwbuf*/)
119 {
120     return nftw_check_sm_labels_app_dir(fpath, sb,
121         nftw_expected_label.c_str(), nftw_expected_transmute, nftw_expected_exec);
122 }
123
124 int nftw_check_labels_non_app_dir(const char *fpath, const struct stat* /*sb*/,
125                                   int /*typeflag*/, struct FTW* /*ftwbuf*/)
126 {
127     int result;
128     CStringPtr labelPtr;
129     char* label = nullptr;
130
131     /* ACCESS */
132     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_ACCESS);
133     labelPtr.reset(label);
134     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
135     result = strcmp("canary_label", labelPtr.get());
136     RUNNER_ASSERT_MSG(result == 0, "ACCESS label on " << fpath << " is overwritten");
137
138     /* EXEC */
139     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_EXEC);
140     labelPtr.reset(label);
141     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
142     result = strcmp("canary_label", labelPtr.get());
143     RUNNER_ASSERT_MSG(result == 0, "EXEC label on " << fpath << " is overwritten");
144
145     /* TRANSMUTE */
146     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_TRANSMUTE);
147     labelPtr.reset(label);
148     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
149     RUNNER_ASSERT_MSG(labelPtr.get() == nullptr, "TRANSMUTE label on " << fpath << " is set");
150
151     return 0;
152 }
153
154 int nftw_remove_labels(const char *fpath, const struct stat* /*sb*/,
155                        int /*typeflag*/, struct FTW* /*ftwbuf*/)
156 {
157     smack_lsetlabel(fpath, nullptr, SMACK_LABEL_ACCESS);
158     smack_lsetlabel(fpath, nullptr, SMACK_LABEL_EXEC);
159     smack_lsetlabel(fpath, nullptr, SMACK_LABEL_TRANSMUTE);
160
161     return 0;
162 }
163
164 void check_app_permissions(const std::string &app_id, const std::string &pkg_id,
165                            const std::string &user, const privileges_t &allowed_privs,
166                            const privileges_t &denied_privs, bool isHybrid)
167 {
168     (void) pkg_id;
169     std::string smackLabel = generateProcessLabel(app_id, pkg_id, isHybrid);
170
171     CynaraTestClient::Client ctc;
172
173     for (auto &priv : allowed_privs) {
174         ctc.check(smackLabel.c_str(), "", user, priv.c_str(), CYNARA_API_ACCESS_ALLOWED);
175     }
176
177     for (auto &priv : denied_privs) {
178         ctc.check(smackLabel.c_str(), "", user, priv.c_str(), CYNARA_API_ACCESS_DENIED);
179     }
180 }
181
182 void sm_app_has_privileges(const AppInstallHelper &app,
183                            const std::vector<std::string> &privileges,
184                            int expectedResult)
185 {
186     for (auto const &privilege : privileges) {
187         int result;
188         Api::appHasPrivilege(app.getAppId(), privilege, app.getUID(), result);
189         RUNNER_ASSERT_MSG(result == expectedResult, "Application " << app.getAppId()
190                           << " has unexpected access to " << privilege << ", is : "
191                           << " should be : " << expectedResult );
192     }
193 }
194
195 static void check_app_smack_accesses(const std::string &appId, const std::string &pkgId,
196                                      bool isHybrid = false)
197 {
198     static const std::vector<AccessRequest> rules[] =
199         {parseSmackRulesFile(CONF_DIR "pkg-rules-template.smack"),
200          parseSmackRulesFile(CONF_DIR "app-rules-template.smack")};
201
202     const std::pair<std::string, std::string> switchAliases[] =
203         {std::make_pair("~PATH_RW~", generatePathRWLabel(pkgId)),
204          std::make_pair("~PATH_RO~", generatePathROLabel(pkgId)),
205          std::make_pair("~PROCESS~", generateProcessLabel(appId, pkgId, isHybrid))};
206
207     for (auto rule : rules[isHybrid]) {
208         if (rule.object == "~PATH_TRUSTED~") {
209             continue;
210         }
211
212         for (const auto &alias : switchAliases) {
213             if (rule.subject == alias.first) {
214                 rule.subject = alias.second;
215             }
216             if (rule.object == alias.first) {
217                 rule.object = alias.second;
218             }
219         }
220
221         if (rule.object == "_") {
222             rule.access = "rx" + rule.access;
223         }
224
225         check_exact_smack_accesses(rule.subject, rule.object, rule.access);
226     }
227 }
228
229 static void assert_no_label_in_rule(const AccessRequest &rule, const std::string &label)
230 {
231     RUNNER_ASSERT_MSG(rule.object != label && rule.subject != label,
232                       "Smack rule left after uninstallation process." <<
233                       " Subject: " << rule.subject <<
234                       " object: " << rule.object <<
235                       " access: " << rule.access);
236 }
237
238 static void check_pkg_smack_rules_after_uninstall(const std::string &appId, const std::string &pkgId)
239 {
240     const std::vector<AccessRequest> rules(std::move(parseSmackRulesFile(SMACK_RULES_PATH)));
241     const std::string labels[] = {generatePathRWLabel(pkgId),
242                                   generatePathROLabel(pkgId),
243                                   generateProcessLabel(appId, pkgId, true),
244                                   generateProcessLabel(appId, pkgId)};
245     for (const auto &rule : rules) {
246         for (const auto &label : labels) {
247             assert_no_label_in_rule(rule, label);
248         }
249     }
250 }
251
252 static void check_hybrid_app_smack_rules_after_uninstall(const std::string &appId, const std::string &pkgId)
253 {
254     const std::vector<AccessRequest> rules(std::move(parseSmackRulesFile(SMACK_RULES_PATH)));
255     const std::string appLabel = generateProcessLabel(appId, pkgId, true);
256     for (const auto &rule : rules) {
257         assert_no_label_in_rule(rule, appLabel);
258     }
259 }
260
261 static void check_app(const std::string &appId, const std::string &pkgId,
262                       bool shouldBeInstalled, bool isHybrid, bool removePkg)
263 {
264     char *retPkgId;
265     int ret = security_manager_get_app_pkgid(&retPkgId, appId.c_str());
266
267     if (shouldBeInstalled) {
268         RUNNER_ASSERT_MSG(ret == SECURITY_MANAGER_SUCCESS, "The given appId is not installed.");
269
270         if (ret == SECURITY_MANAGER_SUCCESS) {
271             CStringPtr retPkgIdPtr(retPkgId);
272             RUNNER_ASSERT_MSG(strcmp(pkgId.c_str(), retPkgId) == 0,
273                               "The given appId does not belong to the given pkgId.");
274         }
275         check_app_smack_accesses(appId, pkgId, isHybrid);
276     } else {
277         RUNNER_ASSERT_MSG(ret == SECURITY_MANAGER_ERROR_NO_SUCH_OBJECT, "The given appId is installed.");
278
279         if (removePkg) {
280             check_pkg_smack_rules_after_uninstall(appId, pkgId);
281         } else if (isHybrid) {
282             check_hybrid_app_smack_rules_after_uninstall(appId, pkgId);
283         }
284     }
285 }
286
287 void check_app_after_install(const std::string &app_id, const std::string &pkg_id, bool isHybrid)
288 {
289     check_app(app_id, pkg_id, true, isHybrid, false);
290 }
291
292 static void check_app_gids(const std::string &app_id, const std::vector<gid_t> &allowed_gids)
293 {
294     int ret;
295     gid_t main_gid = getgid();
296     std::unordered_set<gid_t> reference_gids(allowed_gids.begin(), allowed_gids.end());
297
298     // Reset supplementary groups
299     ret = setgroups(0, NULL);
300     RUNNER_ASSERT_MSG(ret != -1, "Unable to set supplementary groups");
301
302     Api::setProcessGroups(app_id);
303
304     ret = getgroups(0, nullptr);
305     RUNNER_ASSERT_MSG(ret != -1, "Unable to get supplementary groups");
306
307     std::vector<gid_t> actual_gids(ret);
308     ret = getgroups(ret, actual_gids.data());
309     RUNNER_ASSERT_MSG(ret != -1, "Unable to get supplementary groups");
310
311     for (const auto &gid : actual_gids) {
312         RUNNER_ASSERT_MSG(gid == main_gid || reference_gids.count(gid) > 0,
313             "Application shouldn't get access to group " << gid);
314         reference_gids.erase(gid);
315     }
316
317     RUNNER_ASSERT_MSG(reference_gids.empty(), "Application didn't get access to some groups");
318 }
319
320 static const char *const ANY_USER_REPRESENTATION = "anyuser";/*this may be actually any string*/
321
322 void check_app_after_install(const std::string &app_id, const std::string &pkg_id,
323                              const privileges_t &allowed_privs,
324                              const privileges_t &denied_privs,
325                              bool isHybrid)
326 {
327     check_app(app_id, pkg_id, true, isHybrid, false);
328     /* Privileges should be granted to all users if root installs app */
329     check_app_permissions(app_id, pkg_id, ANY_USER_REPRESENTATION, allowed_privs, denied_privs, isHybrid);
330
331     PolicyConfiguration policy;
332     const PolicyConfiguration::GroupVector allowed_groups = policy.privToGroup(allowed_privs);
333     RUNNER_ASSERT_MSG(allowed_groups.size() == allowed_privs.size(),
334                       "Some privileges given were not found in the policy");
335
336     std::vector<gid_t> allowed_gids;
337     for (const auto &groupName : allowed_groups) {
338         errno = 0;
339         struct group* grp = getgrnam(groupName.c_str());
340         RUNNER_ASSERT_ERRNO_MSG(grp, "Group: " << groupName << " not found");
341         allowed_gids.push_back(grp->gr_gid);
342     }
343
344     check_app_gids(app_id, allowed_gids);
345 }
346
347 void check_path(const std::string &path, const std::string &label, bool transmute, bool execute) {
348     nftw_expected_label = label;
349     nftw_expected_transmute = transmute;
350     nftw_expected_exec = execute;
351
352     // check labels
353     int result = nftw(path.c_str(), &nftw_check_sm_labels, FTW_MAX_FDS, FTW_PHYS);
354     RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << path);
355 }
356
357 void check_app_after_uninstall(const std::string &app_id, const std::string &pkg_id,
358                                bool isHybrid, bool removePkg)
359 {
360     check_app(app_id, pkg_id, false, isHybrid, removePkg);
361 }
362
363 void check_app_after_uninstall(const std::string &app_id, const std::string &pkg_id,
364                                const privileges_t &privileges, bool isHybrid,
365                                bool removePkg)
366 {
367     check_app(app_id, pkg_id, false, isHybrid, removePkg);
368
369     /* Privileges should not be granted anymore to any user */
370     check_app_permissions(app_id, pkg_id, ANY_USER_REPRESENTATION, {}, privileges, isHybrid);
371 }
372
373 std::string access_opposite(std::string &access) {
374     static const std::map<char, int> access_mapping = {{'r', 0}, {'w', 1}, {'x', 2}, {'a', 3},
375                                                        {'t', 4}, {'l', 5}};
376     // May write implies may lock
377     if (access.find('w') != std::string::npos && access.find('l') == std::string::npos) {
378         access.append("l");
379     }
380     std::string access_opposite = "rwxatl";
381     for (char c : access) {
382         access_opposite[access_mapping.at(c)] = '-';
383     }
384     auto it = std::remove_if(access_opposite.begin(), access_opposite.end(), [](char c) {return c == '-';});
385     access_opposite.erase(it, access_opposite.end());
386     return access_opposite;
387 }
388
389 void check_exact_smack_accesses(const std::string &subject, const std::string &object,
390                                 const std::string &access) {
391     std::string access_str(access);
392     auto no_access = access_opposite(access_str);
393     for (char c : access_str) {
394         int ret = smack_have_access(subject.c_str(), object.c_str(), std::string(1, c).c_str());
395         RUNNER_ASSERT_MSG(ret >= 0, "smack_have_access failed: <" << subject << ">, <" << object
396                           << ">, <" << c << "> errno=" << strerror(errno));
397         RUNNER_ASSERT_MSG(ret == 1, "Access " << c << " from " << subject << " to "
398                           << object << " not given");
399     }
400
401     for (char c : no_access) {
402         int ret = smack_have_access(subject.c_str(), object.c_str(), std::string(1, c).c_str());
403         RUNNER_ASSERT_MSG(ret >= 0, "smack_have_access failed: <" << subject << ">, <" << object
404                           << ">, <" << c << "> errno=" << strerror(errno));
405         RUNNER_ASSERT_MSG(ret == 0, "Access " << c << " from " << subject << " to "
406                           << object << " unnecessarily given");
407     }
408 }
409
410 CapsSetsUniquePtr setCaps(const char *cap_string)
411 {
412     CapsSetsUniquePtr caps(cap_init());
413     caps.reset(cap_from_text(cap_string));
414     RUNNER_ASSERT_MSG(caps, "can't convert capabilities from text");
415     int result = cap_set_proc(caps.get());
416     RUNNER_ASSERT_MSG(result == 0, "can't set capabilities. Result: " << result);
417     return caps;
418 }
419
420 static int getOppositeAccessType(int accessType) {
421     return accessType ^ (R_OK | W_OK | X_OK);
422 }
423
424 static const std::unordered_map<int, const char* const> accessTypeToString {
425     std::make_pair(0, "F_OK"),
426     std::make_pair(1, "X_OK"),
427     std::make_pair(2, "W_OK"),
428     std::make_pair(3, "W_OK|X_OK"),
429     std::make_pair(4, "R_OK"),
430     std::make_pair(5, "R_OK|X_OK"),
431     std::make_pair(6, "R_OK|W_OK"),
432     std::make_pair(7, "R_OK|W_OK|X_OK")
433 };
434
435 void accessCheck(const std::string &id, const std::string &path, int accessType,
436                  int expected)
437 {
438     RUNNER_ASSERT_MSG(::access(path.c_str(), accessType) == expected,
439                       "access from " << id << " to path " << path
440                        << (expected == 0 ? " not granted" : " unnecessarily granted")
441                        << " (" << accessTypeToString.at(accessType) << ")");
442 }
443
444 void accessTest(const std::string &id, const std::string &testPath, int accessType) {
445     int oppositeAccessType = getOppositeAccessType(accessType);
446
447     if (accessType != 0) {
448         accessCheck(id, testPath, accessType, ALLOW);
449     }
450     if (oppositeAccessType != 0) {
451         static const std::vector<int> singleAccessTypes = {R_OK, W_OK, X_OK};
452         for (auto singleAccessType : singleAccessTypes) {
453             if (oppositeAccessType & singleAccessType) {
454                 accessCheck(id, testPath, singleAccessType, DENY);
455             }
456         }
457     }
458 }
459
460 void runAccessTest(const std::string &label, uid_t uid, gid_t gid,
461                    const std::string &testPath, int accessType) {
462     auto fun = [&](){
463         ScopedProcessLabel spl(label, false);
464         RUNNER_ASSERT_ERRNO_MSG(0 == drop_root_privileges(uid, gid),
465                                 "drop_root_privileges failed.");
466
467         accessTest(label, testPath, accessType);
468     };
469
470     runInChildParentWait(fun);
471 }
472
473 void runAccessTest(const AppInstallHelper &app, const std::string &testPath, int accessType) {
474     auto fun = [&](){
475         Api::setProcessLabel(app.getAppId());
476         RUNNER_ASSERT_ERRNO_MSG(0 == drop_root_privileges(app.getUID(), app.getGID()),
477                                 "drop_root_privileges failed.");
478         accessTest(app.getAppId(), testPath, accessType);
479     };
480
481     runInChildParentWait(fun);
482 }
483
484 static const std::vector<std::string> SM_SYSTEM_LABELS = {"System", "System::Privileged", "User"};
485
486 void runSystemAccessTest(uid_t uid, gid_t gid, const std::string &testPath, int accessType) {
487     for (const auto &label : SM_SYSTEM_LABELS)
488         runAccessTest(label, uid, gid, testPath, accessType);
489 }
490
491 bool isAskuserDisabled() {
492     static bool isAskuserDisabled = false;
493     static bool isChecked = false;
494
495     if (isChecked)
496         return isAskuserDisabled;
497
498     std::string sysShare = TzPlatformConfig::getPath(TZ_SYS_SHARE);
499     std::string askDisableFile = sysShare + "/askuser_disable";
500
501     isAskuserDisabled = (access(askDisableFile.c_str(), F_OK) != -1);
502     isChecked = true;
503     return isAskuserDisabled;
504 }
505
506 bool isPrivilegePrivacy(const std::string &priv) {
507     return (1 == privilege_info_is_privacy(priv.c_str()));
508 }
509
510 int countPrivacyPrivileges(const PrivilegeVector &privs) {
511     return std::count_if(privs.begin(), privs.end(), isPrivilegePrivacy);
512 }
513
514 int countPrivacyPrivileges(const std::vector<std::string> &privs) {
515     return std::count_if(privs.begin(), privs.end(), isPrivilegePrivacy);
516 }
517
518 int setLauncherSecurityAttributes(uid_t uid, gid_t gid)
519 {
520     // Add launcher capabilities (cap_dac_override, cap_setgid, cap_sys_admin, cap_mac_admin),
521     // launcher is user process, we must drop root privileges (cap_setgid, cap_setuid are needed).
522     // By default, the permitted capability set is cleared when credentials change is made
523     // (if a process drops a capability from its permitted set, it can never reacquire that capability),
524     // setting the "keep capabilities" flag prevents it from being cleared.
525     // Effective capability set is always cleared when credential change is made, we need to add them again.
526
527     setCaps("cap_dac_override+ep cap_setgid+ep cap_sys_admin+ep cap_mac_admin+ep cap_setuid+ep");
528     int ret = prctl(PR_SET_KEEPCAPS, 1, 0, 0);
529     if (ret != 0)
530         return ret;
531
532     ret = drop_root_privileges(uid, gid);
533     if (ret != 0)
534         return ret;
535
536     setCaps("cap_dac_override+ep cap_setgid+ep cap_sys_admin+ep cap_mac_admin+ep");
537     return ret;
538 }
539 int setLauncherSecurityAttributes(TemporaryTestUser &user)
540 {
541     return setLauncherSecurityAttributes(user.getUid(), user.getGid());
542 }
543