Magisk/native/jni/su/su.cpp

241 lines
5.9 KiB
C++
Raw Normal View History

/*
2017-04-14 21:21:31 +02:00
* Copyright 2017, John Wu (@topjohnwu)
* Copyright 2015, Pierre-Hugues Husson <phh@phh.me>
* Copyright 2010, Adam Shanks (@ChainsDD)
* Copyright 2008, Zinx Verituse (@zinxv)
*/
/* su.c - The main function running in the daemon
*/
#include <stdio.h>
#include <stdlib.h>
2017-04-14 21:21:31 +02:00
#include <string.h>
#include <unistd.h>
#include <getopt.h>
2017-04-14 21:21:31 +02:00
#include <fcntl.h>
#include <pwd.h>
2017-04-14 21:21:31 +02:00
#include <errno.h>
#include <signal.h>
2017-07-07 19:12:47 +02:00
#include <sched.h>
#include <sys/types.h>
2017-04-14 21:21:31 +02:00
#include <sys/stat.h>
2019-02-10 09:57:51 +01:00
#include <magisk.h>
#include <daemon.h>
#include <utils.h>
#include <flags.h>
2017-04-14 21:21:31 +02:00
#include "su.h"
2018-10-04 10:59:51 +02:00
#include "pts.h"
2018-11-20 10:40:42 +01:00
int quit_signals[] = { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 };
2018-10-13 03:46:09 +02:00
2017-04-14 21:21:31 +02:00
static void usage(int status) {
FILE *stream = (status == EXIT_SUCCESS) ? stdout : stderr;
fprintf(stream,
2019-02-12 11:17:02 +01:00
FULL_VER(MagiskSU) "\n\n"
"Usage: su [options] [-] [user [argument...]]\n\n"
2017-04-14 21:21:31 +02:00
"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 preserve the entire environment\n"
2017-04-14 21:21:31 +02:00
" -s, --shell SHELL use SHELL instead of the default " DEFAULT_SHELL "\n"
" -v, --version display version number and exit\n"
" -V display version code and exit\n"
2017-07-07 19:12:47 +02:00
" -mm, -M,\n"
" --mount-master force run in the global mount namespace\n");
2018-10-04 10:59:51 +02:00
exit(status);
}
2017-04-14 21:21:31 +02:00
static char *concat_commands(int argc, char *argv[]) {
char command[ARG_MAX];
command[0] = '\0';
for (int i = optind - 1; i < argc; ++i) {
if (command[0])
2017-04-14 21:21:31 +02:00
sprintf(command, "%s %s", command, argv[i]);
else
strcpy(command, argv[i]);
2017-04-14 21:21:31 +02:00
}
return strdup(command);
}
2018-10-04 10:59:51 +02:00
static void sighandler(int sig) {
restore_stdin();
// Assume we'll only be called before death
// See note before sigaction() in set_stdin_raw()
//
// Now, close all standard I/O to cause the pumps
// to exit so we can continue and retrieve the exit
// code
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
// Put back all the default handlers
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = SIG_DFL;
for (int i = 0; quit_signals[i]; ++i) {
sigaction(quit_signals[i], &act, NULL);
2017-04-14 21:21:31 +02:00
}
}
2018-10-13 03:46:09 +02:00
static void setup_sighandlers(void (*handler)(int)) {
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = handler;
for (int i = 0; quit_signals[i]; ++i) {
sigaction(quit_signals[i], &act, NULL);
}
}
2018-11-04 09:38:06 +01:00
// Default values
su_req_base::su_req_base()
: uid(UID_ROOT), login(false), keepenv(false), mount_master(false) {}
su_request::su_request()
: shell(DEFAULT_SHELL), command("") {}
2018-10-04 10:59:51 +02:00
/*
* Connect daemon, send argc, argv, cwd, pts slave
*/
int su_client_main(int argc, char *argv[]) {
int c;
struct option long_opts[] = {
{ "command", required_argument, NULL, 'c' },
{ "help", no_argument, NULL, 'h' },
{ "login", no_argument, NULL, 'l' },
{ "preserve-environment", no_argument, NULL, 'p' },
{ "shell", required_argument, NULL, 's' },
{ "version", no_argument, NULL, 'v' },
{ "context", required_argument, NULL, 'z' },
{ "mount-master", no_argument, NULL, 'M' },
{ NULL, 0, NULL, 0 },
};
2018-11-04 09:38:06 +01:00
su_request su_req;
2018-10-04 10:59:51 +02:00
for (int i = 0; i < argc; i++) {
// Replace -cn with -z, -mm with -M for supporting getopt_long
if (strcmp(argv[i], "-cn") == 0)
strcpy(argv[i], "-z");
else if (strcmp(argv[i], "-mm") == 0)
strcpy(argv[i], "-M");
}
2017-04-14 21:21:31 +02:00
2017-07-07 19:12:47 +02:00
while ((c = getopt_long(argc, argv, "c:hlmps:Vvuz:M", long_opts, NULL)) != -1) {
2017-04-14 21:21:31 +02:00
switch (c) {
2018-10-04 10:59:51 +02:00
case 'c':
su_req.command = concat_commands(argc, argv);
optind = argc;
break;
case 'h':
usage(EXIT_SUCCESS);
break;
case 'l':
2018-11-04 09:38:06 +01:00
su_req.login = true;
2018-10-04 10:59:51 +02:00
break;
case 'm':
case 'p':
2018-11-04 09:38:06 +01:00
su_req.keepenv = true;
2018-10-04 10:59:51 +02:00
break;
case 's':
su_req.shell = optarg;
break;
case 'V':
printf("%d\n", MAGISK_VER_CODE);
exit(EXIT_SUCCESS);
case 'v':
2019-02-12 11:17:02 +01:00
printf("%s\n", MAGISK_VERSION ":MAGISKSU");
2018-10-04 10:59:51 +02:00
exit(EXIT_SUCCESS);
case 'z':
// Do nothing, placed here for legacy support :)
break;
case 'M':
2018-11-04 09:38:06 +01:00
su_req.mount_master = true;
2018-10-04 10:59:51 +02:00
break;
default:
/* Bionic getopt_long doesn't terminate its error output by newline */
fprintf(stderr, "\n");
usage(2);
2017-04-14 21:21:31 +02:00
}
}
if (optind < argc && strcmp(argv[optind], "-") == 0) {
2018-11-04 09:38:06 +01:00
su_req.login = true;
2017-04-14 21:21:31 +02:00
optind++;
}
/* username or uid */
if (optind < argc) {
2017-04-14 21:21:31 +02:00
struct passwd *pw;
pw = getpwnam(argv[optind]);
if (pw)
2018-10-04 10:59:51 +02:00
su_req.uid = pw->pw_uid;
else
2018-10-04 10:59:51 +02:00
su_req.uid = atoi(argv[optind]);
2017-04-14 21:21:31 +02:00
optind++;
}
2018-10-04 10:59:51 +02:00
char pts_slave[PATH_MAX];
int ptmx, fd;
2017-07-07 19:12:47 +02:00
2018-10-04 10:59:51 +02:00
// Connect to client
fd = connect_daemon();
2017-07-07 19:12:47 +02:00
2018-10-04 10:59:51 +02:00
// Tell the daemon we are su
write_int(fd, SUPERUSER);
2017-04-14 21:21:31 +02:00
2018-10-20 22:12:08 +02:00
// Wait for ack from daemon
if (read_int(fd)) {
// Fast fail
fprintf(stderr, "%s\n", strerror(EACCES));
return DENY;
}
2018-10-04 10:59:51 +02:00
// Send su_request
2018-11-04 09:38:06 +01:00
xwrite(fd, &su_req, sizeof(su_req_base));
2018-10-04 10:59:51 +02:00
write_string(fd, su_req.shell);
write_string(fd, su_req.command);
2017-04-14 21:21:31 +02:00
2018-10-04 10:59:51 +02:00
// Determine which one of our streams are attached to a TTY
int atty = 0;
if (isatty(STDIN_FILENO)) atty |= ATTY_IN;
if (isatty(STDOUT_FILENO)) atty |= ATTY_OUT;
if (isatty(STDERR_FILENO)) atty |= ATTY_ERR;
2017-04-14 21:21:31 +02:00
2018-10-04 10:59:51 +02:00
if (atty) {
// We need a PTY. Get one.
ptmx = pts_open(pts_slave, sizeof(pts_slave));
} else {
pts_slave[0] = '\0';
}
2017-04-14 21:21:31 +02:00
2018-10-04 10:59:51 +02:00
// Send pts_slave
write_string(fd, pts_slave);
// Send stdin
send_fd(fd, (atty & ATTY_IN) ? -1 : STDIN_FILENO);
// Send stdout
send_fd(fd, (atty & ATTY_OUT) ? -1 : STDOUT_FILENO);
// Send stderr
send_fd(fd, (atty & ATTY_ERR) ? -1 : STDERR_FILENO);
2018-11-20 10:40:42 +01:00
if (atty) {
2018-10-04 10:59:51 +02:00
setup_sighandlers(sighandler);
watch_sigwinch_async(STDOUT_FILENO, ptmx);
2018-11-20 10:40:42 +01:00
pump_stdin_async(ptmx);
2018-10-04 10:59:51 +02:00
pump_stdout_blocking(ptmx);
}
// Get the exit code
int code = read_int(fd);
close(fd);
2018-10-04 10:59:51 +02:00
return code;
2018-11-04 09:38:06 +01:00
}