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