Add SM tests covering many apps in single request
[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/stat.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27 #include <unordered_map>
28 #include <cstdlib>
29
30 #include <array>
31 #include <unordered_set>
32 #include <utility>
33 #include <vector>
34
35 #include <security-manager-types.h>
36 #include <app-runtime.h>
37 #include <sys/smack.h>
38 #include <privilege_info.h>
39 #include <scoped_process_label.h>
40
41 #include <cynara_test_client.h>
42 #include <dpl/test/test_runner.h>
43 #include <memory.h>
44 #include <sm_api.h>
45 #include <sm_commons.h>
46 #include <synchronization_pipe.h>
47 #include <sm_request.h>
48 #include <tests_common.h>
49 #include <policy_configuration.h>
50 #include "tzplatform.h"
51 #include <label_generator.h>
52 #include <template_parser.h>
53
54 using namespace SecurityManagerTest;
55
56 #define CONF_DIR "/usr/share/security-manager/policy/"
57
58 // Common DB/nftw checks
59
60 // nftw doesn't allow passing user data to functions. Work around by using global variable
61 static std::string nftw_expected_label;
62 bool nftw_expected_transmute;
63 bool nftw_expected_exec;
64
65 static int nftw_check_sm_labels_app_dir(const char *fpath, const struct stat *sb,
66                               const char* correctLabel, bool transmute_test, bool exec_test)
67 {
68     int result;
69     CStringPtr labelPtr;
70     char* label = nullptr;
71
72     /* ACCESS */
73     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_ACCESS);
74     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
75     labelPtr.reset(label);
76     RUNNER_ASSERT_MSG(label != nullptr, "ACCESS label on " << fpath << " is not set");
77     result = strcmp(correctLabel, label);
78     RUNNER_ASSERT_MSG(result == 0, "ACCESS label on " << fpath << " is incorrect"
79             " (should be '" << correctLabel << "' and is '" << label << "')");
80
81
82     /* EXEC */
83     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_EXEC);
84     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
85     labelPtr.reset(label);
86
87     if (S_ISREG(sb->st_mode) && (sb->st_mode & S_IXUSR) && exec_test) {
88         RUNNER_ASSERT_MSG(label != nullptr, "EXEC label on " << fpath << " is not set");
89         result = strcmp(correctLabel, label);
90         RUNNER_ASSERT_MSG(result == 0, "Incorrect EXEC label on executable file " << fpath);
91     } else
92         RUNNER_ASSERT_MSG(label == nullptr, "EXEC label on " << fpath << " is set");
93
94
95     /* TRANSMUTE */
96     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_TRANSMUTE);
97     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
98     labelPtr.reset(label);
99
100     if (S_ISDIR(sb->st_mode) && transmute_test == true) {
101         RUNNER_ASSERT_MSG(label != nullptr, "TRANSMUTE label on " << fpath << " is not set at all");
102         RUNNER_ASSERT_MSG(strcmp(label,"TRUE") == 0,
103                 "TRANSMUTE label on " << fpath << " is not set properly: '"<<label<<"'");
104     } else {
105         RUNNER_ASSERT_MSG(label == nullptr, "TRANSMUTE label on " << fpath << " is set");
106     }
107
108     return 0;
109 }
110
111 static int nftw_check_sm_labels(const char *fpath, const struct stat *sb,
112                                int /*typeflag*/, struct FTW* /*ftwbuf*/)
113 {
114     return nftw_check_sm_labels_app_dir(fpath, sb,
115         nftw_expected_label.c_str(), nftw_expected_transmute, nftw_expected_exec);
116 }
117
118 int nftw_check_labels_non_app_dir(const char *fpath, const struct stat* /*sb*/,
119                                   int /*typeflag*/, struct FTW* /*ftwbuf*/)
120 {
121     int result;
122     CStringPtr labelPtr;
123     char* label = nullptr;
124
125     /* ACCESS */
126     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_ACCESS);
127     labelPtr.reset(label);
128     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
129     result = strcmp("canary_label", labelPtr.get());
130     RUNNER_ASSERT_MSG(result == 0, "ACCESS label on " << fpath << " is overwritten");
131
132     /* EXEC */
133     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_EXEC);
134     labelPtr.reset(label);
135     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
136     result = strcmp("canary_label", labelPtr.get());
137     RUNNER_ASSERT_MSG(result == 0, "EXEC label on " << fpath << " is overwritten");
138
139     /* TRANSMUTE */
140     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_TRANSMUTE);
141     labelPtr.reset(label);
142     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
143     RUNNER_ASSERT_MSG(labelPtr.get() == nullptr, "TRANSMUTE label on " << fpath << " is set");
144
145     return 0;
146 }
147
148 int nftw_remove_labels(const char *fpath, const struct stat* /*sb*/,
149                        int /*typeflag*/, struct FTW* /*ftwbuf*/)
150 {
151     smack_lsetlabel(fpath, nullptr, SMACK_LABEL_ACCESS);
152     smack_lsetlabel(fpath, nullptr, SMACK_LABEL_EXEC);
153     smack_lsetlabel(fpath, nullptr, SMACK_LABEL_TRANSMUTE);
154
155     return 0;
156 }
157
158 void check_app_permissions(const std::string &app_id, const std::string &pkg_id,
159                            const std::string &user, const privileges_t &allowed_privs,
160                            const privileges_t &denied_privs, bool isHybrid)
161 {
162     (void) pkg_id;
163     std::string smackLabel = generateProcessLabel(app_id, pkg_id, isHybrid);
164
165     CynaraTestClient::Client ctc;
166
167     for (auto &priv : allowed_privs) {
168         ctc.check(smackLabel.c_str(), "", user, priv.c_str(), CYNARA_API_ACCESS_ALLOWED);
169     }
170
171     for (auto &priv : denied_privs) {
172         ctc.check(smackLabel.c_str(), "", user, priv.c_str(), CYNARA_API_ACCESS_DENIED);
173     }
174 }
175
176 void sm_app_has_privileges(const AppInstallHelper &app,
177                            const std::vector<std::string> &privileges,
178                            int expectedResult)
179 {
180     for (auto const &privilege : privileges) {
181         int result;
182         Api::appHasPrivilege(app.getAppId(), privilege, app.getUID(), result);
183         RUNNER_ASSERT_MSG(result == expectedResult, "Application " << app.getAppId()
184                           << " has unexpected access to " << privilege << ", is : "
185                           << " should be : " << expectedResult );
186     }
187 }
188
189 static void check_app_smack_accesses(const std::string &appId, const std::string &pkgId,
190                                      bool isHybrid = false)
191 {
192     static const std::vector<AccessRequest> rules[] =
193         {parseSmackRulesFile(CONF_DIR "pkg-rules-template.smack"),
194          parseSmackRulesFile(CONF_DIR "app-rules-template.smack")};
195
196     const std::pair<std::string, std::string> switchAliases[] =
197         {std::make_pair("~PATH_RW~", generatePathRWLabel(pkgId)),
198          std::make_pair("~PATH_RO~", generatePathROLabel(pkgId)),
199          std::make_pair("~PATH_SHARED_RO~", generatePathSharedROLabel(pkgId)),
200          std::make_pair("~PROCESS~", generateProcessLabel(appId, pkgId, isHybrid))};
201
202     for (auto rule : rules[isHybrid]) {
203         if (rule.object == "~PATH_TRUSTED~") {
204             continue;
205         }
206
207         for (const auto &alias : switchAliases) {
208             if (rule.subject == alias.first) {
209                 rule.subject = alias.second;
210             }
211             if (rule.object == alias.first) {
212                 rule.object = alias.second;
213             }
214         }
215
216         if (rule.object == "_") {
217             rule.access = "rx" + rule.access;
218         }
219
220         check_exact_smack_accesses(rule.subject, rule.object, rule.access);
221     }
222 }
223
224 static void check_app(const std::string &appId, const std::string &pkgId,
225                       bool shouldBeInstalled, bool isHybrid)
226 {
227     char *retPkgId;
228     int ret = security_manager_get_app_pkgid(&retPkgId, appId.c_str());
229
230     if (shouldBeInstalled) {
231         RUNNER_ASSERT_MSG(ret == SECURITY_MANAGER_SUCCESS, "The given appId is not installed.");
232
233         if (ret == SECURITY_MANAGER_SUCCESS) {
234             CStringPtr retPkgIdPtr(retPkgId);
235             RUNNER_ASSERT_MSG(strcmp(pkgId.c_str(), retPkgId) == 0,
236                               "The given appId does not belong to the given pkgId.");
237         }
238         check_app_smack_accesses(appId, pkgId, isHybrid);
239     } else {
240         RUNNER_ASSERT_MSG(ret == SECURITY_MANAGER_ERROR_NO_SUCH_OBJECT, "The given appId is installed.");
241     }
242 }
243
244 void check_app_after_install(const std::string &app_id, const std::string &pkg_id, bool isHybrid)
245 {
246     check_app(app_id, pkg_id, true, isHybrid);
247 }
248
249 static void check_app_gids(const std::string &app_id, const std::vector<gid_t> &allowed_gids)
250 {
251     int ret;
252     gid_t main_gid = getgid();
253     std::unordered_set<gid_t> reference_gids(allowed_gids.begin(), allowed_gids.end());
254
255     // Reset supplementary groups
256     ret = setgroups(0, NULL);
257     RUNNER_ASSERT_MSG(ret != -1, "Unable to set supplementary groups");
258
259     Api::setProcessGroups(app_id);
260
261     ret = getgroups(0, nullptr);
262     RUNNER_ASSERT_MSG(ret != -1, "Unable to get supplementary groups");
263
264     std::vector<gid_t> actual_gids(ret);
265     ret = getgroups(ret, actual_gids.data());
266     RUNNER_ASSERT_MSG(ret != -1, "Unable to get supplementary groups");
267
268     for (const auto &gid : actual_gids) {
269         RUNNER_ASSERT_MSG(gid == main_gid || reference_gids.count(gid) > 0,
270             "Application shouldn't get access to group " << gid);
271         reference_gids.erase(gid);
272     }
273
274     RUNNER_ASSERT_MSG(reference_gids.empty(), "Application didn't get access to some groups");
275 }
276
277 static const char *const ANY_USER_REPRESENTATION = "anyuser";/*this may be actually any string*/
278
279 void check_app_after_install(const std::string &app_id, const std::string &pkg_id,
280                              const privileges_t &allowed_privs,
281                              const privileges_t &denied_privs,
282                              bool isHybrid)
283 {
284     check_app(app_id, pkg_id, true, isHybrid);
285     /* Privileges should be granted to all users if root installs app */
286     check_app_permissions(app_id, pkg_id, ANY_USER_REPRESENTATION, allowed_privs, denied_privs, isHybrid);
287
288     PolicyConfiguration policy;
289     const PolicyConfiguration::GroupVector allowed_groups = policy.privToGroup(allowed_privs);
290     RUNNER_ASSERT_MSG(allowed_groups.size() == allowed_privs.size(),
291                       "Some privileges given were not found in the policy");
292
293     std::vector<gid_t> allowed_gids;
294     for (const auto &groupName : allowed_groups) {
295         errno = 0;
296         struct group* grp = getgrnam(groupName.c_str());
297         RUNNER_ASSERT_ERRNO_MSG(grp, "Group: " << groupName << " not found");
298         allowed_gids.push_back(grp->gr_gid);
299     }
300
301     check_app_gids(app_id, allowed_gids);
302 }
303
304 void check_path(const std::string &path, const std::string &label, bool transmute, bool execute) {
305     nftw_expected_label = label;
306     nftw_expected_transmute = transmute;
307     nftw_expected_exec = execute;
308
309     // check labels
310     int result = nftw(path.c_str(), &nftw_check_sm_labels, FTW_MAX_FDS, FTW_PHYS);
311     RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << path);
312 }
313
314 void check_app_after_uninstall(const std::string &app_id, const std::string &pkg_id,
315                                bool isHybrid)
316 {
317     check_app(app_id, pkg_id, false, isHybrid);
318 }
319
320 void check_app_after_uninstall(const std::string &app_id, const std::string &pkg_id,
321                                const privileges_t &privileges, bool isHybrid)
322 {
323     check_app(app_id, pkg_id, false, isHybrid);
324
325     /* Privileges should not be granted anymore to any user */
326     check_app_permissions(app_id, pkg_id, ANY_USER_REPRESENTATION, {}, privileges, isHybrid);
327 }
328
329 std::string access_opposite(std::string &access) {
330     static const std::map<char, int> access_mapping = {{'r', 0}, {'w', 1}, {'x', 2}, {'a', 3},
331                                                        {'t', 4}, {'l', 5}};
332     // May write implies may lock
333     if (access.find('w') != std::string::npos && access.find('l') == std::string::npos) {
334         access.append("l");
335     }
336     std::string access_opposite = "rwxatl";
337     for (char c : access) {
338         access_opposite[access_mapping.at(c)] = '-';
339     }
340     auto it = std::remove_if(access_opposite.begin(), access_opposite.end(), [](char c) {return c == '-';});
341     access_opposite.erase(it, access_opposite.end());
342     return access_opposite;
343 }
344
345 void check_exact_smack_accesses(const std::string &subject, const std::string &object,
346                                 const std::string &access) {
347     std::string access_str(access);
348     auto no_access = access_opposite(access_str);
349     for (char c : access_str) {
350         int ret = smack_have_access(subject.c_str(), object.c_str(), std::string(1, c).c_str());
351         RUNNER_ASSERT_MSG(ret >= 0, "smack_have_access failed: <" << subject << ">, <" << object
352                           << ">, <" << c << "> errno=" << strerror(errno));
353         RUNNER_ASSERT_MSG(ret == 1, "Access " << c << " from " << subject << " to "
354                           << object << " not given");
355     }
356
357     for (char c : no_access) {
358         int ret = smack_have_access(subject.c_str(), object.c_str(), std::string(1, c).c_str());
359         RUNNER_ASSERT_MSG(ret >= 0, "smack_have_access failed: <" << subject << ">, <" << object
360                           << ">, <" << c << "> errno=" << strerror(errno));
361         RUNNER_ASSERT_MSG(ret == 0, "Access " << c << " from " << subject << " to "
362                           << object << " unnecessarily given");
363     }
364 }
365
366 CapsSetsUniquePtr setCaps(const char *cap_string)
367 {
368     CapsSetsUniquePtr caps(cap_init());
369     caps.reset(cap_from_text(cap_string));
370     RUNNER_ASSERT_MSG(caps, "can't convert capabilities from text");
371     int result = cap_set_proc(caps.get());
372     RUNNER_ASSERT_MSG(result == 0, "can't set capabilities. Result: " << result);
373     return caps;
374 }
375
376 static int getOppositeAccessType(int accessType) {
377     return accessType ^ (R_OK | W_OK | X_OK);
378 }
379
380 static const std::unordered_map<int, const char* const> accessTypeToString {
381     std::make_pair(0, "F_OK"),
382     std::make_pair(1, "X_OK"),
383     std::make_pair(2, "W_OK"),
384     std::make_pair(3, "W_OK|X_OK"),
385     std::make_pair(4, "R_OK"),
386     std::make_pair(5, "R_OK|X_OK"),
387     std::make_pair(6, "R_OK|W_OK"),
388     std::make_pair(7, "R_OK|W_OK|X_OK")
389 };
390
391 void accessCheck(const std::string &id, const std::string &path, int accessType,
392                  int expected)
393 {
394     RUNNER_ASSERT_MSG(::access(path.c_str(), accessType) == expected,
395                       "access from " << id << " to path " << path
396                        << (expected == 0 ? " not granted" : " unnecessarily granted")
397                        << " (" << accessTypeToString.at(accessType) << ")");
398 }
399
400 void runAccessTest(const std::string &label, uid_t uid, gid_t gid,
401                    const std::string &testPath, int accessType) {
402     auto fun = [&](){
403         int oppositeAccessType = getOppositeAccessType(accessType);
404         ScopedProcessLabel spl(label, false);
405         RUNNER_ASSERT_ERRNO_MSG(0 == drop_root_privileges(uid, gid),
406                                 "drop_root_privileges failed.");
407
408         if (accessType != 0)
409             accessCheck(label, testPath, accessType, 0);
410         if (oppositeAccessType != 0) {
411             std::vector<int> singleAccessTypes = {R_OK, W_OK, X_OK};
412             for (auto singleAccessType : singleAccessTypes)
413                 if (oppositeAccessType & singleAccessType)
414                     accessCheck(label, testPath, singleAccessType, -1);
415         }
416     };
417
418     runInChildParentWait(fun);
419 }
420
421 void runAccessTest(const AppInstallHelper &app, const std::string &testPath, int accessType) {
422     auto fun = [&](){
423         int oppositeAccessType = getOppositeAccessType(accessType);
424         Api::setProcessLabel(app.getAppId());
425         RUNNER_ASSERT_ERRNO_MSG(0 == drop_root_privileges(app.getUID(), app.getGID()),
426                                 "drop_root_privileges failed.");
427         if (accessType != 0)
428             accessCheck(app.getAppId(), testPath, accessType, 0);
429         if (oppositeAccessType != 0) {
430             std::vector<int> singleAccessTypes = {R_OK, W_OK, X_OK};
431             for (auto singleAccessType : singleAccessTypes)
432                 if (oppositeAccessType & singleAccessType)
433                     accessCheck(app.getAppId(), testPath, singleAccessType, -1);
434         }
435     };
436
437     runInChildParentWait(fun);
438 }
439
440 static const std::vector<std::string> SM_SYSTEM_LABELS = {"System", "System::Privileged", "User"};
441
442 void runSystemAccessTest(uid_t uid, gid_t gid, const std::string &testPath, int accessType) {
443     for (const auto &label : SM_SYSTEM_LABELS)
444         runAccessTest(label, uid, gid, testPath, accessType);
445 }
446
447 bool isAskuserDisabled() {
448     static bool isAskuserDisabled = false;
449     static bool isChecked = false;
450
451     if (isChecked)
452         return isAskuserDisabled;
453
454     std::string sysShare = TzPlatformConfig::getPath(TZ_SYS_SHARE);
455     std::string askDisableFile = sysShare + "/askuser_disable";
456
457     isAskuserDisabled = (access(askDisableFile.c_str(), F_OK) != -1);
458     isChecked = true;
459     return isAskuserDisabled;
460 }
461
462 bool isPrivilegePrivacy(const std::string &priv) {
463     return (1 == privilege_info_is_privacy(priv.c_str()));
464 }
465
466 int countPrivacyPrivileges(const PrivilegeVector &privs) {
467     return std::count_if(privs.begin(), privs.end(), isPrivilegePrivacy);
468 }
469
470 int countPrivacyPrivileges(const std::vector<std::string> &privs) {
471     return std::count_if(privs.begin(), privs.end(), isPrivilegePrivacy);
472 }
473