diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk index 2171f4db8..6dec7f689 100644 --- a/cmds/installd/Android.mk +++ b/cmds/installd/Android.mk @@ -12,7 +12,10 @@ LOCAL_MODULE := libinstalld LOCAL_MODULE_TAGS := eng tests LOCAL_SRC_FILES := $(common_src_files) LOCAL_CFLAGS := $(common_cflags) -LOCAL_SHARED_LIBRARIES := libbase +LOCAL_SHARED_LIBRARIES := \ + libbase \ + liblogwrap \ + LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk LOCAL_CLANG := true include $(BUILD_STATIC_LIBRARY) @@ -30,6 +33,7 @@ LOCAL_SHARED_LIBRARIES := \ libbase \ libcutils \ liblog \ + liblogwrap \ libselinux \ LOCAL_STATIC_LIBRARIES := libdiskusage diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp index 32a03f49d..de6fd960f 100644 --- a/cmds/installd/commands.cpp +++ b/cmds/installd/commands.cpp @@ -16,15 +16,18 @@ #include "installd.h" +#include +#include +#include +#include +#include +#include +#include + #include #include #include -#include -#include -#include -#include -#include -#include +#include using android::base::StringPrintf; @@ -38,6 +41,8 @@ dir_rec_t android_media_dir; dir_rec_t android_mnt_expand_dir; dir_rec_array_t android_system_dirs; +static const char* kCpPath = "/system/bin/cp"; + int install(const char *uuid, const char *pkgname, uid_t uid, gid_t gid, const char *seinfo) { if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) { @@ -172,6 +177,80 @@ int make_user_data(const char *uuid, const char *pkgname, uid_t uid, userid_t us return 0; } +int move_user_data(const char *from_uuid, const char *to_uuid, + const char *package_name, appid_t appid, const char* seinfo) { + std::vector users = get_known_users(from_uuid); + + // Copy package private data for all known users + for (auto user : users) { + std::string from(create_package_data_path(from_uuid, package_name, user)); + std::string to(create_package_data_path(to_uuid, package_name, user)); + std::string to_user(create_data_user_path(to_uuid, user)); + + // Data source may not exist for all users; that's okay + if (access(from.c_str(), F_OK) != 0) { + LOG(INFO) << "Missing source " << from; + continue; + } + + std::string user_path(create_data_user_path(to_uuid, user)); + if (fs_prepare_dir(user_path.c_str(), 0771, AID_SYSTEM, AID_SYSTEM) != 0) { + LOG(ERROR) << "Failed to prepare user target " << user_path; + goto fail; + } + + uid_t uid = multiuser_get_uid(user, appid); + if (make_user_data(to_uuid, package_name, uid, user, seinfo) != 0) { + LOG(ERROR) << "Failed to create package target " << to; + goto fail; + } + + char *argv[] = { + (char*) kCpPath, + (char*) "-F", /* delete any existing destination file first (--remove-destination) */ + (char*) "-p", /* preserve timestamps, ownership, and permissions */ + (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */ + (char*) "-P", /* Do not follow symlinks [default] */ + (char*) "-d", /* don't dereference symlinks */ + (char*) from.c_str(), + (char*) to_user.c_str() + }; + + LOG(DEBUG) << "Copying " << from << " to " << to; + int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true); + + if (rc != 0) { + LOG(ERROR) << "Failed copying " << from << " to " << to + << ": status " << rc; + goto fail; + } + + if (restorecon_data(to_uuid, package_name, seinfo, uid) != 0) { + LOG(ERROR) << "Failed to restorecon " << to; + goto fail; + } + } + + // Copy successful, so delete old data + for (auto user : users) { + std::string from(create_package_data_path(from_uuid, package_name, user)); + if (delete_dir_contents(from.c_str(), 1, NULL) != 0) { + LOG(WARNING) << "Failed to delete " << from; + } + } + return 0; + +fail: + // Nuke everything we might have already copied + for (auto user : users) { + std::string to(create_package_data_path(to_uuid, package_name, user)); + if (delete_dir_contents(to.c_str(), 1, NULL) != 0) { + LOG(WARNING) << "Failed to rollback " << to; + } + } + return -1; +} + int make_user_config(userid_t userid) { if (ensure_config_user_dirs(userid) == -1) { @@ -1592,7 +1671,7 @@ int restorecon_data(const char* uuid, const char* pkgName, continue; } - std::string pkgdir(StringPrintf("%s/%s/%s", userdir.c_str(), user, pkgName)); + std::string pkgdir(StringPrintf("%s%s/%s", userdir.c_str(), user, pkgName)); if (stat(pkgdir.c_str(), &s) < 0) { continue; } diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp index 0f035d00b..3a861812f 100644 --- a/cmds/installd/installd.cpp +++ b/cmds/installd/installd.cpp @@ -14,14 +14,15 @@ ** limitations under the License. */ +#include "installd.h" + +#include + #include #include #include #include -#include "installd.h" - - #define BUFFER_MAX 1024 /* input buffer for commands */ #define TOKEN_MAX 16 /* max number of arguments in buffer */ #define REPLY_MAX 256 /* largest reply allowed */ @@ -123,6 +124,12 @@ static int do_rm_user_data(char **arg, char reply[REPLY_MAX] __unused) return delete_user_data(parse_null(arg[0]), arg[1], atoi(arg[2])); /* uuid, pkgname, userid */ } +static int do_mv_user_data(char **arg, char reply[REPLY_MAX] __unused) +{ + // from_uuid, to_uuid, pkgname, appid, seinfo + return move_user_data(parse_null(arg[0]), parse_null(arg[1]), arg[2], atoi(arg[3]), arg[4]); +} + static int do_mk_user_data(char **arg, char reply[REPLY_MAX] __unused) { return make_user_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), arg[4]); @@ -193,6 +200,7 @@ struct cmdinfo cmds[] = { { "rmcodecache", 3, do_rm_code_cache }, { "getsize", 8, do_get_size }, { "rmuserdata", 3, do_rm_user_data }, + { "mvuserdata", 5, do_mv_user_data }, { "movefiles", 0, do_movefiles }, { "linklib", 4, do_linklib }, { "mkuserdata", 5, do_mk_user_data }, @@ -621,46 +629,6 @@ fail: return res; } -static void drop_privileges() { - if (prctl(PR_SET_KEEPCAPS, 1) < 0) { - ALOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno)); - exit(1); - } - - if (setgid(AID_INSTALL) < 0) { - ALOGE("setgid() can't drop privileges; exiting.\n"); - exit(1); - } - - if (setuid(AID_INSTALL) < 0) { - ALOGE("setuid() can't drop privileges; exiting.\n"); - exit(1); - } - - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata[2]; - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - capheader.version = _LINUX_CAPABILITY_VERSION_3; - capheader.pid = 0; - - capdata[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE); - capdata[CAP_TO_INDEX(CAP_CHOWN)].permitted |= CAP_TO_MASK(CAP_CHOWN); - capdata[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); - capdata[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); - capdata[CAP_TO_INDEX(CAP_FOWNER)].permitted |= CAP_TO_MASK(CAP_FOWNER); - - capdata[0].effective = capdata[0].permitted; - capdata[1].effective = capdata[1].permitted; - capdata[0].inheritable = 0; - capdata[1].inheritable = 0; - - if (capset(&capheader, &capdata[0]) < 0) { - ALOGE("capset failed: %s\n", strerror(errno)); - exit(1); - } -} - static int log_callback(int type, const char *fmt, ...) { va_list ap; int priority; @@ -682,13 +650,16 @@ static int log_callback(int type, const char *fmt, ...) { return 0; } -int main(const int argc __unused, const char *argv[] __unused) { +int main(const int argc __unused, char *argv[]) { char buf[BUFFER_MAX]; struct sockaddr addr; socklen_t alen; int lsocket, s; int selinux_enabled = (is_selinux_enabled() > 0); + setenv("ANDROID_LOG_TAGS", "*:v", 1); + android::base::InitLogging(argv); + ALOGI("installd firing up\n"); union selinux_callback cb; @@ -710,8 +681,6 @@ int main(const int argc __unused, const char *argv[] __unused) { exit(1); } - drop_privileges(); - lsocket = android_get_control_socket(SOCKET_PATH); if (lsocket < 0) { ALOGE("Failed to get socket from environment: %s\n", strerror(errno)); diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h index 395d0ea72..f31bf4f2a 100644 --- a/cmds/installd/installd.h +++ b/cmds/installd/installd.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -89,6 +90,8 @@ #define DEXOPT_PATCHOAT_NEEDED 2 #define DEXOPT_SELF_PATCHOAT_NEEDED 3 +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) + /* data structures */ typedef struct { @@ -154,6 +157,8 @@ std::string create_data_user_path(const char* volume_uuid, userid_t userid); std::string create_data_media_path(const char* volume_uuid, userid_t userid); +std::vector get_known_users(const char* volume_uuid); + int create_user_config_path(char path[PKG_PATH_MAX], userid_t userid); int create_move_path(char path[PKG_PATH_MAX], @@ -214,7 +219,10 @@ int uninstall(const char *uuid, const char *pkgname, userid_t userid); int renamepkg(const char *oldpkgname, const char *newpkgname); int fix_uid(const char *uuid, const char *pkgname, uid_t uid, gid_t gid); int delete_user_data(const char *uuid, const char *pkgname, userid_t userid); -int make_user_data(const char *uuid, const char *pkgname, uid_t uid, userid_t userid, const char* seinfo); +int make_user_data(const char *uuid, const char *pkgname, uid_t uid, + userid_t userid, const char* seinfo); +int move_user_data(const char* from_uuid, const char *to_uuid, + const char *package_name, appid_t appid, const char* seinfo); int make_user_config(userid_t userid); int delete_user(const char *uuid, userid_t userid); int delete_cache(const char *uuid, const char *pkgname, userid_t userid); @@ -238,3 +246,5 @@ int create_oat_dir(const char* oat_dir, const char *instruction_set); int rm_package_dir(const char* apk_path); int calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path, const char *instruction_set); +int move_package_dir(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path, + const char *instruction_set); diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index e10116eb8..ba411cd47 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -99,6 +99,38 @@ std::string create_data_media_path(const char* volume_uuid, userid_t userid) { return StringPrintf("%s/media/%u", create_data_path(volume_uuid).c_str(), userid); } +std::vector get_known_users(const char* volume_uuid) { + std::vector users; + + // We always have an owner + users.push_back(0); + + std::string path(create_data_path(volume_uuid) + "/" + SECONDARY_USER_PREFIX); + DIR* dir = opendir(path.c_str()); + if (dir == NULL) { + // Unable to discover other users, but at least return owner + PLOG(ERROR) << "Failed to opendir " << path; + return users; + } + + struct dirent* ent; + while ((ent = readdir(dir))) { + if (ent->d_type != DT_DIR) { + continue; + } + + char* end; + userid_t user = strtol(ent->d_name, &end, 10); + if (*end == '\0' && user != 0) { + LOG(DEBUG) << "Found valid user " << user; + users.push_back(user); + } + } + closedir(dir); + + return users; +} + /** * Create the path name for config for a certain userid. * Returns 0 on success, and -1 on failure.