Update su to match Linux's implementation
This commit is contained in:
parent
257308d5db
commit
b0c1a6f73a
@ -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
12
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) {
|
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
93
su.c
@ -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
11
su.h
@ -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];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
41
su_daemon.c
41
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);
|
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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user