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);
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,

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) {
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;

93
su.c
View File

@ -19,6 +19,7 @@
#include <errno.h>
#include <signal.h>
#include <sched.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/auxv.h>
@ -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++;
}

11
su.h
View File

@ -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];
};

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);
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;

View File

@ -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;
}