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