diff --git a/activity.c b/activity.c index 29ce86b87..e65ab136a 100644 --- a/activity.c +++ b/activity.c @@ -74,7 +74,7 @@ void app_send_result(struct su_context *ctx, policy_t policy) { int notify = setup_user(ctx, user); char activity[128]; - sprintf(activity, ACTION_RESULT, ctx->pkg_name); + sprintf(activity, ACTION_RESULT, ctx->info->pkg_name); // Send notice to manager, enable logging char *result_command[] = { @@ -113,7 +113,7 @@ void app_send_request(struct su_context *ctx) { int notify = setup_user(ctx, user); char activity[128]; - sprintf(activity, ACTION_REQUEST, ctx->pkg_name); + sprintf(activity, ACTION_REQUEST, ctx->info->pkg_name); char *request_command[] = { AM_PATH, "start", "-n", @@ -128,7 +128,7 @@ void app_send_request(struct su_context *ctx) { // Send notice to user to tell them root is managed by owner if (notify) { sprintf(user, "%d", notify); - sprintf(activity, ACTION_RESULT, ctx->pkg_name); + sprintf(activity, ACTION_RESULT, ctx->info->pkg_name); char *notify_command[] = { AM_PATH, "broadcast", "-n", activity, diff --git a/db.c b/db.c index 9152265bb..70ee4127a 100644 --- a/db.c +++ b/db.c @@ -63,7 +63,7 @@ static int strings_callback(void *v, int argc, char **argv, char **azColName) { for (int i = 0; i < argc; ++i) { if (strcmp(azColName[i], "key") == 0) { if (strcmp(argv[i], REQUESTER_ENTRY) == 0) - target = ctx->pkg_name; + target = ctx->info->pkg_name; entry = argv[i]; } else if (strcmp(azColName[i], "value") == 0) { value = argv[i]; @@ -84,7 +84,7 @@ void database_check(struct su_context *ctx) { ctx->info->root_access = ROOT_ACCESS_APPS_AND_ADB; ctx->info->multiuser_mode = MULTIUSER_MODE_OWNER_ONLY; ctx->info->mnt_ns = NAMESPACE_MODE_REQUESTER; - strcpy(ctx->pkg_name, "???"); /* bad string so it doesn't exist */ + strcpy(ctx->info->pkg_name, "???"); /* bad string so it doesn't exist */ // Open database ret = sqlite3_open_v2(DATABASE_PATH, &db, SQLITE_OPEN_READONLY, NULL); @@ -136,11 +136,11 @@ void database_check(struct su_context *ctx) { stat_requester: // We prefer the original name sprintf(buffer, "%s/0/" JAVA_PACKAGE_NAME, base); - if (stat(buffer, &ctx->st) == 0) { - strcpy(ctx->pkg_name, JAVA_PACKAGE_NAME); + if (stat(buffer, &ctx->info->st) == 0) { + strcpy(ctx->info->pkg_name, JAVA_PACKAGE_NAME); } else { - sprintf(buffer, "%s/0/%s", base, ctx->pkg_name); - if (stat(buffer, &ctx->st) == -1) { + sprintf(buffer, "%s/0/%s", base, ctx->info->pkg_name); + if (stat(buffer, &ctx->info->st) == -1) { LOGE("su: cannot find requester"); ctx->info->policy = DENY; ctx->notify = 0; diff --git a/su.c b/su.c index a12fdf55a..e9986bbb1 100644 --- a/su.c +++ b/su.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -35,15 +36,14 @@ static void usage(int status) { fprintf(stream, "MagiskSU v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ")\n\n" - "Usage: su [options] [--] [-] [LOGIN] [--] [args...]\n\n" + "Usage: su [options] [-] [user [argument...]]\n\n" "Options:\n" " -c, --command COMMAND pass COMMAND to the invoked shell\n" " -h, --help display this help message and exit\n" " -, -l, --login pretend the shell to be a login shell\n" " -m, -p,\n" - " --preserve-environment do not change environment variables\n" + " --preserve-environment preserve the entire environment\n" " -s, --shell SHELL use SHELL instead of the default " DEFAULT_SHELL "\n" - " -u display the multiuser mode and exit\n" " -v, --version display version number and exit\n" " -V display version code and exit,\n" " this is used almost exclusively by Superuser.apk\n" @@ -57,10 +57,10 @@ static char *concat_commands(int argc, char *argv[]) { char command[ARG_MAX]; command[0] = '\0'; for (int i = optind - 1; i < argc; ++i) { - if (strlen(command)) + if (command[0]) sprintf(command, "%s %s", command, argv[i]); else - sprintf(command, "%s", argv[i]); + strcpy(command, argv[i]); } return strdup(command); } @@ -102,37 +102,29 @@ void set_identity(unsigned uid) { } static __attribute__ ((noreturn)) void allow() { - char *arg0; - - umask(su_ctx->umask); + char* argv[] = { NULL, NULL, NULL, NULL }; if (su_ctx->notify) app_send_result(su_ctx, ALLOW); - char *binary = su_ctx->to.shell; + if (su_ctx->to.login) + argv[0] = "-"; + else + argv[0] = basename(su_ctx->to.shell); + if (su_ctx->to.command) { - su_ctx->to.argv[--su_ctx->to.argc] = su_ctx->to.command; - su_ctx->to.argv[--su_ctx->to.argc] = "-c"; - } - - arg0 = strrchr(binary, '/'); - arg0 = arg0 ? (arg0 + 1) : binary; - if (su_ctx->to.login) { - int s = strlen(arg0) + 2; - char *p = xmalloc(s); - *p = '-'; - strcpy(p + 1, arg0); - arg0 = p; + argv[1] = "-c"; + argv[2] = su_ctx->to.command; } + // Setup shell + umask(022); populate_environment(su_ctx); set_identity(su_ctx->to.uid); - setexeccon("u:r:su:s0"); - su_ctx->to.argv[--su_ctx->to.argc] = arg0; - execvp(binary, su_ctx->to.argv + su_ctx->to.argc); - fprintf(stderr, "Cannot execute %s: %s\n", binary, strerror(errno)); + execvp(su_ctx->to.shell, argv); + fprintf(stderr, "Cannot execute %s: %s\n", su_ctx->to.shell, strerror(errno)); PLOGE("exec"); exit(EXIT_FAILURE); } @@ -199,15 +191,11 @@ int su_daemon_main(int argc, char **argv) { "LD_AOUT_PRELOAD", // not listed in linker, used due to system() call "IFS", + NULL }; - if(getauxval(AT_SECURE)) { - const char* const* cp = unsec_vars; - const char* const* endp = cp + sizeof(unsec_vars)/sizeof(unsec_vars[0]); - while (cp < endp) { - unsetenv(*cp); - cp++; - } - } + if (getauxval(AT_SECURE)) + for (int i = 0; unsec_vars[i]; ++i) + unsetenv(unsec_vars[i]); int c, socket_serv_fd, fd; char result[64]; @@ -249,19 +237,6 @@ int su_daemon_main(int argc, char **argv) { case 'v': printf("%s\n", MAGISKSU_VER_STR); exit2(EXIT_SUCCESS); - case 'u': - switch (su_ctx->info->multiuser_mode) { - case MULTIUSER_MODE_USER: - printf("Owner only: Only owner has root access\n"); - break; - case MULTIUSER_MODE_OWNER_MANAGED: - printf("Owner managed: Only owner can manage root access and receive request prompts\n"); - break; - case MULTIUSER_MODE_OWNER_ONLY: - printf("User independent: Each user has its own separate root rules\n"); - break; - } - exit2(EXIT_SUCCESS); case 'z': // Do nothing, placed here for legacy support :) break; @@ -275,34 +250,18 @@ int su_daemon_main(int argc, char **argv) { } } - su_ctx->to.argc = argc; - su_ctx->to.argv = argv; - - if (optind < argc && !strcmp(argv[optind], "-")) { + if (optind < argc && strcmp(argv[optind], "-") == 0) { su_ctx->to.login = 1; optind++; } /* username or uid */ - if (optind < argc && strcmp(argv[optind], "--")) { + if (optind < argc) { struct passwd *pw; pw = getpwnam(argv[optind]); - if (!pw) { - char *endptr; - - /* It seems we shouldn't do this at all */ - errno = 0; - su_ctx->to.uid = strtoul(argv[optind], &endptr, 10); - if (errno || *endptr) { - LOGE("Unknown id: %s\n", argv[optind]); - fprintf(stderr, "Unknown id: %s\n", argv[optind]); - exit(EXIT_FAILURE); - } - } else { + if (pw) su_ctx->to.uid = pw->pw_uid; - } - optind++; - } - if (optind < argc && !strcmp(argv[optind], "--")) { + else + su_ctx->to.uid = atoi(argv[optind]); optind++; } diff --git a/su.h b/su.h index 29b6d220f..b61386d89 100644 --- a/su.h +++ b/su.h @@ -59,6 +59,8 @@ struct su_info { int multiuser_mode; int root_access; int mnt_ns; + char pkg_name[PATH_MAX]; + struct stat st; /* These should be guarded with global list lock */ struct list_head pos; @@ -72,20 +74,15 @@ struct su_request { int keepenv; char *shell; char *command; - char **argv; - int argc; }; struct su_context { struct su_info *info; struct su_request to; - char pkg_name[PATH_MAX]; - char sock_path[PATH_MAX]; pid_t pid; int notify; - mode_t umask; - char *cwd; - struct stat st; + char cwd[PATH_MAX]; + char sock_path[PATH_MAX]; int pipefd[2]; }; diff --git a/su_daemon.c b/su_daemon.c index be4736eb0..b3adf3e37 100644 --- a/su_daemon.c +++ b/su_daemon.c @@ -88,7 +88,7 @@ static void *collector(void *args) { } } -void su_daemon_receiver(int client) { +void su_daemon_receiver(int client, struct ucred *credential) { LOGD("su: request from client: %d\n", client); struct su_info *info = NULL, *node; @@ -102,13 +102,9 @@ void su_daemon_receiver(int client) { xpthread_create(&su_collector, NULL, collector, NULL); } - // Get client credential - struct ucred credential; - get_client_cred(client, &credential); - // Search for existing in the active list list_for_each(node, &active_list, struct su_info, pos) { - if (node->uid == credential.uid) { + if (node->uid == credential->uid) { info = node; break; } @@ -118,7 +114,7 @@ void su_daemon_receiver(int client) { if (info == NULL) { new_request = 1; info = malloc(sizeof(*info)); - info->uid = credential.uid; + info->uid = credential->uid; info->policy = QUERY; info->ref = 0; info->count = 0; @@ -142,8 +138,7 @@ void su_daemon_receiver(int client) { .shell = DEFAULT_SHELL, .command = NULL, }, - .pid = credential.pid, - .umask = 022, + .pid = credential->pid, .notify = new_request, }; @@ -157,11 +152,11 @@ void su_daemon_receiver(int client) { // Check requester if (info->policy == QUERY) { - if (ctx.st.st_gid != ctx.st.st_uid) { - LOGE("Bad uid/gid %d/%d for Superuser Requestor", ctx.st.st_uid, ctx.st.st_gid); + if (info->st.st_gid != info->st.st_uid) { + LOGE("Bad uid/gid %d/%d for Superuser Requestor", info->st.st_gid, info->st.st_uid); info->policy = DENY; ctx.notify = 0; - } else if ((info->uid % 100000) == (ctx.st.st_uid % 100000)) { + } else if ((info->uid % 100000) == (info->st.st_uid % 100000)) { info->policy = ALLOW; info->root_access = ROOT_ACCESS_APPS_AND_ADB; ctx.notify = 0; @@ -243,6 +238,20 @@ void su_daemon_receiver(int client) { // Become session leader xsetsid(); + // Migrate environment from client + char path[32], buf[4096]; + snprintf(path, sizeof(path), "/proc/%d/cwd", ctx.pid); + xreadlink(path, ctx.cwd, sizeof(ctx.cwd)); + snprintf(path, sizeof(path), "/proc/%d/environ", ctx.pid); + memset(buf, 0, sizeof(buf)); + int fd = open(path, O_RDONLY); + read(fd, buf, sizeof(buf)); + clearenv(); + for (size_t pos = 0; buf[pos];) { + putenv(buf + pos); + pos += strlen(buf + pos) + 1; + } + // Let's read some info from the socket int argc = read_int(client); if (argc < 0 || argc > 512) { @@ -263,10 +272,6 @@ void su_daemon_receiver(int client) { strcpy(argv[i], "-M"); } - // Get cwd - ctx.cwd = read_string(client); - LOGD("su: cwd=[%s]\n", ctx.cwd); - // Get pts_slave char *pts_slave = read_string(client); LOGD("su: pts_slave=[%s]\n", pts_slave); @@ -345,10 +350,6 @@ int su_client_main(int argc, char *argv[]) { write_string(socketfd, argv[i]); } - // CWD - getcwd(buffer, sizeof(buffer)); - write_string(socketfd, buffer); - // Determine which one of our streams are attached to a TTY int atty = 0; if (isatty(STDIN_FILENO)) atty |= ATTY_IN; diff --git a/su_socket.c b/su_socket.c index cc6e1261f..db49bc937 100644 --- a/su_socket.c +++ b/su_socket.c @@ -43,7 +43,7 @@ int socket_create_temp(char *path, size_t len) { // Set attributes so requester can access it setfilecon(path, "u:object_r:su_file:s0"); - chown(path, su_ctx->st.st_uid, su_ctx->st.st_gid); + chown(path, su_ctx->info->st.st_uid, su_ctx->info->st.st_gid); return fd; }