int API minijail_change_user(struct minijail *j, const char *user) { char *buf = NULL; struct passwd pw; struct passwd *ppw = NULL; ssize_t sz = sysconf(_SC_GETPW_R_SIZE_MAX); if (sz == -1) sz = 65536; /* your guess is as good as mine... */ /* * sysconf(_SC_GETPW_R_SIZE_MAX), under glibc, is documented to return * the maximum needed size of the buffer, so we don't have to search. */ buf = malloc(sz); if (!buf) return -ENOMEM; getpwnam_r(user, &pw, buf, sz, &ppw); /* * We're safe to free the buffer here. The strings inside pw point * inside buf, but we don't use any of them; this leaves the pointers * dangling but it's safe. ppw points at pw if getpwnam_r succeeded. */ free(buf); /* getpwnam_r(3) does *not* set errno when |ppw| is NULL. */ if (!ppw) return -1; minijail_change_uid(j, ppw->pw_uid); j->user = strdup(user); if (!j->user) return -ENOMEM; j->usergid = ppw->pw_gid; return 0; }
static void drop_privs(uid_t uid, gid_t gid) { ScopedMinijail j(minijail_new()); minijail_set_supplementary_gids(j.get(), arraysize(kGroups), kGroups); minijail_change_gid(j.get(), gid); minijail_change_uid(j.get(), uid); /* minijail_enter() will abort if priv-dropping fails. */ minijail_enter(j.get()); }
static void drop_privileges(int server_port) { std::unique_ptr<minijail, void (*)(minijail*)> jail(minijail_new(), &minijail_destroy); // Add extra groups: // AID_ADB to access the USB driver // AID_LOG to read system logs (adb logcat) // AID_INPUT to diagnose input issues (getevent) // AID_INET to diagnose network issues (ping) // AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump) // AID_SDCARD_R to allow reading from the SD card // AID_SDCARD_RW to allow writing to the SD card // AID_NET_BW_STATS to read out qtaguid statistics // AID_READPROC for reading /proc entries across UID boundaries gid_t groups[] = {AID_ADB, AID_LOG, AID_INPUT, AID_INET, AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS, AID_READPROC}; if (minijail_set_supplementary_gids( jail.get(), sizeof(groups) / sizeof(groups[0]), groups) != 0) { LOG(FATAL) << "Could not configure supplementary groups"; } // Don't listen on a port (default 5037) if running in secure mode. // Don't run as root if running in secure mode. if (should_drop_privileges()) { drop_capabilities_bounding_set_if_needed(); minijail_change_gid(jail.get(), AID_SHELL); minijail_change_uid(jail.get(), AID_SHELL); // minijail_enter() will abort if any priv-dropping step fails. minijail_enter(jail.get()); D("Local port disabled"); } else { // minijail_enter() will abort if any priv-dropping step fails. minijail_enter(jail.get()); if (root_seclabel != nullptr) { if (selinux_android_setcon(root_seclabel) < 0) { LOG(FATAL) << "Could not set SELinux context"; } } std::string error; std::string local_name = android::base::StringPrintf("tcp:%d", server_port); if (install_listener(local_name, "*smartsocket*", nullptr, 0, &error)) { LOG(FATAL) << "Could not install *smartsocket* listener: " << error; } } }
int main(int argc, char* argv[]) { // Check arguments. if (argc < 2) { error(1, 0, "usage: run-as <package-name> [--user <uid>] <command> [<args>]\n"); } // This program runs with CAP_SETUID and CAP_SETGID capabilities on Android // production devices. Check user id of caller --- must be 'shell' or 'root'. if (getuid() != AID_SHELL && getuid() != AID_ROOT) { error(1, 0, "only 'shell' or 'root' users can run this program"); } // Some devices can disable running run-as, such as Chrome OS when running in // non-developer mode. if (android::base::GetBoolProperty("ro.boot.disable_runas", false)) { error(1, 0, "run-as is disabled from the kernel commandline"); } char* pkgname = argv[1]; int cmd_argv_offset = 2; // Get user_id from command line if provided. int userId = 0; if ((argc >= 4) && !strcmp(argv[2], "--user")) { userId = atoi(argv[3]); if (userId < 0) error(1, 0, "negative user id: %d", userId); cmd_argv_offset += 2; } // Retrieve package information from system, switching egid so we can read the file. gid_t old_egid = getegid(); if (setegid(AID_PACKAGE_INFO) == -1) error(1, errno, "setegid(AID_PACKAGE_INFO) failed"); pkg_info info; memset(&info, 0, sizeof(info)); info.name = pkgname; if (!packagelist_parse(packagelist_parse_callback, &info)) { error(1, errno, "packagelist_parse failed"); } // Handle a multi-user data path if (userId > 0) { free(info.data_dir); if (asprintf(&info.data_dir, "/data/user/%d/%s", userId, pkgname) == -1) { error(1, errno, "asprintf failed"); } } if (info.uid == 0) { error(1, 0, "unknown package: %s", pkgname); } if (setegid(old_egid) == -1) error(1, errno, "couldn't restore egid"); // Verify that user id is not too big. if ((UID_MAX - info.uid) / AID_USER_OFFSET < (uid_t)userId) { error(1, 0, "user id too big: %d", userId); } // Calculate user app ID. uid_t userAppId = (AID_USER_OFFSET * userId) + info.uid; // Reject system packages. if (userAppId < AID_APP) { error(1, 0, "package not an application: %s", pkgname); } // Reject any non-debuggable package. if (!info.debuggable) { error(1, 0, "package not debuggable: %s", pkgname); } // Check that the data directory path is valid. if (!check_data_path(info.data_dir, userAppId)) { error(1, 0, "package has corrupt installation: %s", pkgname); } // Ensure that we change all real/effective/saved IDs at the // same time to avoid nasty surprises. uid_t uid = userAppId; uid_t gid = userAppId; ScopedMinijail j(minijail_new()); minijail_change_uid(j.get(), uid); minijail_change_gid(j.get(), gid); minijail_keep_supplementary_gids(j.get()); minijail_enter(j.get()); std::string seinfo = std::string(info.seinfo) + ":fromRunAs"; if (selinux_android_setcontext(uid, 0, seinfo.c_str(), pkgname) < 0) { error(1, errno, "couldn't set SELinux security context"); } // cd into the data directory, and set $HOME correspondingly. if (TEMP_FAILURE_RETRY(chdir(info.data_dir)) == -1) { error(1, errno, "couldn't chdir to package's data directory"); } setenv("HOME", info.data_dir, 1); // Reset parts of the environment, like su would. setenv("PATH", _PATH_DEFPATH, 1); unsetenv("IFS"); // Set the user-specific parts for this user. passwd* pw = getpwuid(uid); setenv("LOGNAME", pw->pw_name, 1); setenv("SHELL", pw->pw_shell, 1); setenv("USER", pw->pw_name, 1); // User specified command for exec. if ((argc >= cmd_argv_offset + 1) && (execvp(argv[cmd_argv_offset], argv+cmd_argv_offset) == -1)) { error(1, errno, "exec failed for %s", argv[cmd_argv_offset]); } // Default exec shell. execlp(_PATH_BSHELL, "sh", NULL); error(1, errno, "exec failed"); }