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