diff --git a/native/jni/core/daemon.cpp b/native/jni/core/daemon.cpp index e9e5f1a46..1c3caad34 100644 --- a/native/jni/core/daemon.cpp +++ b/native/jni/core/daemon.cpp @@ -249,6 +249,19 @@ static int switch_cgroup(const char *cgroup, int pid) { return true; }); + // Use isolated devpts if kernel support + if (access("/dev/pts/ptmx", F_OK) == 0) { + auto pts = MAGISKTMP + "/" SHELLPTS; + xmkdirs(pts.data(), 0755); + xmount("devpts", pts.data(), "devpts", + MS_NOSUID | MS_NOEXEC, "newinstance"); + auto ptmx = pts + "/ptmx"; + if (access(ptmx.data(), F_OK)) { + xumount(pts.data()); + rmdir(pts.data()); + } + } + sockaddr_un sun; socklen_t len = setup_sockaddr(&sun, MAIN_SOCKET); fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); diff --git a/native/jni/include/magisk.hpp b/native/jni/include/magisk.hpp index f954f10ef..55d4b3d74 100644 --- a/native/jni/include/magisk.hpp +++ b/native/jni/include/magisk.hpp @@ -22,6 +22,7 @@ extern std::string MAGISKTMP; #define MODULEMNT INTLROOT "/modules" #define BBPATH INTLROOT "/busybox" #define ROOTOVL INTLROOT "/rootdir" +#define SHELLPTS INTLROOT "/pts" #define ROOTMNT ROOTOVL "/.mount_list" constexpr const char *applet_names[] = { "su", "resetprop", "magiskhide", nullptr }; diff --git a/native/jni/magiskhide/hide_policy.cpp b/native/jni/magiskhide/hide_policy.cpp index ffd0573a4..124769e50 100644 --- a/native/jni/magiskhide/hide_policy.cpp +++ b/native/jni/magiskhide/hide_policy.cpp @@ -35,6 +35,10 @@ void hide_unmount(int pid) { // Unmount dummy skeletons and MAGISKTMP targets.push_back(MAGISKTMP); + auto magiskpts = MAGISKTMP + "/" SHELLPTS; + if (access(magiskpts.data(), F_OK) == 0) { + targets.push_back(magiskpts); + } parse_mnt("/proc/self/mounts", [&](mntent *mentry) { if (TMPFS_MNT(system) || TMPFS_MNT(vendor) || TMPFS_MNT(product) || TMPFS_MNT(system_ext)) targets.emplace_back(mentry->mnt_dir); diff --git a/native/jni/su/pts.cpp b/native/jni/su/pts.cpp index fecde743d..ccd58d09d 100644 --- a/native/jni/su/pts.cpp +++ b/native/jni/su/pts.cpp @@ -105,6 +105,14 @@ error: return -1; } +int get_pty_num(int fd) { + int pty_num = -1; + if (ioctl(fd, TIOCGPTN, &pty_num) != 0) { + return -1; + } + return pty_num; +} + // Stores the previous termios of stdin static struct termios old_stdin; static int stdin_is_raw = 0; diff --git a/native/jni/su/pts.hpp b/native/jni/su/pts.hpp index 2eb721415..7257149af 100644 --- a/native/jni/su/pts.hpp +++ b/native/jni/su/pts.hpp @@ -29,10 +29,13 @@ */ int pts_open(char *slave_name, size_t slave_name_size); + +int get_pty_num(int fd); + /** * set_stdin_raw * - * Changes stdin to raw unbuffered mode, disables echo, + * Changes stdin to raw unbuffered mode, disables echo, * auto carriage return, etc. * * Return Value @@ -60,7 +63,7 @@ int restore_stdin(void); * watch_sigwinch_async * * After calling this function, if the application receives - * SIGWINCH, the terminal window size will be read from + * SIGWINCH, the terminal window size will be read from * "input" and set on "output". * * NOTE: This function blocks SIGWINCH and spawns a thread. @@ -72,7 +75,7 @@ int restore_stdin(void); * * Return Value * on failure, -1 and errno will be set. In this case, no - * thread has been spawned and SIGWINCH will not be + * thread has been spawned and SIGWINCH will not be * blocked. * on success, 0 */ diff --git a/native/jni/su/su.cpp b/native/jni/su/su.cpp index 22a6c3729..5b9f6156a 100644 --- a/native/jni/su/su.cpp +++ b/native/jni/su/su.cpp @@ -156,7 +156,6 @@ int su_client_main(int argc, char *argv[]) { optind++; } - char pts_slave[PATH_MAX]; int ptmx, fd; // Connect to client @@ -183,16 +182,6 @@ int su_client_main(int argc, char *argv[]) { if (isatty(STDOUT_FILENO)) atty |= ATTY_OUT; if (isatty(STDERR_FILENO)) atty |= ATTY_ERR; - if (atty) { - // We need a PTY. Get one. - ptmx = pts_open(pts_slave, sizeof(pts_slave)); - } else { - pts_slave[0] = '\0'; - } - - // Send pts_slave - write_string(fd, pts_slave); - // Send stdin send_fd(fd, (atty & ATTY_IN) ? -1 : STDIN_FILENO); // Send stdout @@ -200,6 +189,14 @@ int su_client_main(int argc, char *argv[]) { // Send stderr send_fd(fd, (atty & ATTY_ERR) ? -1 : STDERR_FILENO); + if (atty) { + // We need a PTY. Get one. + write_int(fd, 1); + ptmx = recv_fd(fd); + } else { + write_int(fd, 0); + } + if (atty) { setup_sighandlers(sighandler); watch_sigwinch_async(STDOUT_FILENO, ptmx); diff --git a/native/jni/su/su_daemon.cpp b/native/jni/su/su_daemon.cpp index 71aa72e67..2a1fce3cc 100644 --- a/native/jni/su/su_daemon.cpp +++ b/native/jni/su/su_daemon.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -222,23 +223,32 @@ void su_daemon_handler(int client, struct ucred *credential) { // Become session leader xsetsid(); - // Get pts_slave - string pts_slave = read_string(client); - // The FDs for each of the streams - int infd = recv_fd(client); + int infd = recv_fd(client); int outfd = recv_fd(client); int errfd = recv_fd(client); - if (!pts_slave.empty()) { - LOGD("su: pts_slave=[%s]\n", pts_slave.data()); - // Check pts_slave file is owned by daemon_from_uid - struct stat st; - xstat(pts_slave.data(), &st); + // App need a PTY + if (read_int(client)) { + string pts; + string ptmx; + auto magiskpts = MAGISKTMP + "/" SHELLPTS; + if (access(magiskpts.data(), F_OK)) { + pts = "/dev/pts"; + ptmx = "/dev/ptmx"; + } else { + pts = magiskpts; + ptmx = magiskpts + "/ptmx"; + } + int ptmx_fd = xopen(ptmx.data(), O_RDWR); + int unlock = 0; + ioctl(ptmx_fd, TIOCSPTLCK, &unlock); + send_fd(client, ptmx_fd); + int pty_num = get_pty_num(ptmx_fd); + close(ptmx_fd); - // If caller is not root, ensure the owner of pts_slave is the caller - if(st.st_uid != ctx.info->uid && ctx.info->uid != 0) - LOGE("su: Wrong permission of pts_slave\n"); + string pts_slave = pts + "/" + to_string(pty_num); + LOGD("su: pts_slave=[%s]\n", pts_slave.data()); // Opening the TTY has to occur after the // fork() and setsid() so that it becomes @@ -258,11 +268,6 @@ void su_daemon_handler(int client, struct ucred *credential) { xdup2(outfd, STDOUT_FILENO); xdup2(errfd, STDERR_FILENO); - // Unleash all streams from SELinux hell - setfilecon("/proc/self/fd/0", "u:object_r:" SEPOL_FILE_TYPE ":s0"); - setfilecon("/proc/self/fd/1", "u:object_r:" SEPOL_FILE_TYPE ":s0"); - setfilecon("/proc/self/fd/2", "u:object_r:" SEPOL_FILE_TYPE ":s0"); - close(infd); close(outfd); close(errfd); @@ -331,5 +336,4 @@ void su_daemon_handler(int client, struct ucred *credential) { execvp(ctx.req.shell.data(), (char **) argv); fprintf(stderr, "Cannot execute %s: %s\n", ctx.req.shell.data(), strerror(errno)); PLOGE("exec"); - exit(EXIT_FAILURE); } diff --git a/native/jni/utils/logging.cpp b/native/jni/utils/logging.cpp index 02033438c..717ae95cf 100644 --- a/native/jni/utils/logging.cpp +++ b/native/jni/utils/logging.cpp @@ -54,4 +54,4 @@ void LOGD(const char *fmt, ...) {} #endif void LOGI(const char *fmt, ...) { LOG_BODY(i) } void LOGW(const char *fmt, ...) { LOG_BODY(w) } -void LOGE(const char *fmt, ...) { LOG_BODY(e); log_cb.ex(1); } +void LOGE(const char *fmt, ...) { LOG_BODY(e); log_cb.ex(EXIT_FAILURE); }