132d55ede6cf7fef2bea3b55c6444a9146300ca0
[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_set>
27 #include <vector>
28
29 #include <security-manager-types.h>
30 #include <sys/smack.h>
31
32 #include <cynara_test_client.h>
33 #include <dpl/test/test_runner.h>
34 #include <memory.h>
35 #include <sm_api.h>
36 #include <sm_commons.h>
37 #include <sm_db.h>
38 #include <sm_request.h>
39 #include <tests_common.h>
40 #include "tzplatform.h"
41
42 using namespace SecurityManagerTest;
43
44 // Common const values
45
46 const privileges_t SM_ALLOWED_PRIVILEGES = {
47     "http://tizen.org/privilege/display",
48     "http://tizen.org/privilege/nfc"
49 };
50
51 const privileges_t SM_DENIED_PRIVILEGES  = {
52     "http://tizen.org/privilege/bluetooth",
53     "http://tizen.org/privilege/power"
54 };
55
56 const privileges_t SM_NO_PRIVILEGES  = {
57 };
58
59 const std::vector<std::string> SM_ALLOWED_GROUPS = {"db_browser", "db_alarm"};
60
61 const std::string uidToStr(const uid_t uid)
62 {
63     return std::to_string(static_cast<unsigned int>(uid));
64 }
65
66 // Common implementation details
67
68 std::string generateAppLabel(const std::string &appId)
69 {
70     return "User::App::" + appId;
71 }
72
73 std::string generatePkgLabel(const std::string &pkgId)
74 {
75     return "User::Pkg::" + pkgId;
76 }
77
78 static std::string genPath(int app_num, const char *postfix) {
79     char buf[16];
80     sprintf(buf, "%02d", app_num);
81     return std::string("/opt/usr/apps/sm_test_") + std::string(buf) + std::string("_pkg_id_full/") + std::string(postfix);
82 }
83
84 std::string genRWPath(int app_num) {
85     return genPath(app_num, "app_dir");
86 }
87 std::string genROPath(int app_num) {
88     return genPath(app_num, "app_dir_ro");
89 }
90 std::string genPublicROPath(int app_num) {
91     return genPath(app_num, "app_dir_public_ro");
92 }
93
94 std::string genOwnerRWOthersROPath(int app_num) {
95     return genPath(app_num, "app_dir_rw_others_ro");
96 }
97
98 static std::string generatePkgLabelOwnerRWothersRO(const std::string &pkgId)
99 {
100     return "User::Pkg::" + pkgId + "::SharedRO";
101 }
102
103 // Common DB/nftw checks
104
105 // nftw doesn't allow passing user data to functions. Work around by using global variable
106 static std::string nftw_expected_label;
107 bool nftw_expected_transmute;
108 bool nftw_expected_exec;
109
110 static int nftw_check_sm_labels_app_dir(const char *fpath, const struct stat *sb,
111                               const char* correctLabel, bool transmute_test, bool exec_test)
112 {
113     int result;
114     CStringPtr labelPtr;
115     char* label = nullptr;
116
117     /* ACCESS */
118     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_ACCESS);
119     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
120     labelPtr.reset(label);
121     RUNNER_ASSERT_MSG(label != nullptr, "ACCESS label on " << fpath << " is not set");
122     result = strcmp(correctLabel, label);
123     RUNNER_ASSERT_MSG(result == 0, "ACCESS label on " << fpath << " is incorrect"
124             " (should be '" << correctLabel << "' and is '" << label << "')");
125
126
127     /* EXEC */
128     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_EXEC);
129     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
130     labelPtr.reset(label);
131
132     if (S_ISREG(sb->st_mode) && (sb->st_mode & S_IXUSR) && exec_test) {
133         RUNNER_ASSERT_MSG(label != nullptr, "EXEC label on " << fpath << " is not set");
134         result = strcmp(correctLabel, label);
135         RUNNER_ASSERT_MSG(result == 0, "Incorrect EXEC label on executable file " << fpath);
136     } else
137         RUNNER_ASSERT_MSG(label == nullptr, "EXEC label on " << fpath << " is set");
138
139
140     /* TRANSMUTE */
141     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_TRANSMUTE);
142     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
143     labelPtr.reset(label);
144
145     if (S_ISDIR(sb->st_mode) && transmute_test == true) {
146         RUNNER_ASSERT_MSG(label != nullptr, "TRANSMUTE label on " << fpath << " is not set at all");
147         RUNNER_ASSERT_MSG(strcmp(label,"TRUE") == 0,
148                 "TRANSMUTE label on " << fpath << " is not set properly: '"<<label<<"'");
149     } else {
150         RUNNER_ASSERT_MSG(label == nullptr, "TRANSMUTE label on " << fpath << " is set");
151     }
152
153     return 0;
154 }
155
156 static int nftw_check_sm_labels(const char *fpath, const struct stat *sb,
157                                int /*typeflag*/, struct FTW* /*ftwbuf*/)
158 {
159     return nftw_check_sm_labels_app_dir(fpath, sb,
160         nftw_expected_label.c_str(), nftw_expected_transmute, nftw_expected_exec);
161 }
162
163 int nftw_check_labels_non_app_dir(const char *fpath, const struct stat* /*sb*/,
164                                   int /*typeflag*/, struct FTW* /*ftwbuf*/)
165 {
166     int result;
167     CStringPtr labelPtr;
168     char* label = nullptr;
169
170     /* ACCESS */
171     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_ACCESS);
172     labelPtr.reset(label);
173     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
174     result = strcmp("canary_label", labelPtr.get());
175     RUNNER_ASSERT_MSG(result == 0, "ACCESS label on " << fpath << " is overwritten");
176
177     /* EXEC */
178     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_EXEC);
179     labelPtr.reset(label);
180     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
181     result = strcmp("canary_label", labelPtr.get());
182     RUNNER_ASSERT_MSG(result == 0, "EXEC label on " << fpath << " is overwritten");
183
184     /* TRANSMUTE */
185     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_TRANSMUTE);
186     labelPtr.reset(label);
187     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
188     RUNNER_ASSERT_MSG(labelPtr.get() == nullptr, "TRANSMUTE label on " << fpath << " is set");
189
190     return 0;
191 }
192
193 static int nftw_set_labels_non_app_dir(const char *fpath, const struct stat* /*sb*/,
194                                 int /*typeflag*/, struct FTW* /*ftwbuf*/)
195 {
196     smack_lsetlabel(fpath, "canary_label", SMACK_LABEL_ACCESS);
197     smack_lsetlabel(fpath, "canary_label", SMACK_LABEL_EXEC);
198     smack_lsetlabel(fpath, nullptr, SMACK_LABEL_TRANSMUTE);
199
200     return 0;
201 }
202
203 int nftw_remove_labels(const char *fpath, const struct stat* /*sb*/,
204                        int /*typeflag*/, struct FTW* /*ftwbuf*/)
205 {
206     smack_lsetlabel(fpath, nullptr, SMACK_LABEL_ACCESS);
207     smack_lsetlabel(fpath, nullptr, SMACK_LABEL_EXEC);
208     smack_lsetlabel(fpath, nullptr, SMACK_LABEL_TRANSMUTE);
209
210     return 0;
211 }
212
213 static const char *const SM_DENIED_PATH = "/opt/usr/apps/non_app_dir";
214
215 void check_app_permissions(const char *const app_id, const char *const pkg_id,
216                            const char *const user, const privileges_t &allowed_privs,
217                            const privileges_t &denied_privs)
218 {
219     (void) pkg_id;
220     std::string smackLabel = generateAppLabel(app_id);
221
222     CynaraTestClient::Client ctc;
223
224     for (auto &priv : allowed_privs) {
225         ctc.check(smackLabel.c_str(), "", user, priv.c_str(), CYNARA_API_ACCESS_ALLOWED);
226     }
227
228     for (auto &priv : denied_privs) {
229         ctc.check(smackLabel.c_str(), "", user, priv.c_str(), CYNARA_API_ACCESS_DENIED);
230     }
231 }
232
233
234 void check_app_after_install(const char *const app_id, const char *const pkg_id)
235 {
236     TestSecurityManagerDatabase dbtest;
237     dbtest.test_db_after__app_install(app_id, pkg_id);
238 }
239
240 static void check_app_gids(const char *const 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 char *const app_id, const char *const pkg_id,
271                              const privileges_t &allowed_privs,
272                              const privileges_t &denied_privs,
273                              const std::vector<std::string> &allowed_groups)
274 {
275     TestSecurityManagerDatabase dbtest;
276     dbtest.test_db_after__app_install(app_id, pkg_id);
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);
280
281     /* Setup mapping of gids to privileges */
282     /* Do this for each privilege for extra check */
283     for (const auto &privilege : allowed_privs) {
284         dbtest.setup_privilege_groups(privilege, allowed_groups);
285     }
286
287     std::vector<gid_t> allowed_gids;
288
289     for (const auto &groupName : allowed_groups) {
290         errno = 0;
291         struct group* grp = getgrnam(groupName.c_str());
292         RUNNER_ASSERT_ERRNO_MSG(grp, "Group: " << groupName << " not found");
293         allowed_gids.push_back(grp->gr_gid);
294     }
295
296     check_app_gids(app_id, allowed_gids);
297 }
298
299 void check_path(const std::string &path, const std::string &label) {
300     nftw_expected_label = label;
301     nftw_expected_transmute = true;
302     nftw_expected_exec = false;
303
304     // check labels
305     int result = nftw(path.c_str(), &nftw_check_sm_labels, FTW_MAX_FDS, FTW_PHYS);
306     RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << path);
307 }
308
309 void check_app_path_after_install(int app_num, const char *pkgId, bool others_enabled)
310 {
311     std::string SM_RW_PATH = genRWPath(app_num);
312     std::string SM_RO_PATH = genROPath(app_num);
313     std::string SM_PUBLIC_RO_PATH = genPublicROPath(app_num);
314     int result;
315
316     nftw_expected_label = generatePkgLabel(pkgId);
317     nftw_expected_transmute = true;
318     nftw_expected_exec = false;
319
320     result = nftw(SM_RW_PATH.c_str(), &nftw_check_sm_labels, FTW_MAX_FDS, FTW_PHYS);
321     RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << SM_RW_PATH);
322
323     nftw_expected_label = generatePkgLabel(pkgId) + "::RO";
324     nftw_expected_transmute = false;
325     nftw_expected_exec = false;
326
327     result = nftw(SM_RO_PATH.c_str(), &nftw_check_sm_labels, FTW_MAX_FDS, FTW_PHYS);
328     RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << SM_RO_PATH);
329
330     nftw_expected_label = "User::Home";
331     nftw_expected_transmute = true;
332     nftw_expected_exec = false;
333
334     result = nftw(SM_PUBLIC_RO_PATH.c_str(), &nftw_check_sm_labels, FTW_MAX_FDS, FTW_PHYS);
335     RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << SM_PUBLIC_RO_PATH);
336
337     result = nftw(SM_DENIED_PATH, &nftw_check_labels_non_app_dir, FTW_MAX_FDS, FTW_PHYS);
338     RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << SM_DENIED_PATH);
339
340     // owner RW, others RO
341     if(others_enabled) {
342         std::string SM_OWNER_RW_OTHERS_RO_PATH = genOwnerRWOthersROPath(app_num);
343         nftw_expected_label = generatePkgLabelOwnerRWothersRO(pkgId);
344         nftw_expected_transmute = true;
345         nftw_expected_exec = false;
346
347         result = nftw(SM_OWNER_RW_OTHERS_RO_PATH.c_str(), &nftw_check_sm_labels, FTW_MAX_FDS, FTW_PHYS);
348         RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << SM_OWNER_RW_OTHERS_RO_PATH);
349     }
350 }
351
352 void check_app_after_uninstall(const char *const app_id, const char *const pkg_id,
353                                const bool is_pkg_removed)
354 {
355     TestSecurityManagerDatabase dbtest;
356     dbtest.test_db_after__app_uninstall(app_id, pkg_id, is_pkg_removed);
357 }
358
359 void check_app_after_uninstall(const char *const app_id, const char *const pkg_id,
360                                const privileges_t &privileges, const bool is_pkg_removed)
361 {
362     TestSecurityManagerDatabase dbtest;
363     dbtest.test_db_after__app_uninstall(app_id, pkg_id, is_pkg_removed);
364
365
366     /*Privileges should not be granted anymore to any user*/
367     check_app_permissions(app_id, pkg_id, ANY_USER_REPRESENTATION, SM_NO_PRIVILEGES, privileges);
368 }
369
370 std::string access_opposite(std::string &access) {
371     static const std::map<char, int> access_mapping = {{'r', 0}, {'w', 1}, {'x', 2}, {'a', 3},
372                                                        {'t', 4}, {'l', 5}};
373     //May write implies may lock
374     if (access.find('w') != std::string::npos && access.find('l') == std::string::npos) {
375         access.append("l");
376     }
377     std::string access_opposite = "rwxatl";
378     for (char c : access) {
379         access_opposite[access_mapping.at(c)] = '-';
380     }
381     auto it = std::remove_if(access_opposite.begin(), access_opposite.end(), [](char c) {return c == '-';});
382     access_opposite.erase(it, access_opposite.end());
383     return access_opposite;
384 }
385
386 void check_exact_smack_accesses(const std::string &subject, const std::string &object,
387                                 const std::string &access) {
388     std::string access_str(access);
389     auto no_access = access_opposite(access_str);
390     for (char c : access_str) {
391         int ret = smack_have_access(subject.c_str(), object.c_str(), std::string(1, c).c_str());
392         RUNNER_ASSERT_MSG(ret >= 0, "smack_have_access failed: <" << subject << ">, <" << object
393                           << ">, <" << c << "> errno=" << strerror(errno));
394         RUNNER_ASSERT_MSG(ret == 1, "Access " << c << " from " << subject << " to "
395                           << object << " not given");
396     }
397
398     for (char c : no_access) {
399         int ret = smack_have_access(subject.c_str(), object.c_str(), std::string(1, c).c_str());
400         RUNNER_ASSERT_MSG(ret >= 0, "smack_have_access failed: <" << subject << ">, <" << object
401                           << ">, <" << c << "> errno=" << strerror(errno));
402         RUNNER_ASSERT_MSG(ret == 0, "Access " << c << " from " << subject << " to "
403                           << object << " unnecessarily given");
404     }
405 }
406
407 CapsSetsUniquePtr setCaps(const char *cap_string)
408 {
409     CapsSetsUniquePtr caps(cap_init());
410     caps.reset(cap_from_text(cap_string));
411     RUNNER_ASSERT_MSG(caps, "can't convert capabilities from text");
412     int result = cap_set_proc(caps.get());
413     RUNNER_ASSERT_MSG(result == 0, "can't set capabilities. Result: " << result);
414     return caps;
415 }
416
417 static void prepare_app_path(int app_num, bool others_enabled = false)
418 {
419     std::string SM_RW_PATH = genRWPath(app_num);
420     std::string SM_RO_PATH = genROPath(app_num);
421     std::string SM_PUBLIC_RO_PATH = genPublicROPath(app_num);
422     int result;
423
424     result = nftw(SM_RW_PATH.c_str(), &nftw_remove_labels, FTW_MAX_FDS, FTW_PHYS);
425     RUNNER_ASSERT_MSG(result == 0, "Unable to clean Smack labels in " << SM_RW_PATH);
426
427     result = nftw(SM_RO_PATH.c_str(), &nftw_remove_labels, FTW_MAX_FDS, FTW_PHYS);
428     RUNNER_ASSERT_MSG(result == 0, "Unable to clean Smack labels in " << SM_RO_PATH);
429
430     result = nftw(SM_PUBLIC_RO_PATH.c_str(), &nftw_remove_labels, FTW_MAX_FDS, FTW_PHYS);
431     RUNNER_ASSERT_MSG(result == 0, "Unable to clean Smack labels in " << SM_PUBLIC_RO_PATH);
432
433     if(others_enabled) {
434         std::string SM_OWNER_RW_OTHERS_RO_PATH = genOwnerRWOthersROPath(app_num);
435         result = nftw(SM_OWNER_RW_OTHERS_RO_PATH.c_str(), &nftw_remove_labels, FTW_MAX_FDS, FTW_PHYS);
436         RUNNER_ASSERT_MSG(result == 0, "Unable to clean Smack labels in " << SM_OWNER_RW_OTHERS_RO_PATH);
437     }
438
439     result = nftw(SM_DENIED_PATH, &nftw_set_labels_non_app_dir, FTW_MAX_FDS, FTW_PHYS);
440     RUNNER_ASSERT_MSG(result == 0, "Unable to set Smack labels in " << SM_DENIED_PATH);
441 }
442
443 void prepare_app_env(int app_num, bool others_enabled)
444 {
445     prepare_app_path(app_num, others_enabled);
446 }
447
448 void install_app(const char *app_id, const char *pkg_id, uid_t uid, app_install_type type,
449                  bool check_after)
450 {
451     InstallRequest request;
452     request.setAppId(app_id);
453     request.setPkgId(pkg_id);
454     request.setUid(uid);
455     if (type != SM_APP_INSTALL_NONE)
456         request.setInstallType(type);
457     Api::install(request);
458
459     if (check_after)
460         check_app_after_install(app_id, pkg_id);
461 }
462
463 void uninstall_app(const char *app_id, const char *pkg_id, bool expect_pkg_removed,
464                    app_install_type type, bool check_after)
465 {
466     InstallRequest request;
467     request.setAppId(app_id);
468     if (type != SM_APP_INSTALL_NONE)
469         request.setInstallType(type);
470     Api::uninstall(request);
471
472     if (check_after)
473         check_app_after_uninstall(app_id, pkg_id, expect_pkg_removed);
474 }
475
476 static const std::string EXEC_FILE("exec");
477 static const std::string NORMAL_FILE("normal");
478 static const std::string LINK_PREFIX("link_to_");
479
480 static void createTestDir(const std::string &dir)
481 {
482     mode_t dirMode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
483     mode_t execFileMode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
484     mode_t normalFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
485
486     mktreeSafe(dir, dirMode);
487     creatSafe(dir + "/" + EXEC_FILE, execFileMode);
488     creatSafe(dir + "/" + NORMAL_FILE, normalFileMode);
489     symlinkSafe(dir + "/" + EXEC_FILE, dir + "/" + LINK_PREFIX + EXEC_FILE);
490     symlinkSafe(dir + "/" + NORMAL_FILE, dir + "/" + LINK_PREFIX + NORMAL_FILE);
491 }
492
493 static void createInnerAppDir(const std::string &dir, const std::string &nonAppDir)
494 {
495     createTestDir(dir);
496
497     symlinkSafe(nonAppDir, dir + "/" + LINK_PREFIX + "non_app_dir");
498     symlinkSafe(nonAppDir + "/" + EXEC_FILE,
499                 dir + "/" + LINK_PREFIX + "non_app_" + EXEC_FILE);
500     symlinkSafe(nonAppDir + "/" + NORMAL_FILE,
501                 dir + "/" + LINK_PREFIX + "non_app_" + NORMAL_FILE);
502 }
503
504 static const std::string nonAppDirPath(const TemporaryTestUser &user)
505 {
506     return TMP_DIR + "/" + user.getUserName();
507 }
508
509 static void generateAppDir(const TemporaryTestUser &user,
510        const std::string &appId, const std::string &pkgId)
511 {
512     const std::string dir = TzPlatformConfig::appDirPath(user, appId, pkgId);
513     const std::string nonAppDir = nonAppDirPath(user);
514
515     createInnerAppDir(dir, nonAppDir);
516     createInnerAppDir(dir + "/.inner_dir", nonAppDir);
517     createInnerAppDir(dir + "/inner_dir", nonAppDir);
518 }
519
520 static void generateNonAppDir(const TemporaryTestUser &user)
521 {
522     const std::string dir = nonAppDirPath(user);
523
524     createTestDir(dir);
525     createTestDir(dir + "/.inner_dir");
526     createTestDir(dir + "/inner_dir");
527 }
528
529 void createTestDirs(const TemporaryTestUser &user,
530                     const std::string &appId, const std::string &pkgId)
531 {
532     generateAppDir(user, appId, pkgId);
533     generateNonAppDir(user);
534 }
535
536 void removeTestDirs(const TemporaryTestUser &user,
537                     const std::string &appId, const std::string &pkgId)
538 {
539     removeDir(TzPlatformConfig::appDirPath(user, appId, pkgId));
540     removeDir(nonAppDirPath(user));
541 }