Set write/setgid bit on data, shared/data dir
[platform/core/appfw/app-installers.git] / src / common / shared_dirs.cc
1 // Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
2 // Use of this source code is governed by an apache-2.0 license that can be
3 // found in the LICENSE file.
4
5 #include "common/shared_dirs.h"
6
7 #include <manifest_parser/utils/logging.h>
8 #include <manifest_parser/utils/version_number.h>
9
10 #include <boost/filesystem/operations.hpp>
11 #include <boost/filesystem/path.hpp>
12 #include <boost/program_options.hpp>
13 #include <boost/system/error_code.hpp>
14
15 #include <glib.h>
16 #include <gio/gio.h>
17 #include <vcore/Certificate.h>
18 #include <pkgmgr-info.h>
19 #include <sys/types.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <tzplatform_config.h>
23 #include <sys/xattr.h>
24
25
26 #include <algorithm>
27 #include <cassert>
28 #include <cstring>
29 #include <cstdio>
30 #include <exception>
31 #include <iterator>
32 #include <regex>
33 #include <string>
34 #include <utility>
35 #include <vector>
36 #include <tuple>
37
38 #include "common/paths.h"
39 #include "common/security_registration.h"
40 #include "common/pkgmgr_query.h"
41 #include "common/utils/base64.h"
42 #include "common/utils/file_util.h"
43 #include "common/utils/user_util.h"
44 #include "common/utils/glist_range.h"
45
46 namespace bf = boost::filesystem;
47 namespace bpo = boost::program_options;
48 namespace bs = boost::system;
49 namespace ci = common_installer;
50
51 namespace {
52
53 const uid_t kGlobalUserUid = tzplatform_getuid(TZ_SYS_GLOBALAPP_USER);
54 const utils::VersionNumber ver30("3.0");
55
56 const std::vector<const char*> kEntries = {
57   {"/"},
58   {"cache"},
59   {"data"},
60   {"shared"},
61 };
62 const std::vector<const char*> kReadOnlyEntries = {
63   {"bin"},
64   {"lib"},
65   {"res"},
66   {"shared/res"},
67 };
68
69 const char kSharedResDir[] = "shared/res";
70 const char kSharedDataDir[] = "shared/data";
71 const char kSharedTrustedDir[] = "shared/trusted";
72 const char kSkelAppDir[] = "/etc/skel/apps_rw";
73 const char kExternalStoragePrivilege[] =
74     "http://tizen.org/privilege/externalstorage.appdata";
75 const char kSystemShareGroupName[] = "system_share";
76
77 bool SetFileOwner(const bf::path& subpath, uid_t uid, gid_t gid) {
78   bs::error_code error;
79   int fd = open(subpath.c_str(), O_RDONLY);
80   if (fd < 0) {
81     LOG(ERROR) << "Can't open directory : " << subpath;
82     return false;
83   }
84   int ret = fchown(fd, uid, gid);
85   close(fd);
86   if (ret != 0) {
87     LOG(ERROR) << "Failed to change owner of: " << subpath;
88     return false;
89   }
90   return true;
91 }
92
93 bool SetOwnerAndPermissions(const bf::path& subpath, uid_t uid,
94                             gid_t gid, bf::perms perms) {
95   bs::error_code error;
96   bf::permissions(subpath, perms, error);
97   if (error) {
98     LOG(ERROR) << "Failed to set permissions for: " << subpath;
99     return false;
100   }
101
102   if (!SetFileOwner(subpath, uid, gid)) {
103     return false;
104   }
105   return true;
106 }
107
108 bool SetPackageDirectoryOwnerAndPermissions(const bf::path& subpath, uid_t uid,
109                                             gid_t gid) {
110   bs::error_code error;
111   bf::perms perms = bf::owner_read |
112                     bf::owner_write |
113                     bf::group_read;
114   // symlink will be skipped
115   if (bf::is_symlink(symlink_status(subpath)))
116     return true;
117
118   // non directory will be skipped
119   bool result = true;
120   if (bf::is_directory(subpath)) {
121     perms |= bf::owner_exe | bf::group_exe | bf::others_exe;
122     if (subpath.filename() == "data") {
123       perms |= bf::group_write | bf::set_gid_on_exe;
124       boost::optional<gid_t> system_share =
125           ci::GetGidByGroupName(kSystemShareGroupName);
126       if (!system_share)
127         return false;
128       gid = *system_share;
129     }
130     result = SetOwnerAndPermissions(subpath, uid, gid, perms);
131   }
132
133   return result;
134 }
135
136 bool CreateDirectories(const bf::path& app_dir, const std::string& pkgid,
137                        uid_t uid, gid_t gid, const bool set_permissions) {
138   bf::path base_dir = app_dir / pkgid;
139   if (bf::exists(base_dir)) {
140     LOG(DEBUG) << "Directory for user already exist: " << base_dir;
141     return true;
142   }
143
144   bs::error_code error;
145   std::vector<const char*> dirs(kEntries);
146   // always trusted
147   dirs.push_back(kSharedTrustedDir);
148   for (auto& entry : dirs) {
149     bf::path subpath = base_dir / entry;
150     bf::create_directories(subpath, error);
151     if (error) {
152       LOG(ERROR) << "Failed to create directory: " << subpath;
153       return false;
154     }
155
156     if (set_permissions) {
157       if (!SetPackageDirectoryOwnerAndPermissions(subpath, uid, gid))
158         return false;
159
160       // for content
161       for (bf::recursive_directory_iterator iter(subpath);
162            iter != bf::recursive_directory_iterator(); ++iter) {
163         if (!SetPackageDirectoryOwnerAndPermissions(iter->path(), uid, gid))
164           return false;
165       }
166     }
167   }
168
169   return true;
170 }
171
172 bf::path GetDirectoryPathForStorage(uid_t user, std::string apps_prefix) {
173   std::string username = ci::GetUsernameByUid(user);
174   if (username.empty())
175     return {};
176
177   bf::path apps_rw;
178   apps_rw = bf::path(apps_prefix.c_str()) / username / "apps_rw";
179   return apps_rw;
180 }
181
182 bool CreateUserDirectories(uid_t user, const std::string& pkgid,
183     const std::string& apps_prefix, const bool set_permissions) {
184   boost::optional<gid_t> gid = ci::GetGidByUid(user);
185   if (!gid)
186     return false;
187
188   std::string group_name = ci::GetGroupNameByGid(*gid);
189   if (group_name != tzplatform_getenv(TZ_SYS_USER_GROUP))
190     return false;
191
192   LOG(DEBUG) << "Creating directories for uid: " << user << ", gid: "
193              << *gid;
194
195   bf::path apps_rw = GetDirectoryPathForStorage(user, apps_prefix);
196   if (apps_rw.empty()) {
197     LOG(DEBUG) << "Directory not exists: " << apps_rw;
198     return false;
199   }
200
201   if (!CreateDirectories(apps_rw, pkgid,
202      user, *gid, set_permissions)) {
203     return false;
204   }
205   return true;
206 }
207
208 bool DeleteDirectories(const bf::path& app_dir, const std::string& pkgid) {
209   bf::path base_dir = app_dir / pkgid;
210   bs::error_code error;
211   bf::remove_all(base_dir, error);
212   if (error) {
213     LOG(ERROR) << "Failed to delete directory: " << base_dir;
214     return false;
215   }
216   return true;
217 }
218
219
220 bool CreateSymlinkFiles(const bf::path& src_dir, const bf::path& dst_dir) {
221   std::vector<char*> rofiles;
222   for (auto& entry : kReadOnlyEntries)
223     rofiles.push_back(strdup(entry));
224
225   for (bf::directory_iterator file(src_dir);
226       file != bf::directory_iterator();
227       ++file) {
228     if (bf::is_regular_file(file->path())) {
229       bf::path current(file->path());
230       bf::path file_name = current.filename();
231       LOG(DEBUG) << "file_name: " << file_name;
232       rofiles.push_back(strdup(file_name.c_str()));
233     }
234   }
235
236   bs::error_code error;
237   for (auto& entry : rofiles) {
238     bf::path src_path = src_dir / entry;
239     bf::path dst_path = dst_dir / entry;
240     free(const_cast<char*>(entry));
241     if (!bf::exists(src_path)) {
242       // check if symlink for .mmc/bin,lib,res, then do not skip
243       if (!bf::is_symlink(symlink_status(src_path))) {
244         LOG(ERROR) << "src_path not exist : " << src_path;
245         continue;
246       }
247     }
248     if (bf::exists(dst_path)) {
249       LOG(WARNING) << "dst_path exist, skip : " << dst_path;
250       continue;
251     }
252     bf::create_symlink(src_path, dst_path, error);
253     if (error) {
254       LOG(ERROR) << "Symlink creation failure src_path: " << src_path
255                  << " dst_path: " << dst_path;
256       LOG(ERROR) << "error: " << error.message();
257       return false;
258     }
259   }
260   return true;
261 }
262
263 bool DeleteSymlinkFiles(const bf::path& src_dir, const bf::path& dst_dir) {
264   bs::error_code error;
265   for (bf::directory_iterator file(dst_dir);
266       file != bf::directory_iterator();
267       ++file) {
268     bf::path current(file->path());
269     if (bf::is_symlink(symlink_status(current))) {
270       bf::path resolved_path = bf::read_symlink(current, error);
271       if (error) {
272         LOG(ERROR) << "Getting resolved path of symlink: " << current;
273         LOG(ERROR) << "resolved_path: " << resolved_path;
274         LOG(ERROR) << "error: " << error.message();
275         return false;
276       }
277       LOG(DEBUG) << "resolved_path: " << resolved_path;
278       bf::path parent = resolved_path.parent_path();
279       if (!parent.empty() && (parent == src_dir)) {
280         bf::remove(current, error);
281         LOG(DEBUG) << "removed: " << current;
282         if (error) {
283           LOG(ERROR) << "Symlink deletion failure for: " << current;
284           LOG(ERROR) << "error: " << error.message();
285           return false;
286         }
287       } else {
288         LOG(WARNING) << "Parent is empty or not equal to src, parenet: ("
289                    << parent << ")";
290       }
291     }
292   }
293   bf::path shared_res = dst_dir / kSharedResDir;
294   if (bf::is_symlink(symlink_status(shared_res))) {
295       bf::remove(shared_res, error);
296       if (error) {
297           LOG(ERROR) << "Symlink deletion failure for: " << shared_res;
298           LOG(ERROR) << "error: " << error.message();
299           return false;
300       }
301   }
302   return true;
303 }
304
305 }  // namespace
306
307 namespace common_installer {
308
309 std::string GetDirectoryPathForInternalStorage() {
310   const char* internal_storage_prefix = tzplatform_getenv(TZ_SYS_HOME);
311   if (internal_storage_prefix)
312     return std::string(internal_storage_prefix);
313   return tzplatform_getenv(TZ_SYS_HOME);
314 }
315
316 std::string GetDirectoryPathForExternalStorage() {
317   return GetExternalCardPath().string();
318 }
319
320 bool PerformExternalDirectoryCreationForUser(uid_t user,
321                                              const std::string& pkgid) {
322   bf::path storage_path = GetExternalCardPath();
323
324   const bool set_permissions = false;
325   if (!bf::exists(storage_path)) {
326     LOG(WARNING) << "External storage (SD Card) is not mounted.";
327     return false;
328   }
329
330   bf::path storage_apps_path = storage_path / "apps";
331   if (!bf::exists(storage_apps_path)) {
332     bs::error_code error;
333     bf::create_directories(storage_apps_path, error);
334     if (error) {
335       LOG(ERROR) << "Failed to create directory: "
336           << storage_apps_path.c_str();
337       return false;
338     }
339   }
340
341   if (CreateUserDirectories(user, pkgid,
342                             storage_apps_path.c_str(), set_permissions)) {
343   }
344   return true;
345 }
346
347 bool PerformExternalDirectoryDeletionForUser(uid_t user,
348                                              const std::string& pkgid) {
349   bf::path storage_path = GetExternalCardPath();
350   if (!bf::exists(storage_path)) {
351     LOG(WARNING) << "External storage (SD Card) is not mounted.";
352     return false;
353   }
354
355   bf::path storage_apps_path = bf::path(storage_path) / "apps";
356   return DeleteDirectories(
357       GetDirectoryPathForStorage(user, storage_apps_path.string()), pkgid);
358 }
359
360 bool PerformExternalDirectoryCreationForAllUsers(const std::string& pkgid) {
361   UserList list = ci::GetUserList();
362   for (auto l : list) {
363     if (!PerformExternalDirectoryCreationForUser(std::get<0>(l),
364                                                  pkgid))
365       LOG(WARNING) << "Could not create external storage directories for user: "
366                    << std::get<0>(l);
367   }
368   return true;
369 }
370
371 int PrivilegeCallback(const pkgmgrinfo_pkginfo_h handle, void* user_data) {
372   uid_t uid = static_cast<uid_t>(reinterpret_cast<uintptr_t>(user_data));
373   char* pkgid = nullptr;
374
375   int ret = pkgmgrinfo_pkginfo_get_pkgid(handle, &pkgid);
376   if (ret != PMINFO_R_OK)
377     return -1;
378   if (!PerformExternalDirectoryCreationForUser(uid, pkgid))
379     return -1;
380
381   return 0;
382 }
383
384 bool PerformExternalDirectoryCreationForAllPkgs() {
385   UserList list = ci::GetUserList();
386   for (auto l  : list) {
387     uid_t uid = std::get<0>(l);
388     pkgmgrinfo_pkginfo_filter_h filter_handle = nullptr;
389     int ret = pkgmgrinfo_pkginfo_filter_create(&filter_handle);
390     if (ret != PMINFO_R_OK)
391       return false;
392     ret = pkgmgrinfo_pkginfo_filter_add_string(filter_handle,
393         PMINFO_PKGINFO_PROP_PACKAGE_PRIVILEGE, kExternalStoragePrivilege);
394     if (ret != PMINFO_R_OK) {
395       pkgmgrinfo_pkginfo_filter_destroy(filter_handle);
396       return false;
397     }
398
399     ret = pkgmgrinfo_pkginfo_filter_foreach_pkginfo(filter_handle,
400         PrivilegeCallback,
401         reinterpret_cast<void*>(static_cast<uintptr_t>(uid)));
402     if (ret != PMINFO_R_OK) {
403       LOG(DEBUG) << "Failed to create external directoy";
404       pkgmgrinfo_pkginfo_filter_destroy(filter_handle);
405       return false;
406     }
407     pkgmgrinfo_pkginfo_filter_destroy(filter_handle);
408   }
409
410   return true;
411 }
412
413 bool PerformExternalDirectoryDeletionForAllUsers(const std::string& pkgid) {
414   UserList list = ci::GetUserList();
415   for (auto l : list) {
416     uid_t uid = std::get<0>(l);
417     LOG(DEBUG) << "Deleting directories for user: " << uid;
418     if (QueryIsPackageInstalled(pkgid, uid)) {
419       LOG(DEBUG) << "Package: " << pkgid << " for uid: " << uid
420                  << " still exists. Skipping";
421       continue;
422     }
423
424     if (!PerformExternalDirectoryDeletionForUser(uid, pkgid))
425       LOG(WARNING) << "Could not delete external storage directories for user: "
426                    << uid;
427   }
428   return true;
429 }
430
431 bool CreateSkelDirectories(const std::string& pkgid,
432                            const std::string& api_version,
433                            bool trusted, bool shareddata, bool is_readonly) {
434   bf::path path = bf::path(kSkelAppDir) / pkgid;
435   LOG(DEBUG) << "Creating directories in: " << path;
436
437   utils::VersionNumber api_ver(api_version);
438
439   bs::error_code error;
440   bf::create_directories(path, error);
441   if (error) {
442     LOG(ERROR) << "Failed to create directory: " << path;
443     return false;
444   }
445
446   std::vector<const char*> dirs(kEntries);
447   if (trusted)
448     dirs.push_back(kSharedTrustedDir);
449   if (api_ver < ver30 || shareddata) {
450     dirs.push_back(kSharedDataDir);
451   }
452   for (auto& entry : dirs) {
453     bf::path subpath = path / entry;
454     bf::create_directories(subpath, error);
455     if (error && !bf::exists(subpath)) {
456       LOG(ERROR) << "Failed to create directory: " << subpath;
457       return false;
458     }
459   }
460
461   std::string error_message;
462   if (!RegisterSecurityContextForPath(pkgid, path, kGlobalUserUid,
463                                       is_readonly, &error_message)) {
464     LOG(ERROR) << "Failed to register security context for path: " << path
465                << ", error_message: " << error_message;
466     return false;
467   }
468
469   bool result = true;
470   if (!is_readonly) {
471     bf::path src_dir = bf::path(tzplatform_getenv(TZ_SYS_RW_APP)) / pkgid;
472     result = CreateSymlinkFiles(src_dir, path);
473   }
474
475   return result;
476 }
477
478
479 bool DeleteSkelDirectories(const std::string& pkgid) {
480   return DeleteDirectories(bf::path(kSkelAppDir), pkgid);
481 }
482
483
484 bool DeleteUserDirectories(const std::string& pkgid) {
485   UserList list = ci::GetUserList();
486   for (auto l : list) {
487     if (ci::QueryIsPackageInstalled(pkgid, std::get<0>(l))) {
488       LOG(INFO) << pkgid << " is installed for user " << std::get<0>(l);
489       continue;
490     }
491
492     LOG(DEBUG) << "Deleting directories of " << pkgid
493                << ", for uid: " << std::get<0>(l);
494     bf::path apps_rw(std::get<2>(l) / "apps_rw");
495     if (!DeleteDirectories(apps_rw, pkgid)) {
496       return false;
497     }
498   }
499   return true;
500 }
501
502 bool DeleteUserExternalDirectories(const std::string& pkgid) {
503   UserList list = ci::GetUserList();
504   for (auto l : list) {
505     if (ci::QueryIsPackageInstalled(pkgid, std::get<0>(l))) {
506       LOG(INFO) << pkgid << " is installed for user " << std::get<0>(l);
507       continue;
508     }
509
510     LOG(DEBUG) << "Deleting external directories of " << pkgid
511                << ", for uid: " << std::get<0>(l);
512     bf::path apps_rw(std::get<2>(l) / "apps_rw");
513     if (!DeleteDirectories(apps_rw, pkgid)) {
514       return false;
515     }
516   }
517   return true;
518 }
519
520
521 bool CopyUserDirectories(const std::string& pkgid) {
522   UserList list = ci::GetUserList();
523   for (auto l : list) {
524     uid_t uid = std::get<0>(l);
525     LOG(DEBUG) << "Copying directories for uid: " << uid;
526     bf::path apps_rw(std::get<2>(l) / "apps_rw");
527     bf::path src = bf::path(kSkelAppDir) / pkgid;
528     bf::path dst = apps_rw / pkgid;
529     if (!ci::CopyDir(src, dst, FSFlag::FS_NONE, true))
530       continue;
531     gid_t gid = std::get<1>(l);
532     if (!SetPackageDirectoryOwnerAndPermissions(dst, uid, gid))
533       return false;
534     for (bf::recursive_directory_iterator iter(dst);
535         iter != bf::recursive_directory_iterator(); ++iter) {
536       if (!SetPackageDirectoryOwnerAndPermissions(iter->path(),
537           uid, gid))
538         return false;
539     }
540     std::string error_message;
541     if (!RegisterSecurityContextForPath(pkgid, dst, std::get<0>(l),
542         false, &error_message)) {
543       LOG(ERROR) << "Failed to register security context for path: " << dst
544                  << ", error_message: " << error_message;
545       return false;
546     }
547   }
548   return true;
549 }
550
551 bool CreateGlobalAppSymlinksForAllUsers(const std::string& pkgid) {
552   bf::path src_dir = bf::path(tzplatform_getenv(TZ_SYS_RW_APP)) / pkgid;
553   if (!bf::exists(src_dir)) {
554     LOG(ERROR) << "src_dir not exists";
555     return false;
556   }
557
558   bool result = true;
559   UserList list = ci::GetUserList();
560   for (auto l : list) {
561     uid_t uid = std::get<0>(l);
562     LOG(DEBUG) << "Creating symlinks for uid: " << uid;
563     // check installed user private app.
564     if (QueryIsPackageInstalled(pkgid, uid))
565       continue;
566     bf::path apps_rw(std::get<2>(l) / "apps_rw");
567     bf::path dst_dir = apps_rw / pkgid;
568     if (!bf::exists(dst_dir)) {
569       LOG(WARNING) << "dst_dir not exists";
570       continue;
571     }
572     result = CreateSymlinkFiles(src_dir, dst_dir);
573   }
574   return result;
575 }
576
577 bool CreateGlobalAppSymlinksForUser(const std::string& pkgid, uid_t uid) {
578   bf::path src_dir = bf::path(tzplatform_getenv(TZ_SYS_RW_APP)) / pkgid;
579   if (!bf::exists(src_dir)) {
580     LOG(ERROR) << "src_dir not exists";
581     return false;
582   }
583
584   tzplatform_set_user(uid);
585   bf::path dst_dir = bf::path(tzplatform_getenv(TZ_USER_APP)) / pkgid;
586   tzplatform_reset_user();
587   if (!bf::exists(dst_dir)) {
588     LOG(WARNING) << "dst_dir not exists";
589     return true;
590   }
591   bool result = CreateSymlinkFiles(src_dir, dst_dir);
592
593   return result;
594 }
595
596 bool DeleteGlobalAppSymlinksForAllUsers(const std::string& pkgid) {
597   bf::path src_dir = bf::path(tzplatform_getenv(TZ_SYS_RW_APP)) / pkgid;
598   if (!bf::exists(src_dir)) {
599     LOG(ERROR) << "src_dir not exists";
600     return false;
601   }
602
603   bool result = true;
604   UserList list = ci::GetUserList();
605   for (auto l : list) {
606     uid_t uid = std::get<0>(l);
607     LOG(DEBUG) << "Deleting symlinks for uid: " << uid;
608     // check installed user private app.
609     if (QueryIsPackageInstalled(pkgid, uid))
610       continue;
611     bf::path apps_rw(std::get<2>(l) / "apps_rw");
612     bf::path dst_dir = apps_rw / pkgid;
613     if (!bf::exists(dst_dir)) {
614       LOG(WARNING) << "dst_dir not exists";
615       continue;
616     }
617     result = DeleteSymlinkFiles(src_dir, dst_dir);
618   }
619   return result;
620 }
621
622 bool DeleteGlobalAppSymlinksForUser(const std::string& pkgid, uid_t uid) {
623   bf::path src_dir = bf::path(tzplatform_getenv(TZ_SYS_RW_APP)) / pkgid;
624   if (!bf::exists(src_dir)) {
625     LOG(ERROR) << "src_dir not exists";
626     return false;
627   }
628
629   tzplatform_set_user(uid);
630   bf::path dst_dir = bf::path(tzplatform_getenv(TZ_USER_APP)) / pkgid;
631   tzplatform_reset_user();
632   if (!bf::exists(dst_dir)) {
633     LOG(WARNING) << "dst_dir not exists";
634     return true;
635   }
636   bool result = DeleteSymlinkFiles(src_dir, dst_dir);
637   return result;
638 }
639
640 }  // namespace common_installer