Add security_manager_prepare_app() tests
[platform/core/test/security-tests.git] / src / security-manager-tests / test_cases_prepare_app.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 <sys/smack.h>
18 #include <sys/capability.h>
19 #include <sys/prctl.h>
20
21 #include <thread>
22 #include <string>
23 #include <memory>
24 #include <mutex>
25 #include <fstream>
26
27 #include <dpl/test/test_runner_child.h>
28 #include <dpl/test/test_runner.h>
29
30 #include <app_install_helper.h>
31 #include <scoped_installer.h>
32 #include <sm_api.h>
33 #include <sm_commons.h>
34 #include <memory.h>
35 #include <tests_common.h>
36
37 using namespace SecurityManagerTest;
38
39 namespace {
40 bool finish = false;
41 const size_t THREADS = 10;
42
43 const std::string APP_TEST_USER = "app_test_user";
44
45 const std::string EXTERNAL_STORAGE_PRIVILEGE = "http://tizen.org/privilege/externalstorage";
46 const std::string MEDIA_STORAGE_PRIVILEGE = "http://tizen.org/privilege/mediastorage";
47
48 const std::string ACCESS_DENIED_DIR_PATH = "/usr/share/security-manager/dummy";
49 const std::string EXTERNAL_STORAGE_DIR_PATH = "/opt/media";
50 const std::string MEDIA_STORAGE_RW_DIR_PATH = "/opt/usr/media";
51 const std::string MEDIA_STORAGE_RO_DIR_PATH = "/opt/usr/home/app_test_user/media";
52
53 typedef std::unique_ptr<_cap_struct, decltype(&cap_free)> CapPtr;
54
55 std::string thread_errors;
56 std::mutex error_mutex;
57
58 #define THREAD_ASSERT_MSG(test, message)                                       \
59     do                                                                         \
60     {                                                                          \
61         if (!(test))                                                           \
62         {                                                                      \
63             std::ostringstream assertMsg;                                      \
64             assertMsg << #test << " " << __FILE__ << " " << __LINE__ << " " << \
65                          message << std::endl;                                 \
66             std::lock_guard<std::mutex> guard(error_mutex);                    \
67             thread_errors.append(assertMsg.str());                             \
68         }                                                                      \
69     } while (0)
70
71 void threadFn(int i, const std::string &expectedLabel)
72 {
73     if (i % 2 == 0) {
74         // block all signals
75         sigset_t sigset;
76         THREAD_ASSERT_MSG(sigfillset(&sigset) == 0, "sigfillset failed");
77         THREAD_ASSERT_MSG(sigprocmask(SIG_BLOCK, &sigset, NULL) == 0, "sigprocmask failed");
78     }
79
80     while (!finish)
81         usleep(1000);
82
83     char* label;
84     THREAD_ASSERT_MSG(smack_new_label_from_self(&label) > 0, "smack_new_label_from_self failed");
85     CStringPtr labelPtr(label);
86
87     THREAD_ASSERT_MSG(expectedLabel.compare(label) == 0,
88                       "Thread " << i << " has a wrong label: " << label);
89
90     CapPtr expectedCaps(cap_init(), cap_free);
91     THREAD_ASSERT_MSG(expectedCaps, "cap_init() failed");
92     THREAD_ASSERT_MSG(cap_clear(expectedCaps.get()) == 0, "cap_clear() failed");
93
94     CapPtr realCaps(cap_get_proc(), cap_free);
95     THREAD_ASSERT_MSG(realCaps, "cap_get_proc() failed");
96     THREAD_ASSERT_MSG(cap_compare(realCaps.get(), expectedCaps.get()) == 0,
97                       "Thread " << i << " has wrong caps");
98 }
99
100 struct ThreadWrapper
101 {
102
103     ThreadWrapper()
104     {
105     }
106     ~ThreadWrapper()
107     {
108         finish = true;
109         thread.join();
110     }
111
112     void run(int i, const std::string &expectedLabel)
113     {
114         THREAD_ASSERT_MSG(!thread.joinable(), "Thread already started");
115         thread = std::thread(threadFn, i, expectedLabel);
116     }
117
118     std::thread thread;
119 };
120
121 int setLauncherSecurityAttributes(TemporaryTestUser &user)
122 {
123     // Add launcher capabilities (cap_dac_override, cap_setgid, cap_sys_admin, cap_mac_admin),
124     // launcher is user process, we must drop root privileges (cap_setgid, cap_setuid are needed).
125     // By default, the permitted capability set is cleared when credentials change is made
126     // (if a process drops a capability from its permitted set, it can never reacquire that capability),
127     // setting the "keep capabilities" flag prevents it from being cleared.
128     // Effective capability set is always cleared when credential change is made, we need to add them again.
129
130     setCaps("cap_dac_override+ep cap_setgid+ep cap_sys_admin+ep cap_mac_admin+ep cap_setuid+ep");
131     int ret = prctl(PR_SET_KEEPCAPS, 1, 0, 0);
132     if (ret != 0)
133         return ret;
134
135     ret = drop_root_privileges(user.getUid(), user.getGid());
136     if (ret != 0)
137         return ret;
138
139     setCaps("cap_dac_override+ep cap_setgid+ep cap_sys_admin+ep cap_mac_admin+ep");
140     return ret;
141 }
142
143 ino_t getFileInode(const std::string &path)
144 {
145     struct stat st;
146     if (stat(path.c_str(), &st) != 0)
147         return 0;
148
149     return st.st_ino;
150 }
151
152 std::string getTextFileContents(const std::string &path)
153 {
154     std::ifstream in(path.c_str());
155     if (in.fail())
156         return std::string();
157     std::stringstream ss;
158     ss << in.rdbuf();
159     return ss.str();
160 }
161
162 bool isPathBound(const std::string &what, const std::string &where, pid_t pid = 1)
163 {
164     std::string mountinfoPath = std::string("/proc/") + std::to_string(pid) + "/mountinfo";
165     std::string mountinfo = getTextFileContents(mountinfoPath);
166     std::string line = what + " " + where;
167
168     return std::string::npos != mountinfo.find(line);
169 }
170
171 } // anonymous namespace
172
173 RUNNER_TEST_GROUP_INIT(SECURITY_MANAGER_PREPARE_APP)
174
175 RUNNER_CHILD_TEST(security_manager_100_synchronize_credentials_test)
176 {
177     TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
178     tmpUser.create();
179
180     AppInstallHelper app("app100", tmpUser.getUid());
181     ScopedInstaller appInstall(app);
182     const std::string expectedLabel = app.generateAppLabel();
183
184     pid_t pid = fork();
185     RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
186     if (pid == 0) {
187         {
188             RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
189
190             ThreadWrapper threads[THREADS];
191
192             for (size_t i = 0; i < THREADS; i++)
193                 threads[i].run(i, expectedLabel);
194
195             Api::prepareApp(app.getAppId().c_str());
196         }
197         RUNNER_ASSERT_MSG(thread_errors.empty(), std::endl << thread_errors);
198         exit(0);
199     } else {
200         waitPid(pid);
201     }
202 }
203
204 RUNNER_CHILD_TEST(security_manager_101_create_namespace_test)
205 {
206     TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
207     tmpUser.create();
208
209     AppInstallHelper app("app101", tmpUser.getUid());
210     ScopedInstaller appInstall(app);
211
212     SynchronizationPipe synchPipe;
213     pid_t pid = fork();
214     RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
215     if (pid == 0) {
216         synchPipe.claimParentEp();
217         RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
218
219         Api::prepareApp(app.getAppId().c_str());
220         synchPipe.post();
221         synchPipe.wait();
222
223         exit(0);
224     } else {
225         synchPipe.claimChildEp();
226         synchPipe.wait();
227
228         std::string appBindPath = std::string("/var/run/user/") + std::to_string(tmpUser.getUid())
229                                   + "/apps/" + app.getAppId();
230         std::string appProcPath = std::string("/proc/") + std::to_string(pid) + "/ns/mnt";
231         std::string launcherProcPath = std::string("/proc/") + std::to_string(getpid()) + "/ns/mnt";
232
233         ino_t appBindInode = getFileInode(appBindPath);
234         ino_t appProcInode = getFileInode(appProcPath);
235         ino_t launcherProcInode = getFileInode(launcherProcPath);
236
237         RUNNER_ASSERT_ERRNO_MSG(appBindInode != 0, "get inode failed");
238         RUNNER_ASSERT_ERRNO_MSG(appProcInode != 0, "get inode failed");
239         RUNNER_ASSERT_ERRNO_MSG(launcherProcInode != 0, "get inode failed");
240
241         RUNNER_ASSERT_ERRNO_MSG(launcherProcInode != appProcInode, "create mount namespace failed");
242         RUNNER_ASSERT_ERRNO_MSG(appBindInode == appProcInode, "bind namespace failed");
243
244         synchPipe.post();
245         waitPid(pid);
246     }
247 }
248
249 RUNNER_CHILD_TEST(security_manager_102_check_propagation_test)
250 {
251     TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
252     tmpUser.create();
253
254     AppInstallHelper app("app102", tmpUser.getUid());
255     ScopedInstaller appInstall(app);
256
257     SynchronizationPipe synchPipe;
258     pid_t pid = fork();
259     RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
260     if (pid == 0) {
261         synchPipe.claimParentEp();
262         RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
263
264         Api::prepareApp(app.getAppId().c_str());
265         synchPipe.post();
266         synchPipe.wait();
267
268         exit(0);
269     } else {
270         synchPipe.claimChildEp();
271         synchPipe.wait();
272
273         bool result = isPathBound(ACCESS_DENIED_DIR_PATH, EXTERNAL_STORAGE_DIR_PATH, pid);
274         RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
275         result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RW_DIR_PATH, pid);
276         RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
277         result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RO_DIR_PATH, pid);
278         RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
279
280         result = isPathBound(ACCESS_DENIED_DIR_PATH, EXTERNAL_STORAGE_DIR_PATH);
281         RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
282         result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RW_DIR_PATH);
283         RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
284         result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RO_DIR_PATH);
285         RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
286
287         synchPipe.post();
288         waitPid(pid);
289     }
290 }
291
292 RUNNER_CHILD_TEST(security_manager_103_policy_change_test)
293 {
294     TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
295     tmpUser.create();
296
297     AppInstallHelper app("app103", tmpUser.getUid());
298     app.addPrivilege(EXTERNAL_STORAGE_PRIVILEGE);
299     app.addPrivilege(MEDIA_STORAGE_PRIVILEGE);
300     ScopedInstaller appInstall(app);
301
302     SynchronizationPipe synchPipe;
303     pid_t pid = fork();
304     RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
305     if (pid == 0) {
306         synchPipe.claimParentEp();
307         RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
308
309         Api::prepareApp(app.getAppId().c_str());
310         synchPipe.post();
311         synchPipe.wait();
312
313         exit(0);
314     } else {
315         synchPipe.claimChildEp();
316         synchPipe.wait();
317
318         bool result = isPathBound(ACCESS_DENIED_DIR_PATH, EXTERNAL_STORAGE_DIR_PATH, pid);
319         RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
320         result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RW_DIR_PATH, pid);
321         RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
322         result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RO_DIR_PATH, pid);
323         RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
324
325         PolicyRequest policyRequest;
326         PolicyEntry policyEntry(app.getAppId(), std::to_string(tmpUser.getUid()), EXTERNAL_STORAGE_PRIVILEGE);
327         policyEntry.setLevel("Deny");
328         policyRequest.addEntry(policyEntry);
329
330         policyEntry = PolicyEntry(app.getAppId(), std::to_string(tmpUser.getUid()), MEDIA_STORAGE_PRIVILEGE);
331         policyEntry.setLevel("Deny");
332         policyRequest.addEntry(policyEntry);
333         Api::sendPolicy(policyRequest);
334
335         result = isPathBound(ACCESS_DENIED_DIR_PATH, EXTERNAL_STORAGE_DIR_PATH, pid);
336         RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
337         result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RW_DIR_PATH, pid);
338         RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
339         result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RO_DIR_PATH, pid);
340         RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
341
342         policyEntry = PolicyEntry(app.getAppId(),  std::to_string(tmpUser.getUid()), EXTERNAL_STORAGE_PRIVILEGE);
343         policyEntry.setLevel("Allow");
344         policyRequest.addEntry(policyEntry);
345
346         policyEntry = PolicyEntry(app.getAppId(),  std::to_string(tmpUser.getUid()), MEDIA_STORAGE_PRIVILEGE);
347         policyEntry.setLevel("Allow");
348         policyRequest.addEntry(policyEntry);
349         Api::sendPolicy(policyRequest);
350
351         result = isPathBound(ACCESS_DENIED_DIR_PATH, EXTERNAL_STORAGE_DIR_PATH, pid);
352         RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
353         result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RW_DIR_PATH, pid);
354         RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
355         result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RO_DIR_PATH, pid);
356         RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
357
358         synchPipe.post();
359         waitPid(pid);
360     }
361 }