diff --git a/FsCrypt.cpp b/FsCrypt.cpp index 1f7faac..4163f4f 100644 --- a/FsCrypt.cpp +++ b/FsCrypt.cpp @@ -62,8 +62,11 @@ using android::base::StringPrintf; using android::fs_mgr::GetEntryForMountPoint; using android::vold::BuildDataPath; +using android::vold::IsFilesystemSupported; using android::vold::kEmptyAuthentication; using android::vold::KeyBuffer; +using android::vold::SetQuotaInherit; +using android::vold::SetQuotaProjectId; using android::vold::writeStringToFile; using namespace android::fscrypt; @@ -783,6 +786,14 @@ bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_ if (!prepare_dir(vendor_ce_path, 0771, AID_ROOT, AID_ROOT)) return false; } if (!prepare_dir(media_ce_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW)) return false; + // Setup quota project ID and inheritance policy + if (!IsFilesystemSupported("sdcardfs")) { + if (SetQuotaInherit(media_ce_path) != 0) return false; + if (SetQuotaProjectId(media_ce_path, multiuser_get_uid(user_id, AID_MEDIA_RW)) != 0) { + return false; + } + } + if (!prepare_dir(user_ce_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false; if (fscrypt_is_native()) { diff --git a/Utils.cpp b/Utils.cpp index b2a1992..6894c6c 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -33,20 +33,23 @@ #include #include #include +#include +#include #include #include #include -#include #include #include #include #include #include #include +#include #include #include #include +#include #include #ifndef UMOUNT_NOFOLLOW @@ -74,6 +77,11 @@ static const char* kKeyPath = "/data/misc/vold"; static const char* kProcFilesystems = "/proc/filesystems"; +static const char* kAndroidDir = "/Android/"; +static const char* kAppDataDir = "/Android/data/"; +static const char* kAppMediaDir = "/Android/media/"; +static const char* kAppObbDir = "/Android/obb/"; + // Lock used to protect process-level SELinux changes from racing with each // other between multiple threads. static std::mutex kSecurityLock; @@ -116,6 +124,45 @@ status_t DestroyDeviceNode(const std::string& path) { } } +// Sets a default ACL where the owner and group can read/write/execute. +// Other users aren't allowed anything. +int SetDefault770Acl(const std::string& path, uid_t uid, gid_t gid) { + if (IsFilesystemSupported("sdcardfs")) { + // sdcardfs magically takes care of this + return OK; + } + + static constexpr size_t size = + sizeof(posix_acl_xattr_header) + 3 * sizeof(posix_acl_xattr_entry); + auto buf = std::make_unique(size); + + posix_acl_xattr_header* acl_header = reinterpret_cast(buf.get()); + acl_header->a_version = POSIX_ACL_XATTR_VERSION; + + posix_acl_xattr_entry* entry = + reinterpret_cast(buf.get() + sizeof(posix_acl_xattr_header)); + + entry[0].e_tag = ACL_USER_OBJ; + entry[0].e_perm = ACL_READ | ACL_WRITE | ACL_EXECUTE; + entry[0].e_id = uid; + + entry[1].e_tag = ACL_GROUP_OBJ; + entry[1].e_perm = ACL_READ | ACL_WRITE | ACL_EXECUTE; + entry[1].e_id = gid; + + entry[2].e_tag = ACL_OTHER; + entry[2].e_perm = 0; + entry[2].e_id = 0; + + int ret = setxattr(path.c_str(), XATTR_NAME_POSIX_ACL_DEFAULT, acl_header, size, 0); + + if (ret != 0) { + PLOG(ERROR) << "Failed to set default ACL on " << path; + } + + return ret; +} + int SetQuotaInherit(const std::string& path) { unsigned long flags; @@ -161,44 +208,92 @@ int SetQuotaProjectId(const std::string& path, long projectId) { return ioctl(fd, FS_IOC_FSSETXATTR, &fsx); } -int PrepareAppDirsFromRoot(std::string path, std::string root, mode_t mode, uid_t uid, gid_t gid) { - int ret = 0; - bool isCacheDir = false; - if (!StartsWith(path, root)) { - return -1; +int PrepareDirWithProjectId(const std::string& path, mode_t mode, uid_t uid, gid_t gid, + long projectId) { + int ret = fs_prepare_dir(path.c_str(), mode, uid, gid); + + if (ret != 0) { + return ret; } - // Cache directories (eg "/storage/emulated/Android/data/com.foo/cache/") need special treatment - isCacheDir = EndsWith(root, "/Android/data/") && EndsWith(path, "cache/"); - std::string to_create_from_root = path.substr(root.length()); + if (!IsFilesystemSupported("sdcardfs")) { + ret = SetQuotaProjectId(path, projectId); + } - size_t pos = 0; - while ((pos = to_create_from_root.find('/')) != std::string::npos) { - auto component = to_create_from_root.substr(0, pos); - to_create_from_root.erase(0, pos + 1); - root = root + component + "/"; - ret = fs_prepare_dir(root.c_str(), mode, uid, gid); - if (ret) { - break; - } - if (!IsFilesystemSupported("sdcardfs")) { - long projectId; - // All app-specific directories share the same project-ID, except - // the cache directory - if (isCacheDir && component == "cache") { - // Note that this also matches paths like: - // /Android/data/com.foo/bar/cache/ - // This is currently safe because we're never asked to create - // such directories. - projectId = uid - AID_APP_START + AID_CACHE_GID_START; - } else { - projectId = uid - AID_APP_START + AID_EXT_GID_START; - } - ret = SetQuotaProjectId(root, projectId); + return ret; +} + +static gid_t getAppDirGid(const std::string& appDir) { + gid_t gid = AID_MEDIA_RW; + if (!IsFilesystemSupported("sdcardfs")) { + if (appDir == android::vold::kAppDataDir) { + gid = AID_EXT_DATA_RW; + } else if (appDir == android::vold::kAppObbDir) { + gid = AID_EXT_OBB_RW; + } else if (appDir == android::vold::kAppMediaDir) { + gid = AID_MEDIA_RW; + } else { + gid = AID_MEDIA_RW; } } - return ret; + return gid; +} + +int PrepareAppDirFromRoot(std::string path, int appUid) { + int ret = 0; + // Extract various parts of the path to setup correctly + // Sample path: + // /data/media/0/Android/data/com.foo/files + // [1]: path in which to create app-specific dir, eg. /data/media/0/Android/data/ + // [2]: the part of [1] starting from /Android, eg. /Android/data/ + // [3]: the package name part of the path, eg. com.foo + // [4]: the directory to create within [3], eg files + std::regex re("(^/.*(/Android/(?:data|media|obb|sandbox)/))([^/]+)/([^/]+)?/?"); + + std::smatch match; + bool is_match = regex_match(path, match, re); + + if (!is_match) { + LOG(ERROR) << "Invalid application directory: " << path; + return -EINVAL; + } + + uid_t uid = appUid; + gid_t gid = getAppDirGid(match.str(2)); + // mode = 770, plus sticky bit on directory to inherit GID when apps + // create subdirs + mode_t mode = S_IRWXU | S_IRWXG | S_ISGID; + long projectId = uid - AID_APP_START + AID_EXT_GID_START; + + // First, create the package-path + std::string package_path = match.str(1) + match.str(3); + ret = PrepareDirWithProjectId(package_path, mode, uid, gid, projectId); + if (ret) { + return ret; + } + + // Set the default ACL, to ensure that even if applications run with a + // umask of 0077, new directories within these directories will allow the + // GID specified here to write; this is necessary for apps like installers + // and MTP, that require access here. + // + // See man (5) acl for more details. + ret = SetDefault770Acl(package_path, uid, gid); + if (ret) { + return ret; + } + + // Next, create the directory within the package, if needed + if (match.size() <= 4) { + return OK; + } + + if (match.str(4) == "cache") { + // All dirs use the "app" project ID, except for the cache dir + projectId = uid - AID_APP_START + AID_CACHE_GID_START; + } + return PrepareDirWithProjectId(path.c_str(), mode, uid, gid, projectId); } status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid) { diff --git a/Utils.h b/Utils.h index b35250d..a7dda7a 100644 --- a/Utils.h +++ b/Utils.h @@ -36,11 +36,6 @@ namespace vold { static const char* kPropFuse = "persist.sys.fuse"; -static const char* kAndroidDir = "/Android/"; -static const char* kAppDataDir = "/Android/data/"; -static const char* kAppMediaDir = "/Android/media/"; -static const char* kAppObbDir = "/Android/obb/"; - /* SELinux contexts used depending on the block device type */ extern security_context_t sBlkidContext; extern security_context_t sBlkidUntrustedContext; @@ -56,13 +51,13 @@ status_t DestroyDeviceNode(const std::string& path); int SetQuotaInherit(const std::string& path); int SetQuotaProjectId(const std::string& path, long projectId); /* - * Recursively calls fs_prepare_dir() on all components in 'path', starting at 'root'. - * 'path' must start with 'root'. Sets up quota project IDs correctly. + * Creates and sets up an application-specific path on external + * storage with the correct ACL and project ID (if needed). * * ONLY for use with app-specific data directories on external storage! * (eg, /Android/data/com.foo, /Android/obb/com.foo, etc.) */ -int PrepareAppDirsFromRoot(std::string path, std::string root, mode_t mode, uid_t uid, gid_t gid); +int PrepareAppDirFromRoot(std::string path, int appUid); /* fs_prepare_dir wrapper that creates with SELinux context */ status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid); diff --git a/VolumeManager.cpp b/VolumeManager.cpp index 4427c9b..c141d2a 100644 --- a/VolumeManager.cpp +++ b/VolumeManager.cpp @@ -82,7 +82,7 @@ using android::vold::DeleteDirContents; using android::vold::DeleteDirContentsAndDir; using android::vold::IsFilesystemSupported; using android::vold::PrepareAndroidDirs; -using android::vold::PrepareAppDirsFromRoot; +using android::vold::PrepareAppDirFromRoot; using android::vold::PrivateVolume; using android::vold::Symlink; using android::vold::Unlink; @@ -823,29 +823,6 @@ int VolumeManager::unmountAll() { return 0; } -static gid_t getAppDirGid(const std::string& appDir) { - // Create app-specific dirs with the correct UID/GID - gid_t gid = AID_MEDIA_RW; - if (!IsFilesystemSupported("sdcardfs")) { - if (appDir == android::vold::kAppDataDir) { - gid = AID_EXT_DATA_RW; - } else if (appDir == android::vold::kAppObbDir) { - gid = AID_EXT_OBB_RW; - } else if (appDir == android::vold::kAppMediaDir) { - gid = AID_MEDIA_RW; - } else { - gid = AID_MEDIA_RW; - } - } - - return gid; -} - -static bool isValidAppDirRoot(const std::string& appDirRoot) { - return appDirRoot == android::vold::kAppDataDir || appDirRoot == android::vold::kAppMediaDir || - appDirRoot == android::vold::kAppObbDir; -} - int VolumeManager::setupAppDir(const std::string& path, const std::string& appDirRoot, int32_t appUid) { // Only offer to create directories for paths managed by vold @@ -889,19 +866,10 @@ int VolumeManager::setupAppDir(const std::string& path, const std::string& appDi // on /storage/emulated/10 means /mnt/user/0/emulated/10 const std::string lowerPath = volume->getInternalPath() + path.substr(volume->getPath().length()); - const std::string lowerAppDirRoot = - volume->getInternalPath() + appDirRoot.substr(volume->getPath().length()); // Do some sanity checking on the app dir (relative from root) const std::string volumeRoot = volume->getRootPath(); // eg /data/media/0 - // eg, if lowerAppDirRoot = /data/media/0/Android/data, this is /Android/data - const std::string relativeAppRoot = lowerAppDirRoot.substr(volumeRoot.length()); - if (!isValidAppDirRoot(relativeAppRoot)) { - LOG(ERROR) << path << " is not a valid application directory."; - return -EINVAL; - } - // Make sure the Android/ directories exist and are setup correctly int ret = PrepareAndroidDirs(volumeRoot); if (ret != 0) { @@ -909,8 +877,8 @@ int VolumeManager::setupAppDir(const std::string& path, const std::string& appDi return ret; } - gid_t gid = getAppDirGid(relativeAppRoot); - return PrepareAppDirsFromRoot(lowerPath, lowerAppDirRoot, 0770, appUid, gid); + // Finally, create the app paths we need + return PrepareAppDirFromRoot(lowerPath, appUid); } int VolumeManager::createObb(const std::string& sourcePath, const std::string& sourceKey,