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; } } }
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()); }
/** @brief Fake main(), spliced in before the real call to main() by * __libc_start_main (see below). * We get serialized commands from our invoking process over an fd specified * by an environment variable (kFdEnvVar). The environment variable is a list * of key=value pairs (see move_commands_to_env); we use them to construct a * jail, then enter it. */ static int fake_main(int argc, char **argv, char **envp) { char *fd_name = getenv(kFdEnvVar); int fd = -1; struct minijail *j; if (geteuid() != getuid() || getegid() != getgid()) /* If we didn't do this check, an attacker could set kFdEnvVar * for any setuid program that uses libminijail to cause it to * get capabilities or a uid it did not expect. */ /* TODO(wad) why would libminijail interact here? */ return MINIJAIL_ERR_PRELOAD; if (!fd_name) return MINIJAIL_ERR_PRELOAD; fd = atoi(fd_name); if (fd < 0) return MINIJAIL_ERR_PRELOAD; // Even if the process cannot use that fd, avoid divulging stuff. unsetenv(kFdEnvVar); j = minijail_new(); if (!j) die("preload: out of memory"); if (minijail_from_fd(fd, j)) die("preload: failed to parse minijail from parent"); close(fd); /* TODO(ellyjones): this trashes existing preloads, so one can't do: * LD_PRELOAD="/tmp/test.so libminijailpreload.so" prog; the * descendants of prog will have no LD_PRELOAD set at all. */ unset_in_env(envp, kLdPreloadEnvVar); /* Strip out flags meant for the parent. */ minijail_preenter(j); minijail_enter(j); minijail_destroy(j); dlclose(libc_handle); return real_main(argc, argv, envp); }
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"); }