Update su to match Linux's implementation

This commit is contained in:
topjohnwu 2017-12-18 13:12:06 +08:00
parent 257308d5db
commit b0c1a6f73a
6 changed files with 61 additions and 104 deletions

View File

@ -74,7 +74,7 @@ void app_send_result(struct su_context *ctx, policy_t policy) {
int notify = setup_user(ctx, user); int notify = setup_user(ctx, user);
char activity[128]; char activity[128];
sprintf(activity, ACTION_RESULT, ctx->pkg_name); sprintf(activity, ACTION_RESULT, ctx->info->pkg_name);
// Send notice to manager, enable logging // Send notice to manager, enable logging
char *result_command[] = { char *result_command[] = {
@ -113,7 +113,7 @@ void app_send_request(struct su_context *ctx) {
int notify = setup_user(ctx, user); int notify = setup_user(ctx, user);
char activity[128]; char activity[128];
sprintf(activity, ACTION_REQUEST, ctx->pkg_name); sprintf(activity, ACTION_REQUEST, ctx->info->pkg_name);
char *request_command[] = { char *request_command[] = {
AM_PATH, "start", "-n", 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 // Send notice to user to tell them root is managed by owner
if (notify) { if (notify) {
sprintf(user, "%d", notify); sprintf(user, "%d", notify);
sprintf(activity, ACTION_RESULT, ctx->pkg_name); sprintf(activity, ACTION_RESULT, ctx->info->pkg_name);
char *notify_command[] = { char *notify_command[] = {
AM_PATH, "broadcast", "-n", AM_PATH, "broadcast", "-n",
activity, activity,

12
db.c
View File

@ -63,7 +63,7 @@ static int strings_callback(void *v, int argc, char **argv, char **azColName) {
for (int i = 0; i < argc; ++i) { for (int i = 0; i < argc; ++i) {
if (strcmp(azColName[i], "key") == 0) { if (strcmp(azColName[i], "key") == 0) {
if (strcmp(argv[i], REQUESTER_ENTRY) == 0) if (strcmp(argv[i], REQUESTER_ENTRY) == 0)
target = ctx->pkg_name; target = ctx->info->pkg_name;
entry = argv[i]; entry = argv[i];
} else if (strcmp(azColName[i], "value") == 0) { } else if (strcmp(azColName[i], "value") == 0) {
value = argv[i]; 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->root_access = ROOT_ACCESS_APPS_AND_ADB;
ctx->info->multiuser_mode = MULTIUSER_MODE_OWNER_ONLY; ctx->info->multiuser_mode = MULTIUSER_MODE_OWNER_ONLY;
ctx->info->mnt_ns = NAMESPACE_MODE_REQUESTER; 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 // Open database
ret = sqlite3_open_v2(DATABASE_PATH, &db, SQLITE_OPEN_READONLY, NULL); ret = sqlite3_open_v2(DATABASE_PATH, &db, SQLITE_OPEN_READONLY, NULL);
@ -136,11 +136,11 @@ void database_check(struct su_context *ctx) {
stat_requester: stat_requester:
// We prefer the original name // We prefer the original name
sprintf(buffer, "%s/0/" JAVA_PACKAGE_NAME, base); sprintf(buffer, "%s/0/" JAVA_PACKAGE_NAME, base);
if (stat(buffer, &ctx->st) == 0) { if (stat(buffer, &ctx->info->st) == 0) {
strcpy(ctx->pkg_name, JAVA_PACKAGE_NAME); strcpy(ctx->info->pkg_name, JAVA_PACKAGE_NAME);
} else { } else {
sprintf(buffer, "%s/0/%s", base, ctx->pkg_name); sprintf(buffer, "%s/0/%s", base, ctx->info->pkg_name);
if (stat(buffer, &ctx->st) == -1) { if (stat(buffer, &ctx->info->st) == -1) {
LOGE("su: cannot find requester"); LOGE("su: cannot find requester");
ctx->info->policy = DENY; ctx->info->policy = DENY;
ctx->notify = 0; ctx->notify = 0;

93
su.c
View File

@ -19,6 +19,7 @@
#include <errno.h> #include <errno.h>
#include <signal.h> #include <signal.h>
#include <sched.h> #include <sched.h>
#include <libgen.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/auxv.h> #include <sys/auxv.h>
@ -35,15 +36,14 @@ static void usage(int status) {
fprintf(stream, fprintf(stream,
"MagiskSU v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ")\n\n" "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" "Options:\n"
" -c, --command COMMAND pass COMMAND to the invoked shell\n" " -c, --command COMMAND pass COMMAND to the invoked shell\n"
" -h, --help display this help message and exit\n" " -h, --help display this help message and exit\n"
" -, -l, --login pretend the shell to be a login shell\n" " -, -l, --login pretend the shell to be a login shell\n"
" -m, -p,\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" " -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, --version display version number and exit\n"
" -V display version code and exit,\n" " -V display version code and exit,\n"
" this is used almost exclusively by Superuser.apk\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]; char command[ARG_MAX];
command[0] = '\0'; command[0] = '\0';
for (int i = optind - 1; i < argc; ++i) { for (int i = optind - 1; i < argc; ++i) {
if (strlen(command)) if (command[0])
sprintf(command, "%s %s", command, argv[i]); sprintf(command, "%s %s", command, argv[i]);
else else
sprintf(command, "%s", argv[i]); strcpy(command, argv[i]);
} }
return strdup(command); return strdup(command);
} }
@ -102,37 +102,29 @@ void set_identity(unsigned uid) {
} }
static __attribute__ ((noreturn)) void allow() { static __attribute__ ((noreturn)) void allow() {
char *arg0; char* argv[] = { NULL, NULL, NULL, NULL };
umask(su_ctx->umask);
if (su_ctx->notify) if (su_ctx->notify)
app_send_result(su_ctx, ALLOW); 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) { if (su_ctx->to.command) {
su_ctx->to.argv[--su_ctx->to.argc] = su_ctx->to.command; argv[1] = "-c";
su_ctx->to.argv[--su_ctx->to.argc] = "-c"; argv[2] = su_ctx->to.command;
}
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;
} }
// Setup shell
umask(022);
populate_environment(su_ctx); populate_environment(su_ctx);
set_identity(su_ctx->to.uid); set_identity(su_ctx->to.uid);
setexeccon("u:r:su:s0"); setexeccon("u:r:su:s0");
su_ctx->to.argv[--su_ctx->to.argc] = arg0; execvp(su_ctx->to.shell, argv);
execvp(binary, su_ctx->to.argv + su_ctx->to.argc); fprintf(stderr, "Cannot execute %s: %s\n", su_ctx->to.shell, strerror(errno));
fprintf(stderr, "Cannot execute %s: %s\n", binary, strerror(errno));
PLOGE("exec"); PLOGE("exec");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@ -199,15 +191,11 @@ int su_daemon_main(int argc, char **argv) {
"LD_AOUT_PRELOAD", "LD_AOUT_PRELOAD",
// not listed in linker, used due to system() call // not listed in linker, used due to system() call
"IFS", "IFS",
NULL
}; };
if(getauxval(AT_SECURE)) { if (getauxval(AT_SECURE))
const char* const* cp = unsec_vars; for (int i = 0; unsec_vars[i]; ++i)
const char* const* endp = cp + sizeof(unsec_vars)/sizeof(unsec_vars[0]); unsetenv(unsec_vars[i]);
while (cp < endp) {
unsetenv(*cp);
cp++;
}
}
int c, socket_serv_fd, fd; int c, socket_serv_fd, fd;
char result[64]; char result[64];
@ -249,19 +237,6 @@ int su_daemon_main(int argc, char **argv) {
case 'v': case 'v':
printf("%s\n", MAGISKSU_VER_STR); printf("%s\n", MAGISKSU_VER_STR);
exit2(EXIT_SUCCESS); 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': case 'z':
// Do nothing, placed here for legacy support :) // Do nothing, placed here for legacy support :)
break; break;
@ -275,34 +250,18 @@ int su_daemon_main(int argc, char **argv) {
} }
} }
su_ctx->to.argc = argc; if (optind < argc && strcmp(argv[optind], "-") == 0) {
su_ctx->to.argv = argv;
if (optind < argc && !strcmp(argv[optind], "-")) {
su_ctx->to.login = 1; su_ctx->to.login = 1;
optind++; optind++;
} }
/* username or uid */ /* username or uid */
if (optind < argc && strcmp(argv[optind], "--")) { if (optind < argc) {
struct passwd *pw; struct passwd *pw;
pw = getpwnam(argv[optind]); pw = getpwnam(argv[optind]);
if (!pw) { 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 {
su_ctx->to.uid = pw->pw_uid; su_ctx->to.uid = pw->pw_uid;
} else
optind++; su_ctx->to.uid = atoi(argv[optind]);
}
if (optind < argc && !strcmp(argv[optind], "--")) {
optind++; optind++;
} }

11
su.h
View File

@ -59,6 +59,8 @@ struct su_info {
int multiuser_mode; int multiuser_mode;
int root_access; int root_access;
int mnt_ns; int mnt_ns;
char pkg_name[PATH_MAX];
struct stat st;
/* These should be guarded with global list lock */ /* These should be guarded with global list lock */
struct list_head pos; struct list_head pos;
@ -72,20 +74,15 @@ struct su_request {
int keepenv; int keepenv;
char *shell; char *shell;
char *command; char *command;
char **argv;
int argc;
}; };
struct su_context { struct su_context {
struct su_info *info; struct su_info *info;
struct su_request to; struct su_request to;
char pkg_name[PATH_MAX];
char sock_path[PATH_MAX];
pid_t pid; pid_t pid;
int notify; int notify;
mode_t umask; char cwd[PATH_MAX];
char *cwd; char sock_path[PATH_MAX];
struct stat st;
int pipefd[2]; int pipefd[2];
}; };

View File

@ -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); LOGD("su: request from client: %d\n", client);
struct su_info *info = NULL, *node; struct su_info *info = NULL, *node;
@ -102,13 +102,9 @@ void su_daemon_receiver(int client) {
xpthread_create(&su_collector, NULL, collector, NULL); 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 // Search for existing in the active list
list_for_each(node, &active_list, struct su_info, pos) { list_for_each(node, &active_list, struct su_info, pos) {
if (node->uid == credential.uid) { if (node->uid == credential->uid) {
info = node; info = node;
break; break;
} }
@ -118,7 +114,7 @@ void su_daemon_receiver(int client) {
if (info == NULL) { if (info == NULL) {
new_request = 1; new_request = 1;
info = malloc(sizeof(*info)); info = malloc(sizeof(*info));
info->uid = credential.uid; info->uid = credential->uid;
info->policy = QUERY; info->policy = QUERY;
info->ref = 0; info->ref = 0;
info->count = 0; info->count = 0;
@ -142,8 +138,7 @@ void su_daemon_receiver(int client) {
.shell = DEFAULT_SHELL, .shell = DEFAULT_SHELL,
.command = NULL, .command = NULL,
}, },
.pid = credential.pid, .pid = credential->pid,
.umask = 022,
.notify = new_request, .notify = new_request,
}; };
@ -157,11 +152,11 @@ void su_daemon_receiver(int client) {
// Check requester // Check requester
if (info->policy == QUERY) { if (info->policy == QUERY) {
if (ctx.st.st_gid != ctx.st.st_uid) { if (info->st.st_gid != info->st.st_uid) {
LOGE("Bad uid/gid %d/%d for Superuser Requestor", ctx.st.st_uid, ctx.st.st_gid); LOGE("Bad uid/gid %d/%d for Superuser Requestor", info->st.st_gid, info->st.st_uid);
info->policy = DENY; info->policy = DENY;
ctx.notify = 0; 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->policy = ALLOW;
info->root_access = ROOT_ACCESS_APPS_AND_ADB; info->root_access = ROOT_ACCESS_APPS_AND_ADB;
ctx.notify = 0; ctx.notify = 0;
@ -243,6 +238,20 @@ void su_daemon_receiver(int client) {
// Become session leader // Become session leader
xsetsid(); 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 // Let's read some info from the socket
int argc = read_int(client); int argc = read_int(client);
if (argc < 0 || argc > 512) { if (argc < 0 || argc > 512) {
@ -263,10 +272,6 @@ void su_daemon_receiver(int client) {
strcpy(argv[i], "-M"); strcpy(argv[i], "-M");
} }
// Get cwd
ctx.cwd = read_string(client);
LOGD("su: cwd=[%s]\n", ctx.cwd);
// Get pts_slave // Get pts_slave
char *pts_slave = read_string(client); char *pts_slave = read_string(client);
LOGD("su: pts_slave=[%s]\n", pts_slave); 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]); 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 // Determine which one of our streams are attached to a TTY
int atty = 0; int atty = 0;
if (isatty(STDIN_FILENO)) atty |= ATTY_IN; if (isatty(STDIN_FILENO)) atty |= ATTY_IN;

View File

@ -43,7 +43,7 @@ int socket_create_temp(char *path, size_t len) {
// Set attributes so requester can access it // Set attributes so requester can access it
setfilecon(path, "u:object_r:su_file:s0"); 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; return fd;
} }