"Fossies" - the Fresh Open Source Software Archive

Member "reptyr-reptyr-0.8.0/platform/linux/linux.c" (29 Sep 2020, 12761 Bytes) of package /linux/privat/reptyr-reptyr-0.8.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "linux.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * Copyright (C) 2011 by Nelson Elhage
    3  *
    4  * Permission is hereby granted, free of charge, to any person obtaining a copy
    5  * of this software and associated documentation files (the "Software"), to deal
    6  * in the Software without restriction, including without limitation the rights
    7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    8  * copies of the Software, and to permit persons to whom the Software is
    9  * furnished to do so, subject to the following conditions:
   10  *
   11  * The above copyright notice and this permission notice shall be included in
   12  * all copies or substantial portions of the Software.
   13  *
   14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   20  * THE SOFTWARE.
   21  */
   22 
   23 #ifdef __linux__
   24 
   25 #include "linux.h"
   26 #include "../platform.h"
   27 #include "../../reptyr.h"
   28 #include "../../ptrace.h"
   29 #include <stdint.h>
   30 
   31 int parse_proc_stat(int statfd, struct proc_stat *out) {
   32     char buf[1024];
   33     int n;
   34     unsigned dev;
   35     lseek(statfd, 0, SEEK_SET);
   36     if (read(statfd, buf, sizeof buf) < 0)
   37         return assert_nonzero(errno);
   38     n = sscanf(buf, "%d (%16[^)]) %c %d %d %d %u",
   39                &out->pid, out->comm,
   40                &out->state, &out->ppid, &out->pgid,
   41                &out->sid, &dev);
   42     if (n == EOF)
   43         return assert_nonzero(errno);
   44     if (n != 7) {
   45         return EINVAL;
   46     }
   47     out->ctty = dev;
   48 
   49     return 0;
   50 }
   51 
   52 int read_proc_stat(pid_t pid, struct proc_stat *out) {
   53     char stat_path[PATH_MAX];
   54     int statfd;
   55     int err;
   56 
   57     snprintf(stat_path, sizeof stat_path, "/proc/%d/stat", pid);
   58     statfd = open(stat_path, O_RDONLY);
   59     if (statfd < 0) {
   60         error("Unable to open %s: %s", stat_path, strerror(errno));
   61         return -statfd;
   62     }
   63     err = parse_proc_stat(statfd, out);
   64 
   65 
   66     close(statfd);
   67     return err;
   68 }
   69 
   70 int read_uid(pid_t pid, uid_t *out) {
   71     char stat_path[PATH_MAX];
   72     char buf[1024];
   73     int statfd;
   74     int err = 0;
   75     int n;
   76     char *p = buf;
   77 
   78     snprintf(stat_path, sizeof stat_path, "/proc/%d/status", pid);
   79     statfd = open(stat_path, O_RDONLY);
   80     if (statfd < 0) {
   81         error("Unable to open %s: %s", stat_path, strerror(errno));
   82         return -statfd;
   83     }
   84 
   85     if ((n = read(statfd, buf, sizeof(buf))) < 0) {
   86         err = assert_nonzero(errno);
   87         goto out;
   88     }
   89     while (p < buf + n) {
   90         if (strncmp(p, "Uid:\t", strlen("Uid:\t")) == 0)
   91             break;
   92         p = memchr(p, '\n', buf+n-p);
   93         if (p == NULL)
   94             break;
   95         p++;
   96         continue;
   97     }
   98     if (p == NULL || p >= buf + n) {
   99         debug("Unable to parse emulator uid: no Uid line found");
  100         *out = -1;
  101         goto out;
  102     }
  103     if(sscanf(p, "Uid:\t%d", out) < 0) {
  104         debug("Unable to parse emulator uid: unparseable Uid line");
  105     }
  106 
  107  out:
  108     close(statfd);
  109     return err;
  110 }
  111 
  112 // Find the PID of the terminal emulator for `target's terminal.
  113 //
  114 // We assume that the terminal emulator is the parent of the session
  115 // leader. This is true in most cases, although in principle you can
  116 // construct situations where it is false. We should fail safe later
  117 // on if this turns out to be wrong, however.
  118 int find_terminal_emulator(struct steal_pty_state *steal) {
  119     debug("session leader of pid %d = %d",
  120           (int)steal->target_stat.pid,
  121           (int)steal->target_stat.sid);
  122     struct proc_stat leader_st;
  123     int err;
  124     if ((err = read_proc_stat(steal->target_stat.sid, &leader_st)))
  125         return err;
  126     debug("found terminal emulator process: %d", (int) leader_st.ppid);
  127     steal->emulator_pid = leader_st.ppid;
  128     return 0;
  129 }
  130 
  131 void check_ptrace_scope(void) {
  132     int fd = open("/proc/sys/kernel/yama/ptrace_scope", O_RDONLY);
  133     if (fd >= 0) {
  134         char buf[256];
  135         int n;
  136         n = read(fd, buf, sizeof buf);
  137         close(fd);
  138         if (n > 0) {
  139             if (!atoi(buf)) {
  140                 return;
  141             }
  142         }
  143     } else if (errno == ENOENT)
  144         return;
  145     fprintf(stderr, "The kernel denied permission while attaching. If your uid matches\n");
  146     fprintf(stderr, "the target's, check the value of /proc/sys/kernel/yama/ptrace_scope.\n");
  147     fprintf(stderr, "For more information, see /etc/sysctl.d/10-ptrace.conf\n");
  148 }
  149 
  150 int check_pgroup(pid_t target) {
  151     pid_t pg;
  152     DIR *dir;
  153     struct dirent *d;
  154     pid_t pid;
  155     char *p;
  156     int err = 0;
  157     struct proc_stat pid_stat;
  158 
  159     debug("Checking for problematic process group members...");
  160 
  161     pg = getpgid(target);
  162     if (pg < 0) {
  163         error("Unable to get pgid for pid %d", (int)target);
  164         return errno;
  165     }
  166 
  167     if ((dir = opendir("/proc/")) == NULL)
  168         return assert_nonzero(errno);
  169 
  170     while ((d = readdir(dir)) != NULL) {
  171         if (d->d_name[0] == '.') continue;
  172         pid = strtol(d->d_name, &p, 10);
  173         if (*p) continue;
  174         if (pid == target) continue;
  175         if (getpgid(pid) == pg) {
  176             /*
  177              * We are actually being somewhat overly-conservative here
  178              * -- if pid is a child of target, and has not yet called
  179              * execve(), reptyr's setpgid() strategy may suffice. That
  180              * is a fairly rare case, and annoying to check for, so
  181              * for now let's just bail out.
  182              */
  183             if ((err = read_proc_stat(pid, &pid_stat))) {
  184                 memcpy(pid_stat.comm, "???", 4);
  185             }
  186             error("Process %d (%.*s) shares %d's process group. Unable to attach.\n"
  187                   "(This most commonly means that %d has sub-processes).",
  188                   (int)pid, TASK_COMM_LENGTH, pid_stat.comm, (int)target, (int)target);
  189             err = EINVAL;
  190             goto out;
  191         }
  192     }
  193 out:
  194     closedir(dir);
  195     return err;
  196 }
  197 
  198 int check_proc_stopped(pid_t pid, int fd) {
  199     struct proc_stat st;
  200 
  201     if (parse_proc_stat(fd, &st))
  202         return 1;
  203 
  204     if (st.state == 'T')
  205         return 1;
  206 
  207     return 0;
  208 }
  209 
  210 int *get_child_tty_fds(struct ptrace_child *child, int statfd, int *count) {
  211     struct proc_stat child_status;
  212     struct stat tty_st, console_st, st;
  213     char buf[PATH_MAX];
  214     struct fd_array fds = {};
  215     DIR *dir;
  216     struct dirent *d;
  217 
  218     debug("Looking up fds for tty in child.");
  219     if ((child->error = parse_proc_stat(statfd, &child_status)))
  220         return NULL;
  221 
  222     debug("Resolved child tty: %x", (unsigned)child_status.ctty);
  223 
  224     if (stat("/dev/tty", &tty_st) < 0) {
  225         child->error = assert_nonzero(errno);
  226         error("Unable to stat /dev/tty");
  227         return NULL;
  228     }
  229 
  230     if (stat("/dev/console", &console_st) < 0) {
  231         error("Unable to stat /dev/console");
  232         console_st = (struct stat){
  233             .st_rdev = -1,
  234         };
  235     }
  236 
  237     snprintf(buf, sizeof buf, "/proc/%d/fd/", child->pid);
  238     if ((dir = opendir(buf)) == NULL)
  239         return NULL;
  240     while ((d = readdir(dir)) != NULL) {
  241         if (d->d_name[0] == '.') continue;
  242         snprintf(buf, sizeof buf, "/proc/%d/fd/%s", child->pid, d->d_name);
  243         if (stat(buf, &st) < 0)
  244             continue;
  245 
  246         if (st.st_rdev == child_status.ctty
  247             || st.st_rdev == tty_st.st_rdev
  248             || st.st_rdev == console_st.st_rdev) {
  249             debug("Found an alias for the tty: %s", d->d_name);
  250             if (fd_array_push(&fds, atoi(d->d_name)) != 0) {
  251                 child->error = assert_nonzero(errno);
  252                 error("Unable to allocate memory for fd array.");
  253                 goto out;
  254             }
  255         }
  256     }
  257  out:
  258     *count = fds.n;
  259     closedir(dir);
  260     return fds.fds;
  261 }
  262 
  263 int get_terminal_state(struct steal_pty_state *steal, pid_t target) {
  264     int err;
  265 
  266     if ((err = read_proc_stat(target, &steal->target_stat)))
  267         return err;
  268 
  269     if (major(steal->target_stat.ctty) != UNIX98_PTY_SLAVE_MAJOR) {
  270         error("Child is not connected to a pseudo-TTY. Unable to steal TTY.");
  271         return EINVAL;
  272     }
  273 
  274     if ((err = find_terminal_emulator(steal)))
  275         return err;
  276 
  277     if ((err = read_uid(steal->emulator_pid, &steal->emulator_uid)))
  278         return err;
  279 
  280     return 0;
  281 }
  282 
  283 // ptmx(4) and Linux Documentation/devices.txt document
  284 // /dev/ptmx has having major 5 and minor 2. I can't find any
  285 // constants in headers after a brief glance that I should be
  286 // using here.
  287 #define PTMX_DEVICE (makedev(5, 2))
  288 
  289 // Find the fd(s) in the terminal emulator process that corresponds to
  290 // the master side of the target's pty. Store the result in
  291 // steal->master_fds.
  292 int find_master_fd(struct steal_pty_state *steal) {
  293     DIR *dir;
  294     struct dirent *d;
  295     struct stat st;
  296     int err;
  297     char buf[PATH_MAX];
  298 
  299     snprintf(buf, sizeof buf, "/proc/%d/fd/", steal->child.pid);
  300     if ((dir = opendir(buf)) == NULL)
  301         return errno;
  302     while ((d = readdir(dir)) != NULL) {
  303         if (d->d_name[0] == '.') continue;
  304         snprintf(buf, sizeof buf, "/proc/%d/fd/%s", steal->child.pid, d->d_name);
  305         if (stat(buf, &st) < 0)
  306             continue;
  307 
  308         debug("Checking fd: %s: st_dev=%x", d->d_name, (int)st.st_rdev);
  309 
  310         if (st.st_rdev != PTMX_DEVICE)
  311             continue;
  312 
  313         debug("found a ptmx fd: %s", d->d_name);
  314         err = do_syscall(&steal->child, ioctl,
  315                          atoi(d->d_name),
  316                          TIOCGPTN,
  317                          steal->child_scratch,
  318                          0, 0, 0);
  319         if (err < 0) {
  320             debug(" error doing TIOCGPTN: %s", strerror(-err));
  321             continue;
  322         }
  323         int ptn;
  324         err = ptrace_memcpy_from_child(&steal->child, &ptn,
  325                                        steal->child_scratch, sizeof(ptn));
  326         if (err < 0) {
  327             debug(" error getting ptn: %s", strerror(steal->child.error));
  328             continue;
  329         }
  330         if (ptn == (int)minor(steal->target_stat.ctty)) {
  331             debug("found a master fd: %d", atoi(d->d_name));
  332             if (fd_array_push(&steal->master_fds, atoi(d->d_name)) != 0) {
  333                 error("unable to allocate memory for fd array!");
  334                 return ENOMEM;
  335             }
  336         }
  337     }
  338 
  339     if (steal->master_fds.n == 0) {
  340         return ESRCH;
  341     }
  342     return 0;
  343 }
  344 
  345 /* Homebrew posix_openpt() */
  346 int get_pt() {
  347     return open("/dev/ptmx", O_RDWR | O_NOCTTY);
  348 }
  349 
  350 int get_process_tty_termios(pid_t pid, struct termios *tio) {
  351     int err = EINVAL;
  352     char buf[PATH_MAX];
  353     int i;
  354     int fd;
  355 
  356     for (i = 0; i < 3 && err; i++) {
  357         err = 0;
  358         snprintf(buf, sizeof buf, "/proc/%d/fd/%d", pid, i);
  359 
  360         if ((fd = open(buf, O_RDONLY)) < 0) {
  361             err = -fd;
  362             continue;
  363         }
  364 
  365         if (!isatty(fd)) {
  366             err = ENOTTY;
  367             goto retry;
  368         }
  369 
  370         if (tcgetattr(fd, tio) < 0) {
  371             err = -assert_nonzero(errno);
  372         }
  373 retry:
  374         close(fd);
  375     }
  376 
  377     return err;
  378 }
  379 
  380 void move_process_group(struct ptrace_child *child, pid_t from, pid_t to) {
  381     DIR *dir;
  382     struct dirent *d;
  383     pid_t pid;
  384     char *p;
  385     int err;
  386 
  387     if ((dir = opendir("/proc/")) == NULL)
  388         return;
  389 
  390     while ((d = readdir(dir)) != NULL) {
  391         if (d->d_name[0] == '.') continue;
  392         pid = strtol(d->d_name, &p, 10);
  393         if (*p) continue;
  394         if (getpgid(pid) == from) {
  395             debug("Change pgid for pid %d", pid);
  396             err = do_syscall(child, setpgid, pid, to, 0, 0, 0, 0);
  397             if (err < 0)
  398                 error(" failed: %s", strerror(-err));
  399         }
  400     }
  401     closedir(dir);
  402 }
  403 
  404 void copy_user(struct ptrace_child *d, struct ptrace_child *s) {
  405     memcpy(&d->regs, &s->regs, sizeof(s->regs));
  406 }
  407 
  408 unsigned long ptrace_socketcall(struct ptrace_child *child,
  409                                 unsigned long scratch,
  410                                 unsigned long socketcall,
  411                                 unsigned long p0, unsigned long p1,
  412                                 unsigned long p2, unsigned long p3,
  413                                 unsigned long p4)
  414 {
  415     // We assume that socketcall is only used on 32-bit
  416     // architectures. If there are any 64-bit architectures that do
  417     // socketcall, and we port to them, this will need to change.
  418     uint32_t args[] = {p0, p1, p2, p3, p4};
  419     int err;
  420 
  421     err = ptrace_memcpy_to_child(child, scratch, &args, sizeof args);
  422     if (err < 0)
  423         return (unsigned long)err;
  424     return do_syscall(child, socketcall, socketcall, scratch, 0, 0, 0, 0);
  425 }
  426 
  427 
  428 #endif