"Fossies" - the Fresh Open Source Software Archive

Member "reptyr-reptyr-0.8.0/attach.c" (29 Sep 2020, 17955 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 "attach.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 0.7.0_vs_0.8.0.

    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 #include <sys/types.h>
   23 #include <stdint.h>
   24 #include <dirent.h>
   25 #include <sys/syscall.h>
   26 #include <sys/mman.h>
   27 #include <stdio.h>
   28 #include <string.h>
   29 #include <stdlib.h>
   30 #include <fcntl.h>
   31 #include <errno.h>
   32 #include <termios.h>
   33 #include <sys/ioctl.h>
   34 #include <sys/wait.h>
   35 #include <signal.h>
   36 #include <limits.h>
   37 #include <time.h>
   38 #include <sys/time.h>
   39 #include <sys/stat.h>
   40 #include <sys/socket.h>
   41 #include <sys/un.h>
   42 
   43 #include "ptrace.h"
   44 #include "reptyr.h"
   45 #include "reallocarray.h"
   46 #include "platform/platform.h"
   47 
   48 int fd_array_push(struct fd_array *fda, int fd) {
   49     int *tmp;
   50 
   51     if (fda->n == fda->allocated) {
   52         fda->allocated = fda->allocated ? 2 * fda->allocated : 2;
   53         tmp = xreallocarray(fda->fds, fda->allocated, sizeof *tmp);
   54         if (tmp == NULL) {
   55             free(fda->fds);
   56             fda->fds = NULL;
   57             fda->allocated = 0;
   58             return -1;
   59         }
   60         fda->fds = tmp;
   61     }
   62     fda->fds[fda->n++] = fd;
   63     return 0;
   64 }
   65 
   66 static void do_unmap(struct ptrace_child *child, child_addr_t addr, unsigned long len) {
   67     if (addr == (child_addr_t) - 1)
   68         return;
   69     do_syscall(child, munmap, (unsigned long)addr, len, 0, 0, 0, 0);
   70 }
   71 
   72 static int do_fork(struct ptrace_child *child) {
   73     if (ptrace_syscall_numbers(child)->nr_fork != -1) {
   74         return do_syscall(child, fork, 0, 0, 0, 0, 0, 0);
   75     } else {
   76         return do_syscall(child, clone, SIGCHLD, 0, 0, 0, 0, 0);
   77     }
   78 }
   79 
   80 int do_setsid(struct ptrace_child *child) {
   81     int err = 0;
   82     struct ptrace_child dummy;
   83 
   84     err = do_fork(child);
   85     if (err < 0)
   86         return err;
   87 
   88     debug("Forked a child: %ld", child->forked_pid);
   89 
   90     err = ptrace_finish_attach(&dummy, child->forked_pid);
   91     if (err < 0)
   92         goto out_kill;
   93 
   94     dummy.state = ptrace_after_syscall;
   95     copy_user(&dummy, child);
   96     if (ptrace_restore_regs(&dummy)) {
   97         err = dummy.error;
   98         goto out_kill;
   99     }
  100 
  101     err = do_syscall(&dummy, setpgid, 0, 0, 0, 0, 0, 0);
  102     if (err < 0) {
  103         error("Failed to setpgid: %s", strerror(-err));
  104         goto out_kill;
  105     }
  106 
  107     move_process_group(child, child->pid, dummy.pid);
  108 
  109     err = do_syscall(child, setsid, 0, 0, 0, 0, 0, 0);
  110     if (err < 0) {
  111         error("Failed to setsid: %s", strerror(-err));
  112         move_process_group(child, dummy.pid, child->pid);
  113         goto out_kill;
  114     }
  115 
  116     debug("Did setsid()");
  117 
  118 out_kill:
  119     kill(dummy.pid, SIGKILL);
  120     ptrace_detach_child(&dummy);
  121     ptrace_wait(&dummy);
  122     do_syscall(child, wait4, dummy.pid, 0, WNOHANG, 0, 0, 0);
  123     return err;
  124 }
  125 
  126 static int do_dup2(struct ptrace_child *child, int oldfd, int newfd) {
  127     if (oldfd == newfd) {
  128         return 0;
  129     }
  130     if (ptrace_syscall_numbers(child)->nr_dup2 != -1) {
  131         return do_syscall(child, dup2, oldfd, newfd, 0, 0, 0, 0);
  132     } else {
  133         return do_syscall(child, dup3, oldfd, newfd, 0, 0, 0, 0);
  134     }
  135 }
  136 
  137 int ignore_hup(struct ptrace_child *child, child_addr_t scratch_page) {
  138     int err;
  139 
  140     struct sigaction act = {
  141         .sa_handler = SIG_IGN,
  142     };
  143     err = ptrace_memcpy_to_child(child, scratch_page,
  144                                  &act, sizeof act);
  145     if (err < 0)
  146         return err;
  147     err = do_syscall(child, rt_sigaction,
  148                      SIGHUP, (unsigned long)scratch_page,
  149                      0, 8, 0, 0);
  150 
  151     return err;
  152 }
  153 
  154 /*
  155  * Wait for the specific pid to enter state 'T', or stopped. We have to pull the
  156  * /proc file rather than attaching with ptrace() and doing a wait() because
  157  * half the point of this exercise is for the process's real parent (the shell)
  158  * to see the TSTP.
  159  *
  160  * In case the process is masking or ignoring SIGTSTP, we time out after a
  161  * second and continue with the attach -- it'll still work mostly right, you
  162  * just won't get the old shell back.
  163  */
  164 void wait_for_stop(pid_t pid, int fd) {
  165     struct timeval start, now;
  166     struct timespec sleep;
  167 
  168     gettimeofday(&start, NULL);
  169     while (1) {
  170         gettimeofday(&now, NULL);
  171         if ((now.tv_sec > start.tv_sec && now.tv_usec > start.tv_usec)
  172                 || (now.tv_sec - start.tv_sec > 1)) {
  173             error("Timed out waiting for child stop.");
  174             break;
  175         }
  176         /*
  177          * If anything goes wrong reading or parsing the stat node, just give
  178          * up.
  179          */
  180         if (check_proc_stopped(pid, fd))
  181             break;
  182 
  183         sleep.tv_sec  = 0;
  184         sleep.tv_nsec = 10000000;
  185         nanosleep(&sleep, NULL);
  186     }
  187 }
  188 
  189 int copy_tty_state(pid_t pid, const char *pty) {
  190     int fd, err = EINVAL;
  191     struct termios tio;
  192 
  193     err = get_process_tty_termios(pid, &tio);
  194 
  195     if (err)
  196         return err;
  197 
  198     if ((fd = open(pty, O_RDONLY)) < 0)
  199         return -assert_nonzero(errno);
  200 
  201     if (tcsetattr(fd, TCSANOW, &tio) < 0)
  202         err = assert_nonzero(errno);
  203     close(fd);
  204     return -err;
  205 }
  206 
  207 int mmap_scratch(struct ptrace_child *child, child_addr_t *addr) {
  208     long mmap_syscall;
  209     child_addr_t scratch_page;
  210 
  211     mmap_syscall = ptrace_syscall_numbers(child)->nr_mmap2;
  212     if (mmap_syscall == -1)
  213         mmap_syscall = ptrace_syscall_numbers(child)->nr_mmap;
  214     scratch_page = ptrace_remote_syscall(child, mmap_syscall, 0,
  215                                          sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE,
  216                                          MAP_ANONYMOUS | MAP_SHARED, -1, 0);
  217     //MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
  218 
  219     if (scratch_page > (unsigned long) - 1000) {
  220         return -(signed long)scratch_page;
  221     }
  222 
  223     *addr = scratch_page;
  224     debug("Allocated scratch page: %lx", scratch_page);
  225 
  226     return 0;
  227 }
  228 
  229 int grab_pid(pid_t pid, struct ptrace_child *child, child_addr_t *scratch) {
  230     int err;
  231 
  232     if (ptrace_attach_child(child, pid)) {
  233         err = child->error;
  234         goto out;
  235     }
  236     if (ptrace_advance_to_state(child, ptrace_at_syscall)) {
  237         err = child->error;
  238         goto out;
  239     }
  240     if (ptrace_save_regs(child)) {
  241         err = child->error;
  242         goto out;
  243     }
  244 
  245     if ((err = mmap_scratch(child, scratch)))
  246         goto out_restore_regs;
  247 
  248     return 0;
  249 
  250 out_restore_regs:
  251     ptrace_restore_regs(child);
  252 
  253 out:
  254     ptrace_detach_child(child);
  255 
  256     return err;
  257 }
  258 
  259 int preflight_check(pid_t pid) {
  260     struct ptrace_child child;
  261     debug("Making sure we have permission to attach...");
  262     if (ptrace_attach_child(&child, pid)) {
  263         return child.error;
  264     }
  265     ptrace_detach_child(&child);
  266     return 0;
  267 }
  268 
  269 int attach_child(pid_t pid, const char *pty, int force_stdio) {
  270     struct ptrace_child child;
  271     child_addr_t scratch_page = -1;
  272     int *child_tty_fds = NULL, n_fds, child_fd, statfd = -1;
  273     int i;
  274     int err = 0;
  275     long page_size = sysconf(_SC_PAGE_SIZE);
  276 #ifdef __linux__
  277     char stat_path[PATH_MAX];
  278 #endif
  279 
  280     if ((err = check_pgroup(pid))) {
  281         return err;
  282     }
  283 
  284     if ((err = preflight_check(pid))) {
  285         return err;
  286     }
  287 
  288     debug("Using tty: %s", pty);
  289 
  290     if ((err = copy_tty_state(pid, pty))) {
  291         if (err == ENOTTY && !force_stdio) {
  292             error("Target is not connected to a terminal.\n"
  293                   "    Use -s to force attaching anyways.");
  294             return err;
  295         }
  296     }
  297 
  298 #ifdef __linux__
  299     snprintf(stat_path, sizeof stat_path, "/proc/%d/stat", pid);
  300     statfd = open(stat_path, O_RDONLY);
  301     if (statfd < 0) {
  302         error("Unable to open %s: %s", stat_path, strerror(errno));
  303         return -statfd;
  304     }
  305 #endif
  306 
  307     kill(pid, SIGTSTP);
  308     wait_for_stop(pid, statfd);
  309 
  310     if ((err = grab_pid(pid, &child, &scratch_page))) {
  311         goto out_cont;
  312     }
  313 
  314     if (force_stdio) {
  315         child_tty_fds = malloc(3 * sizeof(int));
  316         if (!child_tty_fds) {
  317             err = ENOMEM;
  318             goto out_unmap;
  319         }
  320         n_fds = 3;
  321         child_tty_fds[0] = 0;
  322         child_tty_fds[1] = 1;
  323         child_tty_fds[2] = 2;
  324     } else {
  325         child_tty_fds = get_child_tty_fds(&child, statfd, &n_fds);
  326         if (!child_tty_fds) {
  327             err = child.error;
  328             goto out_unmap;
  329         }
  330     }
  331 
  332     if (ptrace_memcpy_to_child(&child, scratch_page, pty, strlen(pty) + 1)) {
  333         err = child.error;
  334         error("Unable to memcpy the pty path to child.");
  335         goto out_free_fds;
  336     }
  337 
  338     child_fd = do_syscall(&child, openat,
  339                           -1, scratch_page, O_RDWR | O_NOCTTY,
  340                           0, 0, 0);
  341     if (child_fd < 0) {
  342         err = child_fd;
  343         error("Unable to open the tty in the child.");
  344         goto out_free_fds;
  345     }
  346 
  347     debug("Opened the new tty in the child: %d", child_fd);
  348 
  349     err = ignore_hup(&child, scratch_page);
  350     if (err < 0)
  351         goto out_close;
  352 
  353     err = do_syscall(&child, getsid, 0, 0, 0, 0, 0, 0);
  354     if (err != child.pid) {
  355         debug("Target is not a session leader, attempting to setsid.");
  356         err = do_setsid(&child);
  357     } else {
  358         do_syscall(&child, ioctl, child_tty_fds[0], TIOCNOTTY, 0, 0, 0, 0);
  359     }
  360     if (err < 0)
  361         goto out_close;
  362 
  363     err = do_syscall(&child, ioctl, child_fd, TIOCSCTTY, 1, 0, 0, 0);
  364     if (err != 0) { /* Seems to be returning >0 for error */
  365         error("Unable to set controlling terminal: %s", strerror(err));
  366         goto out_close;
  367     }
  368 
  369     debug("Set the controlling tty");
  370 
  371     for (i = 0; i < n_fds; i++) {
  372         err = do_dup2(&child, child_fd, child_tty_fds[i]);
  373         if (err < 0)
  374             error("Problem moving child fd number %d to new tty: %s", child_tty_fds[i], strerror(errno));
  375     }
  376 
  377 
  378     err = 0;
  379 
  380 out_close:
  381     do_syscall(&child, close, child_fd, 0, 0, 0, 0, 0);
  382 out_free_fds:
  383     free(child_tty_fds);
  384 
  385 out_unmap:
  386     do_unmap(&child, scratch_page, page_size);
  387 
  388     ptrace_restore_regs(&child);
  389     ptrace_detach_child(&child);
  390 
  391     if (err == 0) {
  392         kill(child.pid, SIGSTOP);
  393         wait_for_stop(child.pid, statfd);
  394     }
  395     kill(child.pid, SIGWINCH);
  396 out_cont:
  397     kill(child.pid, SIGCONT);
  398 #ifdef __linux__
  399     close(statfd);
  400 #endif
  401 
  402     return err < 0 ? -err : err;
  403 }
  404 
  405 int setup_steal_socket(struct steal_pty_state *steal) {
  406     strcpy(steal->tmpdir, "/tmp/reptyr.XXXXXX");
  407     if (mkdtemp(steal->tmpdir) == NULL)
  408         return errno;
  409 
  410     steal->addr_un.sun_family = AF_UNIX;
  411     if (snprintf(steal->addr_un.sun_path, sizeof(steal->addr_un.sun_path),
  412                  "%s/reptyr.sock", steal->tmpdir) >= sizeof(steal->addr_un.sun_path)) {
  413         error("tmpdir path too long!");
  414         return ENAMETOOLONG;
  415     }
  416 
  417     if ((steal->sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
  418         return errno;
  419 
  420     if (bind(steal->sockfd, &steal->addr, sizeof(steal->addr_un)) < 0)
  421         return errno;
  422 
  423     if (chown(steal->addr_un.sun_path, steal->emulator_uid, -1) < 0)
  424         debug("chown %s: %s", steal->addr_un.sun_path, strerror(errno));
  425     if (chown(steal->tmpdir, steal->emulator_uid, -1) < 0)
  426         debug("chown %s: %s", steal->tmpdir, strerror(errno));
  427 
  428     return 0;
  429 }
  430 
  431 int setup_steal_socket_child(struct steal_pty_state *steal) {
  432     int err;
  433     err = do_socketcall(&steal->child,
  434                         steal->child_scratch + sysconf(_SC_PAGE_SIZE)/2,
  435                         socket, AF_UNIX, SOCK_DGRAM, 0, 0, 0);
  436     if (err < 0)
  437         return -err;
  438     steal->child_fd = err;
  439     debug("Opened fd %d in the child.", steal->child_fd);
  440     err = ptrace_memcpy_to_child(&steal->child, steal->child_scratch,
  441                                  &steal->addr_un, sizeof(steal->addr_un));
  442     if (err < 0)
  443         return steal->child.error;
  444     err = do_socketcall(&steal->child,
  445                         steal->child_scratch + sysconf(_SC_PAGE_SIZE)/2,
  446                         connect, steal->child_fd, steal->child_scratch,
  447                         sizeof(steal->addr_un), 0, 0);
  448     if (err < 0)
  449         return -err;
  450     debug("Connected to the shared socket.");
  451     return 0;
  452 }
  453 
  454 int steal_child_pty(struct steal_pty_state *steal) {
  455     struct {
  456         struct msghdr msg;
  457         unsigned char buf[CMSG_SPACE(sizeof(int))];
  458     } buf = {};
  459     struct cmsghdr *cm;
  460     int err;
  461 
  462     buf.msg.msg_control = buf.buf;
  463     buf.msg.msg_controllen = CMSG_SPACE(sizeof(int));
  464     cm = CMSG_FIRSTHDR(&buf.msg);
  465     cm->cmsg_level = SOL_SOCKET;
  466     cm->cmsg_type  = SCM_RIGHTS;
  467     cm->cmsg_len   = CMSG_LEN(sizeof(int));
  468     memcpy(CMSG_DATA(cm), &steal->master_fds.fds[0], sizeof(int));
  469     buf.msg.msg_controllen = cm->cmsg_len;
  470 
  471     // Relocate for the child
  472     buf.msg.msg_control = (void*)(steal->child_scratch +
  473                                   ((uint8_t*)buf.msg.msg_control - (uint8_t*)&buf));
  474 
  475     if (ptrace_memcpy_to_child(&steal->child,
  476                                steal->child_scratch,
  477                                &buf, sizeof(buf))) {
  478         return steal->child.error;
  479     }
  480 
  481     steal->child.error = 0;
  482     err = do_socketcall(&steal->child,
  483                         steal->child_scratch + sysconf(_SC_PAGE_SIZE)/2,
  484                         sendmsg,
  485                         steal->child_fd,
  486                         steal->child_scratch,
  487                         MSG_DONTWAIT, 0, 0);
  488     if (err < 0) {
  489         return steal->child.error ? steal->child.error : -err;
  490     }
  491 
  492     debug("Sent the pty fd, going to receive it.");
  493 
  494     buf.msg.msg_control = buf.buf;
  495     buf.msg.msg_controllen = CMSG_SPACE(sizeof(int));
  496 
  497     err = recvmsg(steal->sockfd, &buf.msg, MSG_DONTWAIT);
  498     if (err < 0) {
  499         error("Error receiving message.");
  500         return errno;
  501     }
  502 
  503     debug("Got a message: %d bytes, %ld control",
  504           err, (long)buf.msg.msg_controllen);
  505 
  506     if (buf.msg.msg_controllen < CMSG_LEN(sizeof(int))) {
  507         error("No fd received?");
  508         return EINVAL;
  509     }
  510 
  511     memcpy(&steal->ptyfd, CMSG_DATA(cm), sizeof(steal->ptyfd));
  512 
  513     debug("Got tty fd: %d", steal->ptyfd);
  514 
  515     return 0;
  516 }
  517 
  518 // Attach to the session leader of the stolen session, and block
  519 // SIGHUP so that if and when the terminal emulator tries to HUP it,
  520 // it doesn't die.
  521 int steal_block_hup(struct steal_pty_state *steal) {
  522     struct ptrace_child leader;
  523     child_addr_t scratch = 0;
  524     int err = 0;
  525 
  526     if ((err = grab_pid(steal->target_stat.sid, &leader, &scratch)))
  527         return err;
  528 
  529     err = ignore_hup(&leader, scratch);
  530 
  531     ptrace_restore_regs(&leader);
  532     ptrace_detach_child(&leader);
  533 
  534     return err;
  535 }
  536 
  537 int steal_cleanup_child(struct steal_pty_state *steal) {
  538     if (ptrace_memcpy_to_child(&steal->child,
  539                                steal->child_scratch,
  540                                "/dev/null", sizeof("/dev/null"))) {
  541         return steal->child.error;
  542     }
  543 
  544     int nullfd = do_syscall(&steal->child, openat, -1, steal->child_scratch, O_RDWR, 0, 0, 0);
  545     if (nullfd < 0) {
  546         return steal->child.error;
  547     }
  548 
  549     int i;
  550     for (i = 0; i < steal->master_fds.n; ++i) {
  551         do_dup2(&steal->child, nullfd, steal->master_fds.fds[i]);
  552     }
  553 
  554     do_syscall(&steal->child, close, nullfd, 0, 0, 0, 0, 0);
  555     do_syscall(&steal->child, close, steal->child_fd, 0, 0, 0, 0, 0);
  556 
  557     steal->child_fd = 0;
  558 
  559     ptrace_restore_regs(&steal->child);
  560 
  561     ptrace_detach_child(&steal->child);
  562     ptrace_wait(&steal->child);
  563     return 0;
  564 }
  565 
  566 int steal_pty(pid_t pid, int *pty) {
  567     int err = 0;
  568     struct steal_pty_state steal = {};
  569     long page_size = sysconf(_SC_PAGE_SIZE);
  570 
  571     if ((err = preflight_check(pid)))
  572         goto out;
  573 
  574     if ((err = get_terminal_state(&steal, pid)))
  575         goto out;
  576 
  577     if ((err = setup_steal_socket(&steal)))
  578         goto out;
  579 
  580     debug("Listening on socket: %s", steal.addr_un.sun_path);
  581     debug("Attaching terminal emulator pid=%d", steal.emulator_pid);
  582 
  583     if ((err = grab_pid(steal.emulator_pid, &steal.child, &steal.child_scratch)))
  584         goto out;
  585 
  586     debug("Attached to terminal emulator (pid %d)",
  587           (int)steal.emulator_pid);
  588 
  589     if ((err = find_master_fd(&steal))) {
  590         error("Unable to find the fd for the pty!");
  591         goto out;
  592     }
  593 
  594     if ((err = setup_steal_socket_child(&steal)))
  595         goto out;
  596 
  597     if ((err = steal_child_pty(&steal)))
  598         goto out;
  599 
  600     if ((err = steal_block_hup(&steal)))
  601         goto out;
  602 
  603     if ((err = steal_cleanup_child(&steal)))
  604         goto out;
  605 
  606     goto out_no_child;
  607 
  608 out:
  609     if (steal.ptyfd) {
  610         close(steal.ptyfd);
  611         steal.ptyfd = 0;
  612     }
  613 
  614     if (steal.child_fd > 0)
  615         do_syscall(&steal.child, close, steal.child_fd, 0, 0, 0, 0, 0);
  616 
  617     if (steal.child_scratch > 0)
  618         do_unmap(&steal.child, steal.child_scratch, page_size);
  619 
  620     if (steal.child.state != ptrace_detached) {
  621         ptrace_restore_regs(&steal.child);
  622         ptrace_detach_child(&steal.child);
  623     }
  624 
  625 out_no_child:
  626 
  627     if (steal.sockfd > 0) {
  628         close(steal.sockfd);
  629         unlink(steal.addr_un.sun_path);
  630     }
  631 
  632     if (steal.tmpdir[0]) {
  633         rmdir(steal.tmpdir);
  634     }
  635 
  636     if (steal.ptyfd)
  637         *pty = steal.ptyfd;
  638 
  639     free(steal.master_fds.fds);
  640 
  641     return err;
  642 }